summaryrefslogtreecommitdiffstats
path: root/sbin
diff options
context:
space:
mode:
Diffstat (limited to 'sbin')
-rw-r--r--sbin/Makefile139
-rw-r--r--sbin/Makefile.inc9
-rw-r--r--sbin/adjkerntz/Makefile6
-rw-r--r--sbin/adjkerntz/adjkerntz.8195
-rw-r--r--sbin/adjkerntz/adjkerntz.c386
-rw-r--r--sbin/adjkerntz/pathnames.h29
-rw-r--r--sbin/atacontrol/Makefile7
-rw-r--r--sbin/atacontrol/atacontrol.8241
-rw-r--r--sbin/atacontrol/atacontrol.c584
-rw-r--r--sbin/atm/Makefile31
-rw-r--r--sbin/atm/Makefile.inc26
-rw-r--r--sbin/atm/atm/Makefile42
-rw-r--r--sbin/atm/atm/atm.8993
-rw-r--r--sbin/atm/atm/atm.c1143
-rw-r--r--sbin/atm/atm/atm.h195
-rw-r--r--sbin/atm/atm/atm_fore200.c596
-rw-r--r--sbin/atm/atm/atm_inet.c154
-rw-r--r--sbin/atm/atm/atm_print.c891
-rw-r--r--sbin/atm/atm/atm_set.c528
-rw-r--r--sbin/atm/atm/atm_show.c1144
-rw-r--r--sbin/atm/atm/atm_subr.c626
-rw-r--r--sbin/atm/atmconfig/Makefile46
-rw-r--r--sbin/atm/atmconfig/atm_oid.list20
-rw-r--r--sbin/atm/atmconfig/atmconfig.8319
-rw-r--r--sbin/atm/atmconfig/atmconfig.h102
-rw-r--r--sbin/atm/atmconfig/atmconfig.help223
-rw-r--r--sbin/atm/atmconfig/atmconfig_device.c444
-rw-r--r--sbin/atm/atmconfig/atmconfig_device.h75
-rw-r--r--sbin/atm/atmconfig/atmconfig_device.help62
-rw-r--r--sbin/atm/atmconfig/diag.c1122
-rw-r--r--sbin/atm/atmconfig/diag.h49
-rw-r--r--sbin/atm/atmconfig/main.c880
-rw-r--r--sbin/atm/atmconfig/natm.c680
-rw-r--r--sbin/atm/atmconfig/private.h62
-rw-r--r--sbin/atm/fore_dnld/COPYRIGHT38
-rw-r--r--sbin/atm/fore_dnld/Makefile36
-rw-r--r--sbin/atm/fore_dnld/fore_dnld.8114
-rw-r--r--sbin/atm/fore_dnld/fore_dnld.c1346
-rw-r--r--sbin/atm/fore_dnld/pca200e.c3796
-rw-r--r--sbin/atm/ilmid/Makefile40
-rw-r--r--sbin/atm/ilmid/ilmid.8110
-rw-r--r--sbin/atm/ilmid/ilmid.c2725
-rw-r--r--sbin/badsect/Makefile9
-rw-r--r--sbin/badsect/badsect.8133
-rw-r--r--sbin/badsect/badsect.c193
-rw-r--r--sbin/bsdlabel/Makefile26
-rw-r--r--sbin/bsdlabel/bsdlabel.5521
-rw-r--r--sbin/bsdlabel/bsdlabel.8505
-rw-r--r--sbin/bsdlabel/bsdlabel.c1498
-rw-r--r--sbin/bsdlabel/pathnames.h36
-rw-r--r--sbin/bsdlabel/runtest.sh175
-rw-r--r--sbin/camcontrol/Makefile19
-rw-r--r--sbin/camcontrol/camcontrol.8885
-rw-r--r--sbin/camcontrol/camcontrol.c3564
-rw-r--r--sbin/camcontrol/camcontrol.h56
-rw-r--r--sbin/camcontrol/modeedit.c909
-rw-r--r--sbin/camcontrol/util.c156
-rw-r--r--sbin/ccdconfig/Makefile10
-rw-r--r--sbin/ccdconfig/ccdconfig.8235
-rw-r--r--sbin/ccdconfig/ccdconfig.c439
-rw-r--r--sbin/ccdconfig/pathnames.h38
-rw-r--r--sbin/clri/Makefile7
-rw-r--r--sbin/clri/clri.876
-rw-r--r--sbin/clri/clri.c158
-rw-r--r--sbin/comcontrol/Makefile8
-rw-r--r--sbin/comcontrol/comcontrol.865
-rw-r--r--sbin/comcontrol/comcontrol.c128
-rw-r--r--sbin/conscontrol/Makefile6
-rw-r--r--sbin/conscontrol/conscontrol.8115
-rw-r--r--sbin/conscontrol/conscontrol.c208
-rw-r--r--sbin/devd/Makefile18
-rw-r--r--sbin/devd/devd.8148
-rw-r--r--sbin/devd/devd.cc964
-rw-r--r--sbin/devd/devd.conf.5129
-rw-r--r--sbin/devd/devd.h58
-rw-r--r--sbin/devd/devd.hh183
-rw-r--r--sbin/devd/parse.y152
-rw-r--r--sbin/devd/token.l110
-rw-r--r--sbin/devfs/Makefile8
-rw-r--r--sbin/devfs/devfs.8355
-rw-r--r--sbin/devfs/devfs.c230
-rw-r--r--sbin/devfs/extern.h57
-rw-r--r--sbin/devfs/rule.c462
-rw-r--r--sbin/dhclient/Makefile43
-rw-r--r--sbin/dhclient/alloc.c79
-rw-r--r--sbin/dhclient/bpf.c387
-rw-r--r--sbin/dhclient/clparse.c941
-rw-r--r--sbin/dhclient/conflex.c529
-rw-r--r--sbin/dhclient/convert.c117
-rw-r--r--sbin/dhclient/dhclient-script295
-rw-r--r--sbin/dhclient/dhclient-script.8273
-rw-r--r--sbin/dhclient/dhclient.8194
-rw-r--r--sbin/dhclient/dhclient.c2486
-rw-r--r--sbin/dhclient/dhclient.conf36
-rw-r--r--sbin/dhclient/dhclient.conf.5544
-rw-r--r--sbin/dhclient/dhclient.leases.595
-rw-r--r--sbin/dhclient/dhcp-options.5606
-rw-r--r--sbin/dhclient/dhcp.h175
-rw-r--r--sbin/dhclient/dhcpd.h437
-rw-r--r--sbin/dhclient/dhctoken.h136
-rw-r--r--sbin/dhclient/dispatch.c498
-rw-r--r--sbin/dhclient/errwarn.c237
-rw-r--r--sbin/dhclient/hash.c122
-rw-r--r--sbin/dhclient/inet.c121
-rw-r--r--sbin/dhclient/options.c720
-rw-r--r--sbin/dhclient/packet.c256
-rw-r--r--sbin/dhclient/parse.c580
-rw-r--r--sbin/dhclient/privsep.c238
-rw-r--r--sbin/dhclient/privsep.h47
-rw-r--r--sbin/dhclient/tables.c433
-rw-r--r--sbin/dhclient/tree.c59
-rw-r--r--sbin/dhclient/tree.h66
-rw-r--r--sbin/dmesg/Makefile12
-rw-r--r--sbin/dmesg/dmesg.885
-rw-r--r--sbin/dmesg/dmesg.c202
-rw-r--r--sbin/dump/Makefile23
-rw-r--r--sbin/dump/cache.c146
-rw-r--r--sbin/dump/dump.8544
-rw-r--r--sbin/dump/dump.h179
-rw-r--r--sbin/dump/dumprmt.c375
-rw-r--r--sbin/dump/itime.c260
-rw-r--r--sbin/dump/main.c749
-rw-r--r--sbin/dump/optr.c425
-rw-r--r--sbin/dump/pathnames.h39
-rw-r--r--sbin/dump/tape.c880
-rw-r--r--sbin/dump/traverse.c838
-rw-r--r--sbin/dump/unctime.c56
-rw-r--r--sbin/dumpfs/Makefile9
-rw-r--r--sbin/dumpfs/dumpfs.870
-rw-r--r--sbin/dumpfs/dumpfs.c413
-rw-r--r--sbin/dumpon/Makefile7
-rw-r--r--sbin/dumpon/dumpon.8143
-rw-r--r--sbin/dumpon/dumpon.c135
-rw-r--r--sbin/fdisk/Makefile16
-rw-r--r--sbin/fdisk/fdisk.8457
-rw-r--r--sbin/fdisk/fdisk.c1412
-rw-r--r--sbin/fdisk/runtest.sh30
-rw-r--r--sbin/fdisk_pc98/Makefile13
-rw-r--r--sbin/fdisk_pc98/fdisk.8483
-rw-r--r--sbin/fdisk_pc98/fdisk.c861
-rw-r--r--sbin/ffsinfo/Makefile19
-rw-r--r--sbin/ffsinfo/ffsinfo.8145
-rw-r--r--sbin/ffsinfo/ffsinfo.c675
-rw-r--r--sbin/fsck/Makefile9
-rw-r--r--sbin/fsck/fsck.8198
-rw-r--r--sbin/fsck/fsck.c574
-rw-r--r--sbin/fsck/fsutil.c225
-rw-r--r--sbin/fsck/fsutil.h54
-rw-r--r--sbin/fsck/preen.c333
-rw-r--r--sbin/fsck_ffs/Makefile16
-rw-r--r--sbin/fsck_ffs/SMM.doc/0.t147
-rw-r--r--sbin/fsck_ffs/SMM.doc/1.t80
-rw-r--r--sbin/fsck_ffs/SMM.doc/2.t262
-rw-r--r--sbin/fsck_ffs/SMM.doc/3.t449
-rw-r--r--sbin/fsck_ffs/SMM.doc/4.t1421
-rw-r--r--sbin/fsck_ffs/SMM.doc/Makefile8
-rw-r--r--sbin/fsck_ffs/dir.c713
-rw-r--r--sbin/fsck_ffs/ea.c86
-rw-r--r--sbin/fsck_ffs/fsck.h390
-rw-r--r--sbin/fsck_ffs/fsck_ffs.8317
-rw-r--r--sbin/fsck_ffs/fsutil.c709
-rw-r--r--sbin/fsck_ffs/inode.c678
-rw-r--r--sbin/fsck_ffs/main.c538
-rw-r--r--sbin/fsck_ffs/pass1.c467
-rw-r--r--sbin/fsck_ffs/pass1b.c113
-rw-r--r--sbin/fsck_ffs/pass2.c472
-rw-r--r--sbin/fsck_ffs/pass3.c127
-rw-r--r--sbin/fsck_ffs/pass4.c148
-rw-r--r--sbin/fsck_ffs/pass5.c532
-rw-r--r--sbin/fsck_ffs/setup.c533
-rw-r--r--sbin/fsck_ffs/utilities.c121
-rw-r--r--sbin/fsck_msdosfs/Makefile14
-rw-r--r--sbin/fsck_msdosfs/boot.c272
-rw-r--r--sbin/fsck_msdosfs/check.c200
-rw-r--r--sbin/fsck_msdosfs/dir.c982
-rw-r--r--sbin/fsck_msdosfs/dosfs.h144
-rw-r--r--sbin/fsck_msdosfs/ext.h150
-rw-r--r--sbin/fsck_msdosfs/fat.c702
-rw-r--r--sbin/fsck_msdosfs/fsck_msdosfs.8126
-rw-r--r--sbin/fsck_msdosfs/main.c160
-rw-r--r--sbin/fsdb/Makefile16
-rw-r--r--sbin/fsdb/fsdb.8258
-rw-r--r--sbin/fsdb/fsdb.c918
-rw-r--r--sbin/fsdb/fsdb.h62
-rw-r--r--sbin/fsdb/fsdbutil.c371
-rw-r--r--sbin/fsirand/Makefile10
-rw-r--r--sbin/fsirand/fsirand.8116
-rw-r--r--sbin/fsirand/fsirand.c310
-rw-r--r--sbin/gbde/Makefile36
-rw-r--r--sbin/gbde/gbde.8232
-rw-r--r--sbin/gbde/gbde.c865
-rw-r--r--sbin/gbde/image.uu3305
-rw-r--r--sbin/gbde/template.txt32
-rw-r--r--sbin/gbde/test.sh65
-rw-r--r--sbin/geom/Makefile5
-rw-r--r--sbin/geom/Makefile.inc6
-rw-r--r--sbin/geom/class/Makefile14
-rw-r--r--sbin/geom/class/Makefile.inc11
-rw-r--r--sbin/geom/class/concat/Makefile7
-rw-r--r--sbin/geom/class/concat/gconcat.8197
-rw-r--r--sbin/geom/class/concat/geom_concat.c247
-rw-r--r--sbin/geom/class/eli/Makefile18
-rw-r--r--sbin/geom/class/eli/geli.8527
-rw-r--r--sbin/geom/class/eli/geom_eli.c1133
-rw-r--r--sbin/geom/class/label/Makefile7
-rw-r--r--sbin/geom/class/label/geom_label.c220
-rw-r--r--sbin/geom/class/label/glabel.8227
-rw-r--r--sbin/geom/class/mirror/Makefile10
-rw-r--r--sbin/geom/class/mirror/geom_mirror.c372
-rw-r--r--sbin/geom/class/mirror/gmirror.8306
-rw-r--r--sbin/geom/class/nop/Makefile7
-rw-r--r--sbin/geom/class/nop/geom_nop.c75
-rw-r--r--sbin/geom/class/nop/gnop.8167
-rw-r--r--sbin/geom/class/raid3/Makefile10
-rw-r--r--sbin/geom/class/raid3/geom_raid3.c323
-rw-r--r--sbin/geom/class/raid3/graid3.8241
-rw-r--r--sbin/geom/class/shsec/Makefile7
-rw-r--r--sbin/geom/class/shsec/geom_shsec.c259
-rw-r--r--sbin/geom/class/shsec/gshsec.8130
-rw-r--r--sbin/geom/class/stripe/Makefile7
-rw-r--r--sbin/geom/class/stripe/geom_stripe.c285
-rw-r--r--sbin/geom/class/stripe/gstripe.8246
-rw-r--r--sbin/geom/core/Makefile17
-rw-r--r--sbin/geom/core/geom.8171
-rw-r--r--sbin/geom/core/geom.c1000
-rw-r--r--sbin/geom/core/geom.h63
-rw-r--r--sbin/geom/misc/subr.c390
-rw-r--r--sbin/geom/misc/subr.h48
-rw-r--r--sbin/ggate/Makefile12
-rw-r--r--sbin/ggate/Makefile.inc5
-rw-r--r--sbin/ggate/ggatec/Makefile15
-rw-r--r--sbin/ggate/ggatec/ggatec.8181
-rw-r--r--sbin/ggate/ggatec/ggatec.c640
-rw-r--r--sbin/ggate/ggated/Makefile14
-rw-r--r--sbin/ggate/ggated/ggated.8111
-rw-r--r--sbin/ggate/ggated/ggated.c1044
-rw-r--r--sbin/ggate/ggatel/Makefile15
-rw-r--r--sbin/ggate/ggatel/ggatel.8160
-rw-r--r--sbin/ggate/ggatel/ggatel.c336
-rw-r--r--sbin/ggate/shared/ggate.c395
-rw-r--r--sbin/ggate/shared/ggate.h196
-rw-r--r--sbin/gpt/Makefile9
-rw-r--r--sbin/gpt/add.c234
-rw-r--r--sbin/gpt/create.c245
-rw-r--r--sbin/gpt/destroy.c113
-rw-r--r--sbin/gpt/gpt.8335
-rw-r--r--sbin/gpt/gpt.c635
-rw-r--r--sbin/gpt/gpt.h86
-rw-r--r--sbin/gpt/label.c263
-rw-r--r--sbin/gpt/map.c216
-rw-r--r--sbin/gpt/map.h63
-rw-r--r--sbin/gpt/migrate.c365
-rw-r--r--sbin/gpt/recover.c178
-rw-r--r--sbin/gpt/remove.c224
-rw-r--r--sbin/gpt/show.c209
-rw-r--r--sbin/growfs/Makefile19
-rw-r--r--sbin/growfs/debug.c874
-rw-r--r--sbin/growfs/debug.h143
-rw-r--r--sbin/growfs/growfs.8196
-rw-r--r--sbin/growfs/growfs.c2520
-rw-r--r--sbin/gvinum/Makefile14
-rw-r--r--sbin/gvinum/gvinum.8397
-rw-r--r--sbin/gvinum/gvinum.c906
-rw-r--r--sbin/gvinum/gvinum.h39
-rw-r--r--sbin/idmapd/Makefile13
-rw-r--r--sbin/idmapd/idmapd.863
-rw-r--r--sbin/idmapd/idmapd.c418
-rw-r--r--sbin/ifconfig/Makefile42
-rw-r--r--sbin/ifconfig/af_atalk.c184
-rw-r--r--sbin/ifconfig/af_inet.c202
-rw-r--r--sbin/ifconfig/af_inet6.c540
-rw-r--r--sbin/ifconfig/af_ipx.c128
-rw-r--r--sbin/ifconfig/af_link.c127
-rw-r--r--sbin/ifconfig/ifbridge.c565
-rw-r--r--sbin/ifconfig/ifcarp.c199
-rw-r--r--sbin/ifconfig/ifclone.c155
-rw-r--r--sbin/ifconfig/ifconfig.81393
-rw-r--r--sbin/ifconfig/ifconfig.c1065
-rw-r--r--sbin/ifconfig/ifconfig.h143
-rw-r--r--sbin/ifconfig/ifieee80211.c1970
-rw-r--r--sbin/ifconfig/ifmac.c121
-rw-r--r--sbin/ifconfig/ifmedia.c806
-rw-r--r--sbin/ifconfig/ifpfsync.c210
-rw-r--r--sbin/ifconfig/ifvlan.c168
-rw-r--r--sbin/init/Makefile17
-rw-r--r--sbin/init/NOTES112
-rw-r--r--sbin/init/init.8377
-rw-r--r--sbin/init/init.c1635
-rw-r--r--sbin/init/pathnames.h40
-rw-r--r--sbin/ip6fw/Makefile7
-rw-r--r--sbin/ip6fw/ip6fw.8582
-rw-r--r--sbin/ip6fw/ip6fw.c1453
-rw-r--r--sbin/ip6fw/sample.sh28
-rw-r--r--sbin/ipf/Makefile8
-rw-r--r--sbin/ipf/Makefile.inc20
-rw-r--r--sbin/ipf/ipf/Makefile41
-rw-r--r--sbin/ipf/ipfs/Makefile6
-rw-r--r--sbin/ipf/ipfstat/Makefile11
-rw-r--r--sbin/ipf/ipftest/Makefile90
-rw-r--r--sbin/ipf/ipmon/Makefile34
-rw-r--r--sbin/ipf/ipnat/Makefile36
-rw-r--r--sbin/ipf/ippool/Makefile33
-rw-r--r--sbin/ipf/ipresend/Makefile9
-rw-r--r--sbin/ipf/ipsend/Makefile39
-rw-r--r--sbin/ipf/iptest/Makefile11
-rw-r--r--sbin/ipf/libipf/Makefile29
-rw-r--r--sbin/ipf/rules/Makefile17
-rw-r--r--sbin/ipfw/Makefile8
-rw-r--r--sbin/ipfw/ipfw.82440
-rw-r--r--sbin/ipfw/ipfw2.c5237
-rw-r--r--sbin/kldconfig/Makefile33
-rw-r--r--sbin/kldconfig/kldconfig.8108
-rw-r--r--sbin/kldconfig/kldconfig.c443
-rw-r--r--sbin/kldload/Makefile33
-rw-r--r--sbin/kldload/kldload.890
-rw-r--r--sbin/kldload/kldload.c81
-rw-r--r--sbin/kldstat/Makefile33
-rw-r--r--sbin/kldstat/kldstat.876
-rw-r--r--sbin/kldstat/kldstat.c159
-rw-r--r--sbin/kldunload/Makefile33
-rw-r--r--sbin/kldunload/kldunload.881
-rw-r--r--sbin/kldunload/kldunload.c104
-rw-r--r--sbin/ldconfig/Makefile12
-rw-r--r--sbin/ldconfig/elfhints.c302
-rw-r--r--sbin/ldconfig/ldconfig.8224
-rw-r--r--sbin/ldconfig/ldconfig.c640
-rw-r--r--sbin/ldconfig/ldconfig.h41
-rw-r--r--sbin/mca/Makefile6
-rw-r--r--sbin/mca/mca.c529
-rw-r--r--sbin/md5/Makefile20
-rw-r--r--sbin/md5/md5.1132
-rw-r--r--sbin/md5/md5.c354
-rw-r--r--sbin/mdconfig/Makefile10
-rw-r--r--sbin/mdconfig/mdconfig.8227
-rw-r--r--sbin/mdconfig/mdconfig.c360
-rw-r--r--sbin/mdmfs/Makefile9
-rw-r--r--sbin/mdmfs/mdmfs.8373
-rw-r--r--sbin/mdmfs/mdmfs.c689
-rw-r--r--sbin/mknod/Makefile8
-rw-r--r--sbin/mknod/mknod.8154
-rw-r--r--sbin/mknod/mknod.c166
-rw-r--r--sbin/mksnap_ffs/Makefile14
-rw-r--r--sbin/mksnap_ffs/mksnap_ffs.875
-rw-r--r--sbin/mksnap_ffs/mksnap_ffs.c130
-rw-r--r--sbin/mount/Makefile10
-rw-r--r--sbin/mount/extern.h33
-rw-r--r--sbin/mount/getmntopts.3180
-rw-r--r--sbin/mount/getmntopts.c182
-rw-r--r--sbin/mount/mntopts.h97
-rw-r--r--sbin/mount/mount.8491
-rw-r--r--sbin/mount/mount.c802
-rw-r--r--sbin/mount/mount_fs.c135
-rw-r--r--sbin/mount/pathnames.h33
-rw-r--r--sbin/mount/vfslist.c92
-rw-r--r--sbin/mount_autofs/Makefile8
-rw-r--r--sbin/mount_autofs/mount_autofs.871
-rw-r--r--sbin/mount_autofs/mount_autofs.c113
-rw-r--r--sbin/mount_cd9660/Makefile20
-rw-r--r--sbin/mount_cd9660/mount_cd9660.8160
-rw-r--r--sbin/mount_cd9660/mount_cd9660.c267
-rw-r--r--sbin/mount_ext2fs/Makefile13
-rw-r--r--sbin/mount_ext2fs/mount_ext2fs.872
-rw-r--r--sbin/mount_ext2fs/mount_ext2fs.c126
-rw-r--r--sbin/mount_hpfs/Makefile15
-rw-r--r--sbin/mount_hpfs/mount_hpfs.8100
-rw-r--r--sbin/mount_hpfs/mount_hpfs.c251
-rw-r--r--sbin/mount_msdosfs/Makefile21
-rw-r--r--sbin/mount_msdosfs/mount_msdosfs.8226
-rw-r--r--sbin/mount_msdosfs/mount_msdosfs.c331
-rw-r--r--sbin/mount_nfs/Makefile16
-rw-r--r--sbin/mount_nfs/mount_nfs.8338
-rw-r--r--sbin/mount_nfs/mount_nfs.c926
-rw-r--r--sbin/mount_nfs4/Makefile16
-rw-r--r--sbin/mount_nfs4/mount_nfs4.8201
-rw-r--r--sbin/mount_nfs4/mount_nfs4.c735
-rw-r--r--sbin/mount_ntfs/Makefile21
-rw-r--r--sbin/mount_ntfs/mount_ntfs.8167
-rw-r--r--sbin/mount_ntfs/mount_ntfs.c284
-rw-r--r--sbin/mount_nullfs/Makefile14
-rw-r--r--sbin/mount_nullfs/mount_nullfs.8244
-rw-r--r--sbin/mount_nullfs/mount_nullfs.c141
-rw-r--r--sbin/mount_reiserfs/Makefile14
-rw-r--r--sbin/mount_reiserfs/mount_reiserfs.890
-rw-r--r--sbin/mount_reiserfs/mount_reiserfs.c107
-rw-r--r--sbin/mount_std/Makefile23
-rw-r--r--sbin/mount_std/mount_std.8167
-rw-r--r--sbin/mount_std/mount_std.c163
-rw-r--r--sbin/mount_udf/Makefile18
-rw-r--r--sbin/mount_udf/mount_udf.876
-rw-r--r--sbin/mount_udf/mount_udf.c184
-rw-r--r--sbin/mount_umapfs/Makefile14
-rw-r--r--sbin/mount_umapfs/mount_umapfs.8146
-rw-r--r--sbin/mount_umapfs/mount_umapfs.c235
-rw-r--r--sbin/mount_umapfs/sample.group.mapfile2
-rw-r--r--sbin/mount_umapfs/sample.user.mapfile3
-rw-r--r--sbin/mount_umapfs/umap_manual175
-rw-r--r--sbin/mount_unionfs/Makefile14
-rw-r--r--sbin/mount_unionfs/mount_unionfs.8216
-rw-r--r--sbin/mount_unionfs/mount_unionfs.c160
-rw-r--r--sbin/natd/HISTORY146
-rw-r--r--sbin/natd/Makefile10
-rw-r--r--sbin/natd/README50
-rw-r--r--sbin/natd/icmp.c127
-rw-r--r--sbin/natd/natd.8640
-rw-r--r--sbin/natd/natd.c1961
-rw-r--r--sbin/natd/natd.h24
-rw-r--r--sbin/natd/samples/natd.cf.sample92
-rw-r--r--sbin/natd/samples/natd.test14
-rw-r--r--sbin/newfs/Makefile17
-rw-r--r--sbin/newfs/mkfs.c1080
-rw-r--r--sbin/newfs/newfs.8264
-rw-r--r--sbin/newfs/newfs.c443
-rw-r--r--sbin/newfs/newfs.h71
-rw-r--r--sbin/newfs/ref.test7
-rw-r--r--sbin/newfs/runtest00.sh19
-rw-r--r--sbin/newfs/runtest01.sh27
-rw-r--r--sbin/newfs_msdos/Makefile12
-rw-r--r--sbin/newfs_msdos/newfs_msdos.8193
-rw-r--r--sbin/newfs_msdos/newfs_msdos.c896
-rw-r--r--sbin/nfsiod/Makefile8
-rw-r--r--sbin/nfsiod/nfsiod.887
-rw-r--r--sbin/nfsiod/nfsiod.c137
-rw-r--r--sbin/nos-tun/Makefile8
-rw-r--r--sbin/nos-tun/nos-tun.892
-rw-r--r--sbin/nos-tun/nos-tun.c395
-rw-r--r--sbin/pfctl/Makefile25
-rw-r--r--sbin/pfctl/missing/altq/altq.h204
-rw-r--r--sbin/pfctl/missing/altq/altq_cbq.h232
-rw-r--r--sbin/pfctl/missing/altq/altq_classq.h203
-rw-r--r--sbin/pfctl/missing/altq/altq_hfsc.h325
-rw-r--r--sbin/pfctl/missing/altq/altq_priq.h172
-rw-r--r--sbin/pfctl/missing/altq/altq_red.h199
-rw-r--r--sbin/pfctl/missing/altq/altq_rio.h145
-rw-r--r--sbin/pfctl/missing/altq/altq_rmclass.h263
-rw-r--r--sbin/pfctl/missing/altq/altq_rmclass_debug.h113
-rw-r--r--sbin/pfctl/missing/altq/altq_var.h267
-rw-r--r--sbin/pfctl/missing/altq/altq_wfq.h129
-rw-r--r--sbin/pflogd/Makefile13
-rw-r--r--sbin/ping/Makefile21
-rw-r--r--sbin/ping/ping.8544
-rw-r--r--sbin/ping/ping.c1698
-rw-r--r--sbin/ping6/Makefile16
-rw-r--r--sbin/ping6/ping6.8510
-rw-r--r--sbin/ping6/ping6.c2727
-rw-r--r--sbin/quotacheck/Makefile11
-rw-r--r--sbin/quotacheck/preen.c282
-rw-r--r--sbin/quotacheck/quotacheck.8162
-rw-r--r--sbin/quotacheck/quotacheck.c691
-rw-r--r--sbin/rcorder/Makefile21
-rw-r--r--sbin/rcorder/ealloc.c123
-rw-r--r--sbin/rcorder/ealloc.h6
-rw-r--r--sbin/rcorder/hash.c438
-rw-r--r--sbin/rcorder/hash.h130
-rw-r--r--sbin/rcorder/rcorder.8167
-rw-r--r--sbin/rcorder/rcorder.c828
-rw-r--r--sbin/rcorder/sprite.h113
-rw-r--r--sbin/reboot/Makefile25
-rw-r--r--sbin/reboot/boot_i386.8381
-rw-r--r--sbin/reboot/nextboot.8125
-rw-r--r--sbin/reboot/nextboot.sh57
-rw-r--r--sbin/reboot/reboot.8143
-rw-r--r--sbin/reboot/reboot.c227
-rw-r--r--sbin/recoverdisk/Makefile12
-rw-r--r--sbin/recoverdisk/recoverdisk.c160
-rw-r--r--sbin/restore/Makefile15
-rw-r--r--sbin/restore/dirs.c722
-rw-r--r--sbin/restore/extern.h107
-rw-r--r--sbin/restore/interactive.c769
-rw-r--r--sbin/restore/main.c373
-rw-r--r--sbin/restore/restore.8497
-rw-r--r--sbin/restore/restore.c847
-rw-r--r--sbin/restore/restore.h151
-rw-r--r--sbin/restore/symtab.c614
-rw-r--r--sbin/restore/tape.c1407
-rw-r--r--sbin/restore/utilities.c421
-rw-r--r--sbin/route/Makefile22
-rw-r--r--sbin/route/keywords51
-rw-r--r--sbin/route/route.8428
-rw-r--r--sbin/route/route.c1664
-rw-r--r--sbin/routed/Makefile12
-rw-r--r--sbin/routed/Makefile.inc3
-rw-r--r--sbin/routed/defs.h674
-rw-r--r--sbin/routed/if.c1398
-rw-r--r--sbin/routed/input.c1013
-rw-r--r--sbin/routed/main.c959
-rw-r--r--sbin/routed/output.c982
-rw-r--r--sbin/routed/parms.c1052
-rw-r--r--sbin/routed/pathnames.h52
-rw-r--r--sbin/routed/radix.c893
-rw-r--r--sbin/routed/radix.h161
-rw-r--r--sbin/routed/rdisc.c1076
-rw-r--r--sbin/routed/routed.8732
-rw-r--r--sbin/routed/rtquery/Makefile11
-rw-r--r--sbin/routed/rtquery/rtquery.8131
-rw-r--r--sbin/routed/rtquery/rtquery.c919
-rw-r--r--sbin/routed/table.c2153
-rw-r--r--sbin/routed/trace.c1023
-rw-r--r--sbin/rtsol/Makefile28
-rw-r--r--sbin/savecore/Makefile9
-rw-r--r--sbin/savecore/savecore.8148
-rw-r--r--sbin/savecore/savecore.c614
-rw-r--r--sbin/sconfig/Makefile8
-rw-r--r--sbin/sconfig/sconfig.8607
-rw-r--r--sbin/sconfig/sconfig.c1190
-rw-r--r--sbin/setkey/Makefile64
-rw-r--r--sbin/setkey/parse.y1267
-rw-r--r--sbin/setkey/sample.cf219
-rw-r--r--sbin/setkey/scriptdump.pl56
-rw-r--r--sbin/setkey/setkey.8723
-rw-r--r--sbin/setkey/setkey.c632
-rw-r--r--sbin/setkey/test-pfkey.c531
-rw-r--r--sbin/setkey/test-policy.c161
-rw-r--r--sbin/setkey/token.l286
-rw-r--r--sbin/setkey/vchar.h36
-rw-r--r--sbin/shutdown/Makefile13
-rw-r--r--sbin/shutdown/shutdown.8190
-rw-r--r--sbin/shutdown/shutdown.c516
-rw-r--r--sbin/slattach/Makefile12
-rw-r--r--sbin/slattach/slattach.8271
-rw-r--r--sbin/slattach/slattach.c599
-rw-r--r--sbin/spppcontrol/Makefile6
-rw-r--r--sbin/spppcontrol/spppcontrol.8275
-rw-r--r--sbin/spppcontrol/spppcontrol.c264
-rw-r--r--sbin/startslip/Makefile9
-rw-r--r--sbin/startslip/startslip.1213
-rw-r--r--sbin/startslip/startslip.c595
-rw-r--r--sbin/sunlabel/Makefile25
-rw-r--r--sbin/sunlabel/runtest.sh157
-rw-r--r--sbin/sunlabel/sunlabel.8433
-rw-r--r--sbin/sunlabel/sunlabel.c1007
-rw-r--r--sbin/swapon/Makefile11
-rw-r--r--sbin/swapon/swapon.8177
-rw-r--r--sbin/swapon/swapon.c276
-rw-r--r--sbin/sysctl/Makefile8
-rw-r--r--sbin/sysctl/sysctl.8316
-rw-r--r--sbin/sysctl/sysctl.c702
-rw-r--r--sbin/tunefs/Makefile9
-rw-r--r--sbin/tunefs/tunefs.8168
-rw-r--r--sbin/tunefs/tunefs.c425
-rw-r--r--sbin/umount/Makefile16
-rw-r--r--sbin/umount/umount.8144
-rw-r--r--sbin/umount/umount.c597
542 files changed, 180396 insertions, 0 deletions
diff --git a/sbin/Makefile b/sbin/Makefile
new file mode 100644
index 0000000..bf43850
--- /dev/null
+++ b/sbin/Makefile
@@ -0,0 +1,139 @@
+# @(#)Makefile 8.5 (Berkeley) 3/31/94
+# $FreeBSD$
+
+# XXX MISSING: icheck ncheck
+
+SUBDIR= adjkerntz \
+ atacontrol \
+ ${_atm} \
+ badsect \
+ bsdlabel \
+ camcontrol \
+ ccdconfig \
+ clri \
+ comcontrol \
+ conscontrol \
+ ${_devd} \
+ devfs \
+ dhclient \
+ dmesg \
+ dump \
+ dumpfs \
+ dumpon \
+ ${_fdisk} \
+ ${_fdisk_pc98} \
+ ffsinfo \
+ fsck \
+ fsck_ffs \
+ fsck_msdosfs \
+ fsdb \
+ fsirand \
+ gbde \
+ geom \
+ ggate \
+ gpt \
+ growfs \
+ gvinum \
+ ifconfig \
+ init \
+ ${_ip6fw} \
+ ${_ipf} \
+ ipfw \
+ kldconfig \
+ kldload \
+ kldstat \
+ kldunload \
+ ldconfig \
+ ${_mca} \
+ md5 \
+ mdconfig \
+ mdmfs \
+ mknod \
+ mksnap_ffs \
+ mount \
+ mount_cd9660 \
+ mount_ext2fs \
+ mount_msdosfs \
+ mount_nfs \
+ mount_nfs4 \
+ mount_ntfs \
+ mount_nullfs \
+ mount_reiserfs \
+ mount_std \
+ mount_udf \
+ mount_umapfs \
+ mount_unionfs \
+ natd \
+ newfs \
+ newfs_msdos \
+ nfsiod \
+ nos-tun \
+ ${_pfctl} \
+ ${_pflogd} \
+ ping \
+ ${_ping6} \
+ quotacheck \
+ rcorder \
+ reboot \
+ restore \
+ route \
+ routed \
+ rtsol \
+ savecore \
+ ${_sconfig} \
+ setkey \
+ shutdown \
+ slattach \
+ spppcontrol \
+ startslip \
+ sunlabel \
+ swapon \
+ sysctl \
+ tunefs \
+ umount \
+
+.if !defined(NO_ATM)
+_atm= atm
+.endif
+
+.if !defined(NO_CXX)
+_devd= devd
+.endif
+
+.if !defined(NO_IPFILTER)
+_ipf= ipf
+.endif
+
+.if !defined(NO_PF)
+_pfctl= pfctl
+_pflogd= pflogd
+.endif
+
+.if !defined(NO_INET6)
+_ip6fw= ip6fw
+_ping6= ping6
+.endif
+
+.if ${MACHINE_ARCH} == "i386"
+.if ${MACHINE} == "i386"
+_fdisk= fdisk
+.elif ${MACHINE} == "pc98"
+_fdisk_pc98= fdisk_pc98
+.endif
+_sconfig= sconfig
+.endif
+
+.if ${MACHINE_ARCH} == "amd64"
+_fdisk= fdisk
+.endif
+
+.if ${MACHINE_ARCH} == "arm"
+_fdisk= fdisk
+.endif
+
+.if ${MACHINE_ARCH} == "ia64"
+_fdisk= fdisk
+_mca= mca
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/sbin/Makefile.inc b/sbin/Makefile.inc
new file mode 100644
index 0000000..d1fed26
--- /dev/null
+++ b/sbin/Makefile.inc
@@ -0,0 +1,9 @@
+# @(#)Makefile.inc 8.1 (Berkeley) 6/8/93
+# $FreeBSD$
+
+BINDIR?= /sbin
+WARNS?= 2
+
+.if defined(NO_DYNAMICROOT)
+NO_SHARED?= YES
+.endif
diff --git a/sbin/adjkerntz/Makefile b/sbin/adjkerntz/Makefile
new file mode 100644
index 0000000..27c1289
--- /dev/null
+++ b/sbin/adjkerntz/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= adjkerntz
+MAN= adjkerntz.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/adjkerntz/adjkerntz.8 b/sbin/adjkerntz/adjkerntz.8
new file mode 100644
index 0000000..5591f2e
--- /dev/null
+++ b/sbin/adjkerntz/adjkerntz.8
@@ -0,0 +1,195 @@
+.\" Copyright (C) 1993-1998 by Andrey A. Chernov, Moscow, Russia.
+.\" 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 DEVELOPERS ``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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 4, 1996
+.Dt ADJKERNTZ 8
+.Os
+.Sh NAME
+.Nm adjkerntz
+.Nd "adjust local time CMOS clock to reflect time zone changes and keep current timezone offset for the kernel"
+.Sh SYNOPSIS
+.Nm
+.Fl i
+.Nm
+.Fl a Op Fl s
+.Sh DESCRIPTION
+The
+.Nm
+utility maintains the proper relationship between the kernel clock, which
+is always set to UTC, and the CMOS clock, which may be set to local
+time.
+The
+.Nm
+utility also informs the kernel about machine timezone shifts to
+maintain proper timestamps for local time file systems such as the MS-DOS
+file system.
+The main purpose of this thing is not general fixing of
+initially broken MS-DOS file timestamp idea but keeping
+the same timestamps between
+.Fx
+MS-DOS file system
+and MS-DOS operating system installed on the same
+machine.
+If the file
+.Pa /etc/wall_cmos_clock
+exists, it means that CMOS clock keeps local time (MS-DOS and MS-Windows
+compatible mode).
+If that file does not exist, it means that the CMOS clock keeps UTC time.
+The
+.Nm
+utility passes this state to the
+.Pa machdep.wall_cmos_clock
+kernel variable.
+.Pp
+Adjustments may be needed at system startup and shutdown, and
+whenever a time zone change occurs.
+To handle these different situations,
+.Nm
+is invoked in two ways:
+.Bl -tag -width 4n
+.It Fl i
+This form handles system startups and shutdowns.
+The
+.Nm
+utility is invoked with this option from
+.Pa /etc/rc
+on entry to multi-user mode, before any other daemons have been started.
+The
+.Nm
+utility puts itself into the background.
+Then, for a local time CMOS clock,
+.Nm
+reads the local time from it
+and sets the kernel clock to the corresponding UTC time.
+The
+.Nm
+utility also stores the local time zone offset into the
+.Pa machdep.adjkerntz
+kernel variable, for use by subsequent invocations of
+.Em "'adjkerntz -a'"
+and by local time file systems.
+.Pp
+For a local time CMOS clock
+.Em "'adjkerntz -i'"
+pauses, and remains inactive as a background daemon until it
+receives a SIGTERM.
+The SIGTERM will normally be sent by
+.Xr init 8
+when the system leaves multi-user mode (usually, because the system
+is being shut down).
+After receiving the SIGTERM,
+.Nm
+reads the UTC kernel clock and updates the CMOS clock, if necessary,
+to ensure that it reflects the current local time zone.
+Then
+.Nm
+exits.
+.It Fl a Op Fl s
+This form is used to update the local time CMOS clock and kernel
+.Pa machdep.adjkerntz
+variable when time zone changes occur,
+e.g., when entering or leaving daylight savings time.
+The
+.Nm
+utility uses the kernel clock's UTC time,
+the previously stored
+time zone offset, and the changed time zone rule to
+calculate a new time zone offset.
+It stores the new offset into the
+.Pa machdep.adjkerntz
+kernel variable, and updates the wall CMOS clock to the new local time.
+If
+.Em "'adjkerntz -a'"
+was started at a nonexistent time (during a timezone change), it exits
+with a warning diagnostic unless the
+.Fl s
+option was used, in which case
+.Nm
+sleeps 30 minutes and tries again.
+.Pp
+This form should be invoked from root's
+.Xr crontab 5
+every half hour between midnight and 5am, when most modern time
+zone changes occur.
+Warning: do not use the
+.Fl s
+option in a
+.Xr crontab 5
+command line, or multiple
+.Em "'adjkerntz -a'"
+instances could conflict with each other.
+.El
+.Pp
+The
+.Nm
+utility clears the kernel timezone structure and makes the kernel clock run
+in the UTC time zone.
+Super-user privileges are required for all operations.
+.Sh ENVIRONMENT
+.Bl -tag -width Fl
+.It Ev TZ
+Time zone change rule, see
+.Xr tzset 3 ;
+not needed when
+.Xr tzsetup 8
+or
+.Xr zic 8
+is used.
+.El
+.Sh FILES
+.Bl -tag -width /etc/wall_cmos_clock -compact
+.It Pa /etc/localtime
+Current zoneinfo file, see
+.Xr tzsetup 8
+and
+.Xr zic 8 .
+.It Pa /etc/wall_cmos_clock
+Empty file.
+Its presence indicates that the machine's CMOS clock is set to local
+time, while its absence indicates a UTC CMOS clock.
+.El
+.Sh DIAGNOSTICS
+No diagnostics.
+If an error occurs,
+.Nm
+logs an error message via
+.Xr syslog 3
+and exits with a nonzero return code.
+.Sh SEE ALSO
+.Xr tzset 3 ,
+.Xr crontab 5 ,
+.Xr mount_msdosfs 8 ,
+.Xr rc 8 ,
+.Xr sysctl 8 ,
+.Xr tzsetup 8 ,
+.Xr zic 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 1.0 .
+.Sh AUTHORS
+.An Andrey A. Chernov Aq ache@astral.msk.su
diff --git a/sbin/adjkerntz/adjkerntz.c b/sbin/adjkerntz/adjkerntz.c
new file mode 100644
index 0000000..bf39010
--- /dev/null
+++ b/sbin/adjkerntz/adjkerntz.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 1993-1998 by Andrey A. Chernov, Moscow, Russia.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#)Copyright (C) 1993-1996 by Andrey A. Chernov, Moscow, Russia.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Andrey A. Chernov <ache@astral.msk.su> Dec 20 1993
+ *
+ * Fix kernel time value if machine run wall CMOS clock
+ * (and /etc/wall_cmos_clock file present)
+ * using zoneinfo rules or direct TZ environment variable set.
+ * Use Joerg Wunsch idea for seconds accurate offset calculation
+ * with Garrett Wollman and Bruce Evans fixes.
+ *
+ */
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <machine/cpu.h>
+#include <sys/sysctl.h>
+
+#include "pathnames.h"
+
+/*#define DEBUG*/
+
+#define True (1)
+#define False (0)
+#define Unknown (-1)
+
+#define REPORT_PERIOD (30*60)
+
+static void fake(int);
+static void usage(void);
+
+static void
+fake(int unused __unused)
+{
+
+ /* Do nothing. */
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct tm local;
+ struct timeval tv, *stv;
+ struct timezone tz, *stz;
+ int kern_offset, wall_clock, disrtcset;
+ size_t len;
+ int mib[2];
+ /* Avoid time_t here, can be unsigned long or worse */
+ long offset, localsec, diff;
+ time_t initial_sec, final_sec;
+ int ch;
+ int initial_isdst = -1, final_isdst;
+ int need_restore = False, sleep_mode = False, looping,
+ init = Unknown;
+ sigset_t mask, emask;
+
+ while ((ch = getopt(argc, argv, "ais")) != -1)
+ switch((char)ch) {
+ case 'i': /* initial call, save offset */
+ if (init != Unknown)
+ usage();
+ init = True;
+ break;
+ case 'a': /* adjustment call, use saved offset */
+ if (init != Unknown)
+ usage();
+ init = False;
+ break;
+ case 's':
+ sleep_mode = True;
+ break;
+ default:
+ usage();
+ }
+ if (init == Unknown)
+ usage();
+
+ if (access(_PATH_CLOCK, F_OK) != 0)
+ return 0;
+
+ if (init)
+ sleep_mode = True;
+
+ sigemptyset(&mask);
+ sigemptyset(&emask);
+ sigaddset(&mask, SIGTERM);
+
+ openlog("adjkerntz", LOG_PID|LOG_PERROR, LOG_DAEMON);
+
+ (void) signal(SIGHUP, SIG_IGN);
+
+ if (init && daemon(0, 1)) {
+ syslog(LOG_ERR, "daemon: %m");
+ return 1;
+ }
+
+again:
+ (void) sigprocmask(SIG_BLOCK, &mask, NULL);
+ (void) signal(SIGTERM, fake);
+
+ diff = 0;
+ stv = NULL;
+ stz = NULL;
+ looping = False;
+
+ wall_clock = (access(_PATH_CLOCK, F_OK) == 0);
+ if (init && !sleep_mode) {
+ init = False;
+ if (!wall_clock)
+ return 0;
+ }
+
+ tzset();
+
+ mib[0] = CTL_MACHDEP;
+ mib[1] = CPU_ADJKERNTZ;
+ len = sizeof(kern_offset);
+ if (sysctl(mib, 2, &kern_offset, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "sysctl(get_offset): %m");
+ return 1;
+ }
+
+/****** Critical section, do all things as fast as possible ******/
+
+ /* get local CMOS clock and possible kernel offset */
+ if (gettimeofday(&tv, &tz)) {
+ syslog(LOG_ERR, "gettimeofday: %m");
+ return 1;
+ }
+
+ /* get the actual local timezone difference */
+ initial_sec = tv.tv_sec;
+
+recalculate:
+ local = *localtime(&initial_sec);
+ if (diff == 0)
+ initial_isdst = local.tm_isdst;
+ local.tm_isdst = initial_isdst;
+
+ /* calculate local CMOS diff from GMT */
+
+ localsec = mktime(&local);
+ if (localsec == -1) {
+ /*
+ * XXX user can only control local time, and it is
+ * unacceptable to fail here for init. 2:30 am in the
+ * middle of the nonexistent hour means 3:30 am.
+ */
+ if (!sleep_mode) {
+ syslog(LOG_WARNING,
+ "Warning: nonexistent local time, try to run later.");
+ syslog(LOG_WARNING, "Giving up.");
+ return 1;
+ }
+ syslog(LOG_WARNING,
+ "Warning: nonexistent local time.");
+ syslog(LOG_WARNING, "Will retry after %d minutes.",
+ REPORT_PERIOD / 60);
+ (void) signal(SIGTERM, SIG_DFL);
+ (void) sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ (void) sleep(REPORT_PERIOD);
+ goto again;
+ }
+ offset = -local.tm_gmtoff;
+#ifdef DEBUG
+ fprintf(stderr, "Initial offset: %ld secs\n", offset);
+#endif
+
+ /* correct the kerneltime for this diffs */
+ /* subtract kernel offset, if present, old offset too */
+
+ diff = offset - tz.tz_minuteswest * 60 - kern_offset;
+
+ if (diff != 0) {
+#ifdef DEBUG
+ fprintf(stderr, "Initial diff: %ld secs\n", diff);
+#endif
+ /* Yet one step for final time */
+
+ final_sec = initial_sec + diff;
+
+ /* get the actual local timezone difference */
+ local = *localtime(&final_sec);
+ final_isdst = diff < 0 ? initial_isdst : local.tm_isdst;
+ if (diff > 0 && initial_isdst != final_isdst) {
+ if (looping)
+ goto bad_final;
+ looping = True;
+ initial_isdst = final_isdst;
+ goto recalculate;
+ }
+ local.tm_isdst = final_isdst;
+
+ localsec = mktime(&local);
+ if (localsec == -1) {
+ bad_final:
+ /*
+ * XXX as above. The user has even less control,
+ * but perhaps we never get here.
+ */
+ if (!sleep_mode) {
+ syslog(LOG_WARNING,
+ "Warning: nonexistent final local time, try to run later.");
+ syslog(LOG_WARNING, "Giving up.");
+ return 1;
+ }
+ syslog(LOG_WARNING,
+ "Warning: nonexistent final local time.");
+ syslog(LOG_WARNING, "Will retry after %d minutes.",
+ REPORT_PERIOD / 60);
+ (void) signal(SIGTERM, SIG_DFL);
+ (void) sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ (void) sleep(REPORT_PERIOD);
+ goto again;
+ }
+ offset = -local.tm_gmtoff;
+#ifdef DEBUG
+ fprintf(stderr, "Final offset: %ld secs\n", offset);
+#endif
+
+ /* correct the kerneltime for this diffs */
+ /* subtract kernel offset, if present, old offset too */
+
+ diff = offset - tz.tz_minuteswest * 60 - kern_offset;
+
+ if (diff != 0) {
+#ifdef DEBUG
+ fprintf(stderr, "Final diff: %ld secs\n", diff);
+#endif
+ /*
+ * stv is abused as a flag. The important value
+ * is in `diff'.
+ */
+ stv = &tv;
+ }
+ }
+
+ if (tz.tz_dsttime != 0 || tz.tz_minuteswest != 0) {
+ tz.tz_dsttime = tz.tz_minuteswest = 0; /* zone info is garbage */
+ stz = &tz;
+ }
+ if (!wall_clock && stz == NULL)
+ stv = NULL;
+
+ /* if init or UTC clock and offset/date will be changed, */
+ /* disable RTC modification for a while. */
+
+ if ( (init && stv != NULL)
+ || ((init || !wall_clock) && kern_offset != offset)
+ ) {
+ mib[0] = CTL_MACHDEP;
+ mib[1] = CPU_DISRTCSET;
+ len = sizeof(disrtcset);
+ if (sysctl(mib, 2, &disrtcset, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "sysctl(get_disrtcset): %m");
+ return 1;
+ }
+ if (disrtcset == 0) {
+ disrtcset = 1;
+ need_restore = True;
+ if (sysctl(mib, 2, NULL, NULL, &disrtcset, len) == -1) {
+ syslog(LOG_ERR, "sysctl(set_disrtcset): %m");
+ return 1;
+ }
+ }
+ }
+
+ if ( (init && (stv != NULL || stz != NULL))
+ || (stz != NULL && stv == NULL)
+ ) {
+ if (stv != NULL) {
+ /*
+ * Get the time again, as close as possible to
+ * adjusting it, to minimise drift.
+ * XXX we'd better not fail between here and
+ * restoring disrtcset, since we don't clean up
+ * anything.
+ */
+ if (gettimeofday(&tv, (struct timezone *)NULL)) {
+ syslog(LOG_ERR, "gettimeofday: %m");
+ return 1;
+ }
+ tv.tv_sec += diff;
+ stv = &tv;
+ }
+ if (settimeofday(stv, stz)) {
+ syslog(LOG_ERR, "settimeofday: %m");
+ return 1;
+ }
+ }
+
+ /* setting CPU_ADJKERNTZ have a side effect: resettodr(), which */
+ /* can be disabled by CPU_DISRTCSET, so if init or UTC clock */
+ /* -- don't write RTC, else write RTC. */
+
+ if (kern_offset != offset) {
+ kern_offset = offset;
+ mib[0] = CTL_MACHDEP;
+ mib[1] = CPU_ADJKERNTZ;
+ len = sizeof(kern_offset);
+ if (sysctl(mib, 2, NULL, NULL, &kern_offset, len) == -1) {
+ syslog(LOG_ERR, "sysctl(update_offset): %m");
+ return 1;
+ }
+ }
+
+ mib[0] = CTL_MACHDEP;
+ mib[1] = CPU_WALLCLOCK;
+ len = sizeof(wall_clock);
+ if (sysctl(mib, 2, NULL, NULL, &wall_clock, len) == -1) {
+ syslog(LOG_ERR, "sysctl(put_wallclock): %m");
+ return 1;
+ }
+
+ if (need_restore) {
+ need_restore = False;
+ mib[0] = CTL_MACHDEP;
+ mib[1] = CPU_DISRTCSET;
+ disrtcset = 0;
+ len = sizeof(disrtcset);
+ if (sysctl(mib, 2, NULL, NULL, &disrtcset, len) == -1) {
+ syslog(LOG_ERR, "sysctl(restore_disrtcset): %m");
+ return 1;
+ }
+ }
+
+/****** End of critical section ******/
+
+ if (init && wall_clock) {
+ sleep_mode = False;
+ /* wait for signals and acts like -a */
+ (void) sigsuspend(&emask);
+ goto again;
+ }
+
+ return 0;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: adjkerntz -i",
+ "\t\t(initial call from /etc/rc)",
+ " adjkerntz -a [-s]",
+ "\t\t(adjustment call, -s for sleep/retry mode)");
+ exit(2);
+}
diff --git a/sbin/adjkerntz/pathnames.h b/sbin/adjkerntz/pathnames.h
new file mode 100644
index 0000000..c0e7526
--- /dev/null
+++ b/sbin/adjkerntz/pathnames.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 1993 by Andrew A. Chernov, Moscow, Russia.
+ * 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 DEVELOPERS ``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_CLOCK "/etc/wall_cmos_clock"
diff --git a/sbin/atacontrol/Makefile b/sbin/atacontrol/Makefile
new file mode 100644
index 0000000..73b9160
--- /dev/null
+++ b/sbin/atacontrol/Makefile
@@ -0,0 +1,7 @@
+#$FreeBSD$
+
+PROG= atacontrol
+MAN= atacontrol.8
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/sbin/atacontrol/atacontrol.8 b/sbin/atacontrol/atacontrol.8
new file mode 100644
index 0000000..3333e17
--- /dev/null
+++ b/sbin/atacontrol/atacontrol.8
@@ -0,0 +1,241 @@
+.\"
+.\" Copyright (c) 2000,2001,2002 Søren Schmidt <sos@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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 16, 2005
+.Dt ATACONTROL 8
+.Os
+.Sh NAME
+.Nm atacontrol
+.Nd ATA device driver control program
+.Sh SYNOPSIS
+.Nm
+.Aq Ar command
+.Ar args
+.Pp
+.Nm
+.Ic attach
+.Ar channel
+.Nm
+.Ic detach
+.Ar channel
+.Nm
+.Ic reinit
+.Ar channel
+.Nm
+.Ic create
+.Ar type Oo Ar interleave Oc Ar disk0 ... diskN
+.Nm
+.Ic delete
+.Ar raid
+.Nm
+.Ic addspare
+.Ar raid disk
+.Nm
+.Ic rebuild
+.Ar raid
+.Nm
+.Ic status
+.Ar raid
+.Nm
+.Ic mode
+.Ar device
+.Nm
+.Ic info
+.Ar channel
+.Nm
+.Ic cap
+.Ar device
+.Nm
+.Ic list
+.Sh DESCRIPTION
+The
+.Nm
+utility is a control program that provides the user access and control to the
+.Fx
+.Xr ata 4
+subsystem.
+.Pp
+The
+.Nm
+utility
+can cause severe system crashes and loss of data if used improperly.
+Please
+exercise caution when using this command!
+.Pp
+The
+.Ar channel
+argument is the ATA channel device (e.g., ata0) on which to operate.
+The following commands are supported:
+.Bl -tag -width "rebuild"
+.It Ic attach
+Attach an ATA
+.Ar channel .
+Devices on the channel are probed and attached as
+is done on boot.
+.It Ic detach
+Detach an ATA
+.Ar channel .
+Devices on the channel are removed from the kernel,
+and all outstanding transfers etc.\& are returned back to the system marked
+as failed.
+.It Ic reinit
+Reinitialize an ATA
+.Ar channel .
+Both devices on the channel are reset and
+initialized to the parameters the ATA driver has stored internally.
+Devices that have gone bad and no longer respond to the probe, or devices
+that have physically been removed, are removed from the kernel.
+Likewise are devices that show up during a reset, probed and attached.
+.It Ic create
+Create a
+.Ar type
+ATA RAID.
+The type can be
+.Cm RAID0
+(stripe),
+.Cm RAID1
+(mirror),
+.Cm RAID0+1 ,
+.Cm SPAN
+or
+.Cm JBOD .
+In case the RAID has a
+.Cm RAID0
+component,
+the
+.Ar interleave
+must be specified in number of sectors.
+The RAID will be created
+of the individual disks named
+.Bk -words
+.Ar disk0 ... diskN .
+.Ek
+.Pp
+Although the ATA driver allows for creating an ATA RAID on disks with any
+controller, there are restrictions.
+It is only possible to boot on
+an array if it is either located on a
+.Dq real
+ATA RAID controller like
+the Promise or Highpoint controllers, or if the RAID declared is of
+.Cm RAID1
+or
+.Cm SPAN
+type; in case of a
+.Cm SPAN ,
+the partition to boot must
+reside on the first disk in the SPAN.
+.It Ic delete
+Delete a RAID array on a RAID capable ATA controller.
+.It Ic addspare
+Add a spare disk to an existing RAID.
+.It Ic rebuild
+Rebuild a RAID1 array on a RAID capable ATA controller.
+.It Ic status
+Get the status of an ATA RAID.
+.It Ic mode
+Without the mode argument, the current transfer modes of the
+device are printed.
+If the mode argument is given, the ATA driver
+is asked to change the transfer mode to the one given.
+The ATA driver
+will reject modes that are not supported by the hardware.
+Modes are given like
+.Dq Li PIO3 ,
+.Dq Li udma2 ,
+.Dq Li udma100 ,
+case does not matter.
+.Pp
+Currently supported modes are:
+.Cm PIO0 , PIO1 , PIO2 , PIO3 , PIO4 ,
+.Cm WDMA2 ,
+.Cm UDMA2
+(alias
+.Cm UDMA33 ) ,
+.Cm UDMA4
+(alias
+.Cm UDMA66 ) ,
+.Cm UDMA5
+(alias
+.Cm UDMA100 )
+and
+.Cm UDMA6
+(alias
+.Cm UDMA133 ) .
+The device name and manufacture/version strings are shown.
+.It Ic cap
+Show detailed info about the device on
+.Ar device .
+.It Ic info
+Show info about the attached devices on the
+.Ar channel .
+.It Ic list
+Show info about all attached devices on all active controllers.
+.El
+.Sh EXAMPLES
+To get information on devices attached to a channel,
+use the command line:
+.Pp
+.Dl "atacontrol info ata0"
+.Pp
+To see the devices' current access modes, use the command line:
+.Pp
+.Dl "atacontrol mode ad0"
+.Pp
+which results in the modes of the devices being displayed as a string
+like this:
+.Pp
+.Dl "current mode = UDMA100"
+.Pp
+You can set the mode with
+.Nm
+and a string like the above,
+for example:
+.Pp
+.Dl "atacontrol mode ad0 PIO4"
+.Pp
+The new modes are set as soon as the
+.Nm
+command returns.
+.Sh SEE ALSO
+.Xr ata 4
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 4.6 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An S\(/oren Schmidt
+.Aq sos@FreeBSD.org .
+.Pp
+This manual page was written by
+.An S\(/oren Schmidt
+.Aq sos@FreeBSD.org .
diff --git a/sbin/atacontrol/atacontrol.c b/sbin/atacontrol/atacontrol.c
new file mode 100644
index 0000000..00ff59d
--- /dev/null
+++ b/sbin/atacontrol/atacontrol.c
@@ -0,0 +1,584 @@
+/*-
+ * Copyright (c) 2000 - 2006 Søren Schmidt <sos@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,
+ * without modification, immediately at the beginning of the file.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/ata.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+const char *mode2str(int mode);
+int str2mode(char *str);
+void usage(void);
+int version(int ver);
+void param_print(struct ata_params *parm);
+void cap_print(struct ata_params *parm);
+int ata_cap_print(int fd);
+int info_print(int fd, int channel, int prchan);
+
+const char *
+mode2str(int mode)
+{
+ switch (mode) {
+ case ATA_PIO: return "BIOSPIO";
+ case ATA_PIO0: return "PIO0";
+ case ATA_PIO1: return "PIO1";
+ case ATA_PIO2: return "PIO2";
+ case ATA_PIO3: return "PIO3";
+ case ATA_PIO4: return "PIO4";
+ case ATA_WDMA2: return "WDMA2";
+ case ATA_UDMA2: return "UDMA33";
+ case ATA_UDMA4: return "UDMA66";
+ case ATA_UDMA5: return "UDMA100";
+ case ATA_UDMA6: return "UDMA133";
+ case ATA_SA150: return "SATA150";
+ case ATA_DMA: return "BIOSDMA";
+ default: return "???";
+ }
+}
+
+int
+str2mode(char *str)
+{
+ if (!strcasecmp(str, "BIOSPIO")) return ATA_PIO;
+ if (!strcasecmp(str, "PIO0")) return ATA_PIO0;
+ if (!strcasecmp(str, "PIO1")) return ATA_PIO1;
+ if (!strcasecmp(str, "PIO2")) return ATA_PIO2;
+ if (!strcasecmp(str, "PIO3")) return ATA_PIO3;
+ if (!strcasecmp(str, "PIO4")) return ATA_PIO4;
+ if (!strcasecmp(str, "WDMA2")) return ATA_WDMA2;
+ if (!strcasecmp(str, "UDMA2")) return ATA_UDMA2;
+ if (!strcasecmp(str, "UDMA33")) return ATA_UDMA2;
+ if (!strcasecmp(str, "UDMA4")) return ATA_UDMA4;
+ if (!strcasecmp(str, "UDMA66")) return ATA_UDMA4;
+ if (!strcasecmp(str, "UDMA5")) return ATA_UDMA5;
+ if (!strcasecmp(str, "UDMA100")) return ATA_UDMA5;
+ if (!strcasecmp(str, "UDMA6")) return ATA_UDMA6;
+ if (!strcasecmp(str, "UDMA133")) return ATA_UDMA6;
+ if (!strcasecmp(str, "BIOSDMA")) return ATA_DMA;
+ return -1;
+}
+
+void
+usage()
+{
+ fprintf(stderr,
+ "usage: atacontrol <command> args:\n"
+ " atacontrol list\n"
+ " atacontrol info channel\n"
+ " atacontrol attach channel\n"
+ " atacontrol detach channel\n"
+ " atacontrol reinit channel\n"
+ " atacontrol create type [interleave] disk0 ... diskN\n"
+ " atacontrol delete array\n"
+ " atacontrol addspare array disk\n"
+ " atacontrol rebuild array\n"
+ " atacontrol status array\n"
+ " atacontrol mode device [mode]\n"
+ " atacontrol cap device\n"
+ );
+ exit(EX_USAGE);
+}
+
+int
+version(int ver)
+{
+ int bit;
+
+ if (ver == 0xffff)
+ return 0;
+ for (bit = 15; bit >= 0; bit--)
+ if (ver & (1<<bit))
+ return bit;
+ return 0;
+}
+
+void
+param_print(struct ata_params *parm)
+{
+ printf("<%.40s/%.8s> ", parm->model, parm->revision);
+ if (parm->satacapabilities && parm->satacapabilities != 0xffff) {
+ if (parm->satacapabilities & ATA_SATA_GEN2)
+ printf("Serial ATA II\n");
+ else if (parm->satacapabilities & ATA_SATA_GEN1)
+ printf("Serial ATA v1.0\n");
+ else
+ printf("Unknown serial ATA version\n");
+ }
+ else
+ printf("ATA/ATAPI revision %d\n", version(parm->version_major));
+}
+
+void
+cap_print(struct ata_params *parm)
+{
+ u_int32_t lbasize = (u_int32_t)parm->lba_size_1 |
+ ((u_int32_t)parm->lba_size_2 << 16);
+
+ u_int64_t lbasize48 = ((u_int64_t)parm->lba_size48_1) |
+ ((u_int64_t)parm->lba_size48_2 << 16) |
+ ((u_int64_t)parm->lba_size48_3 << 32) |
+ ((u_int64_t)parm->lba_size48_4 << 48);
+
+ printf("\n");
+ printf("Protocol ");
+ if (parm->satacapabilities && parm->satacapabilities != 0xffff) {
+ if (parm->satacapabilities & ATA_SATA_GEN2)
+ printf("Serial ATA II\n");
+ else if (parm->satacapabilities & ATA_SATA_GEN1)
+ printf("Serial ATA v1.0\n");
+ else
+ printf("Unknown serial ATA version\n");
+ }
+ else
+ printf("ATA/ATAPI revision %d\n", version(parm->version_major));
+ printf("device model %.40s\n", parm->model);
+ printf("serial number %.20s\n", parm->serial);
+ printf("firmware revision %.8s\n", parm->revision);
+
+ printf("cylinders %d\n", parm->cylinders);
+ printf("heads %d\n", parm->heads);
+ printf("sectors/track %d\n", parm->sectors);
+
+ printf("lba%ssupported ",
+ parm->capabilities1 & ATA_SUPPORT_LBA ? " " : " not ");
+ if (lbasize)
+ printf("%d sectors\n", lbasize);
+ else
+ printf("\n");
+
+ printf("lba48%ssupported ",
+ parm->support.command2 & ATA_SUPPORT_ADDRESS48 ? " " : " not ");
+ if (lbasize48)
+ printf("%ju sectors\n", (uintmax_t)lbasize48);
+ else
+ printf("\n");
+
+ printf("dma%ssupported\n",
+ parm->capabilities1 & ATA_SUPPORT_DMA ? " " : " not ");
+
+ printf("overlap%ssupported\n",
+ parm->capabilities1 & ATA_SUPPORT_OVERLAP ? " " : " not ");
+
+ printf("\nFeature "
+ "Support Enable Value Vendor\n");
+
+ printf("write cache %s %s\n",
+ parm->support.command1 & ATA_SUPPORT_WRITECACHE ? "yes" : "no",
+ parm->enabled.command1 & ATA_SUPPORT_WRITECACHE ? "yes" : "no");
+
+ printf("read ahead %s %s\n",
+ parm->support.command1 & ATA_SUPPORT_LOOKAHEAD ? "yes" : "no",
+ parm->enabled.command1 & ATA_SUPPORT_LOOKAHEAD ? "yes" : "no");
+
+ if (parm->satacapabilities && parm->satacapabilities != 0xffff) {
+ printf("Native Command Queuing (NCQ) %s %s"
+ " %d/0x%02X\n",
+ parm->satacapabilities & ATA_SUPPORT_NCQ ?
+ "yes" : "no", " -",
+ (parm->satacapabilities & ATA_SUPPORT_NCQ) ?
+ ATA_QUEUE_LEN(parm->queue) : 0,
+ (parm->satacapabilities & ATA_SUPPORT_NCQ) ?
+ ATA_QUEUE_LEN(parm->queue) : 0);
+ }
+ printf("Tagged Command Queuing (TCQ) %s %s %d/0x%02X\n",
+ parm->support.command2 & ATA_SUPPORT_QUEUED ? "yes" : "no",
+ parm->enabled.command2 & ATA_SUPPORT_QUEUED ? "yes" : "no",
+ ATA_QUEUE_LEN(parm->queue), ATA_QUEUE_LEN(parm->queue));
+
+ printf("SMART %s %s\n",
+ parm->support.command1 & ATA_SUPPORT_SMART ? "yes" : "no",
+ parm->enabled.command1 & ATA_SUPPORT_SMART ? "yes" : "no");
+
+ printf("microcode download %s %s\n",
+ parm->support.command2 & ATA_SUPPORT_MICROCODE ? "yes" : "no",
+ parm->enabled.command2 & ATA_SUPPORT_MICROCODE ? "yes" : "no");
+
+ printf("security %s %s\n",
+ parm->support.command1 & ATA_SUPPORT_SECURITY ? "yes" : "no",
+ parm->enabled.command1 & ATA_SUPPORT_SECURITY ? "yes" : "no");
+
+ printf("power management %s %s\n",
+ parm->support.command1 & ATA_SUPPORT_POWERMGT ? "yes" : "no",
+ parm->enabled.command1 & ATA_SUPPORT_POWERMGT ? "yes" : "no");
+
+ printf("advanced power management %s %s %d/0x%02X\n",
+ parm->support.command2 & ATA_SUPPORT_APM ? "yes" : "no",
+ parm->enabled.command2 & ATA_SUPPORT_APM ? "yes" : "no",
+ parm->apm_value, parm->apm_value);
+
+ printf("automatic acoustic management %s %s "
+ "%d/0x%02X %d/0x%02X\n",
+ parm->support.command2 & ATA_SUPPORT_AUTOACOUSTIC ? "yes" :"no",
+ parm->enabled.command2 & ATA_SUPPORT_AUTOACOUSTIC ? "yes" :"no",
+ ATA_ACOUSTIC_CURRENT(parm->acoustic),
+ ATA_ACOUSTIC_CURRENT(parm->acoustic),
+ ATA_ACOUSTIC_VENDOR(parm->acoustic),
+ ATA_ACOUSTIC_VENDOR(parm->acoustic));
+}
+
+int
+ata_cap_print(int fd)
+{
+ struct ata_params params;
+
+ if (ioctl(fd, IOCATAGPARM, &params) < 0)
+ return errno;
+ cap_print(&params);
+ return 0;
+}
+
+int
+info_print(int fd, int channel, int prchan)
+{
+ struct ata_ioc_devices devices;
+
+ devices.channel = channel;
+
+ if (ioctl(fd, IOCATADEVICES, &devices) < 0)
+ return errno;
+
+ if (prchan)
+ printf("ATA channel %d:\n", channel);
+ printf("%sMaster: ", prchan ? " " : "");
+ if (*devices.name[0]) {
+ printf("%4.4s ", devices.name[0]);
+ param_print(&devices.params[0]);
+ }
+ else
+ printf(" no device present\n");
+ printf("%sSlave: ", prchan ? " " : "");
+ if (*devices.name[1]) {
+ printf("%4.4s ", devices.name[1]);
+ param_print(&devices.params[1]);
+ }
+ else
+ printf(" no device present\n");
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ int fd;
+
+ if (argc < 2)
+ usage();
+
+ if (!strcmp(argv[1], "mode") && (argc == 3 || argc == 4)) {
+ int disk, mode;
+ char device[64];
+
+ if (!(sscanf(argv[2], "ad%d", &disk) == 1 ||
+ sscanf(argv[2], "acd%d", &disk) == 1 ||
+ sscanf(argv[2], "afd%d", &disk) == 1 ||
+ sscanf(argv[2], "ast%d", &disk) == 1)) {
+ fprintf(stderr, "atacontrol: Invalid device %s\n",
+ argv[2]);
+ exit(EX_USAGE);
+ }
+ sprintf(device, "/dev/%s", argv[2]);
+ if ((fd = open(device, O_RDONLY)) < 0)
+ err(1, "device not found");
+ if (argc == 4) {
+ mode = str2mode(argv[3]);
+ if (ioctl(fd, IOCATASMODE, &mode) < 0)
+ warn("ioctl(IOCATASMODE)");
+ }
+ if (argc == 3 || argc == 4) {
+ if (ioctl(fd, IOCATAGMODE, &mode) < 0)
+ err(1, "ioctl(IOCATAGMODE)");
+ printf("current mode = %s\n", mode2str(mode));
+ }
+ exit(EX_OK);
+ }
+ if (!strcmp(argv[1], "cap") && argc == 3) {
+ int disk;
+ char device[64];
+
+ if (!(sscanf(argv[2], "ad%d", &disk) == 1 ||
+ sscanf(argv[2], "acd%d", &disk) == 1 ||
+ sscanf(argv[2], "afd%d", &disk) == 1 ||
+ sscanf(argv[2], "ast%d", &disk) == 1)) {
+ fprintf(stderr, "atacontrol: Invalid device %s\n",
+ argv[2]);
+ exit(EX_USAGE);
+ }
+ sprintf(device, "/dev/%s", argv[2]);
+ if ((fd = open(device, O_RDONLY)) < 0)
+ err(1, "device not found");
+ ata_cap_print(fd);
+ exit(EX_OK);
+ }
+
+ if ((fd = open("/dev/ata", O_RDWR)) < 0)
+ err(1, "control device not found");
+
+ if (!strcmp(argv[1], "list") && argc == 2) {
+ int maxchannel, channel;
+
+ if (ioctl(fd, IOCATAGMAXCHANNEL, &maxchannel) < 0)
+ err(1, "ioctl(IOCATAGMAXCHANNEL)");
+ for (channel = 0; channel < maxchannel; channel++)
+ info_print(fd, channel, 1);
+ exit(EX_OK);
+ }
+ if (!strcmp(argv[1], "info") && argc == 3) {
+ int channel;
+
+ if (!(sscanf(argv[2], "ata%d", &channel) == 1)) {
+ fprintf(stderr,
+ "atacontrol: Invalid channel %s\n", argv[2]);
+ exit(EX_USAGE);
+ }
+ info_print(fd, channel, 0);
+ exit(EX_OK);
+ }
+ if (!strcmp(argv[1], "detach") && argc == 3) {
+ int channel;
+
+ if (!(sscanf(argv[2], "ata%d", &channel) == 1)) {
+ fprintf(stderr,
+ "atacontrol: Invalid channel %s\n", argv[2]);
+ exit(EX_USAGE);
+ }
+ if (ioctl(fd, IOCATADETACH, &channel) < 0)
+ err(1, "ioctl(IOCATADETACH)");
+ exit(EX_OK);
+ }
+ if (!strcmp(argv[1], "attach") && argc == 3) {
+ int channel;
+
+ if (!(sscanf(argv[2], "ata%d", &channel) == 1)) {
+ fprintf(stderr,
+ "atacontrol: Invalid channel %s\n", argv[2]);
+ exit(EX_USAGE);
+ }
+ if (ioctl(fd, IOCATAATTACH, &channel) < 0)
+ err(1, "ioctl(IOCATAATTACH)");
+ info_print(fd, channel, 0);
+ exit(EX_OK);
+ }
+ if (!strcmp(argv[1], "reinit") && argc == 3) {
+ int channel;
+
+ if (!(sscanf(argv[2], "ata%d", &channel) == 1)) {
+ fprintf(stderr,
+ "atacontrol: Invalid channel %s\n", argv[2]);
+ exit(EX_USAGE);
+ }
+ if (ioctl(fd, IOCATAREINIT, &channel) < 0)
+ warn("ioctl(IOCATAREINIT)");
+ info_print(fd, channel, 0);
+ exit(EX_OK);
+ }
+ if (!strcmp(argv[1], "create")) {
+ int disk, dev, offset;
+ struct ata_ioc_raid_config config;
+
+ bzero(&config, sizeof(config));
+ if (argc > 2) {
+ if (!strcasecmp(argv[2], "RAID0") ||
+ !strcasecmp(argv[2], "stripe"))
+ config.type = AR_RAID0;
+ if (!strcasecmp(argv[2], "RAID1") ||
+ !strcasecmp(argv[2],"mirror"))
+ config.type = AR_RAID1;
+ if (!strcasecmp(argv[2], "RAID0+1") ||
+ !strcasecmp(argv[2],"RAID10"))
+ config.type = AR_RAID01;
+ if (!strcasecmp(argv[2], "RAID5"))
+ config.type = AR_RAID5;
+ if (!strcasecmp(argv[2], "SPAN"))
+ config.type = AR_SPAN;
+ if (!strcasecmp(argv[2], "JBOD"))
+ config.type = AR_JBOD;
+ }
+ if (!config.type) {
+ fprintf(stderr, "atacontrol: Invalid RAID type %s\n",
+ argv[2]);
+ fprintf(stderr, "atacontrol: Valid RAID types: \n");
+ fprintf(stderr, " stripe | mirror | "
+ "RAID0 | RAID1 | RAID0+1 | RAID5 | "
+ "SPAN | JBOD\n");
+ exit(EX_USAGE);
+ }
+
+ if (config.type == AR_RAID0 ||
+ config.type == AR_RAID01 ||
+ config.type == AR_RAID5) {
+ if (argc < 4 ||
+ !sscanf(argv[3], "%d", &config.interleave) == 1) {
+ fprintf(stderr,
+ "atacontrol: Invalid interleave %s\n",
+ argv[3]);
+ exit(EX_USAGE);
+ }
+ offset = 4;
+ }
+ else
+ offset = 3;
+
+ for (disk = 0; disk < 16 && (offset + disk) < argc; disk++) {
+ if (!(sscanf(argv[offset + disk], "ad%d", &dev) == 1)) {
+ fprintf(stderr,
+ "atacontrol: Invalid disk %s\n",
+ argv[offset + disk]);
+ exit(EX_USAGE);
+ }
+ config.disks[disk] = dev;
+ }
+
+ if ((config.type == AR_RAID1 || config.type == AR_RAID01) &&
+ disk < 2) {
+ fprintf(stderr, "atacontrol: At least 2 disks must be "
+ "specified\n");
+ exit(EX_USAGE);
+ }
+
+ config.total_disks = disk;
+ if (ioctl(fd, IOCATARAIDCREATE, &config) < 0)
+ err(1, "ioctl(IOCATARAIDCREATE)");
+ else
+ printf("ar%d created\n", config.lun);
+ exit(EX_OK);
+ }
+ if (!strcmp(argv[1], "delete") && argc == 3) {
+ int array;
+
+ if (!(sscanf(argv[2], "ar%d", &array) == 1)) {
+ fprintf(stderr,
+ "atacontrol: Invalid array %s\n", argv[2]);
+ exit(EX_USAGE);
+ }
+ if (ioctl(fd, IOCATARAIDDELETE, &array) < 0)
+ warn("ioctl(IOCATARAIDDELETE)");
+ exit(EX_OK);
+ }
+ if (!strcmp(argv[1], "addspare") && argc == 4) {
+ struct ata_ioc_raid_config config;
+
+ if (!(sscanf(argv[2], "ar%d", &config.lun) == 1)) {
+ fprintf(stderr,
+ "atacontrol: Invalid array %s\n", argv[2]);
+ usage();
+ }
+ if (!(sscanf(argv[3], "ad%d", &config.disks[0]) == 1)) {
+ fprintf(stderr,
+ "atacontrol: Invalid disk %s\n", argv[3]);
+ usage();
+ }
+ if (ioctl(fd, IOCATARAIDADDSPARE, &config) < 0)
+ warn("ioctl(IOCATARAIDADDSPARE)");
+ exit(EX_OK);
+ }
+ if (!strcmp(argv[1], "rebuild") && argc == 3) {
+ int array;
+
+ if (!(sscanf(argv[2], "ar%d", &array) == 1)) {
+ fprintf(stderr,
+ "atacontrol: Invalid array %s\n", argv[2]);
+ usage();
+ }
+ if (ioctl(fd, IOCATARAIDREBUILD, &array) < 0)
+ warn("ioctl(IOCATARAIDREBUILD)");
+ else {
+ char buffer[128];
+ sprintf(buffer, "/usr/bin/nice -n 20 /bin/dd "
+ "if=/dev/ar%d of=/dev/null bs=1m &",
+ array);
+ if (system(buffer))
+ warn("background dd");
+ }
+ exit(EX_OK);
+ }
+ if (!strcmp(argv[1], "status") && argc == 3) {
+ struct ata_ioc_raid_config config;
+ int i;
+
+ if (!(sscanf(argv[2], "ar%d", &config.lun) == 1)) {
+ fprintf(stderr,
+ "atacontrol: Invalid array %s\n", argv[2]);
+ usage();
+ }
+ if (ioctl(fd, IOCATARAIDSTATUS, &config) < 0)
+ err(1, "ioctl(IOCATARAIDSTATUS)");
+
+ printf("ar%d: ATA ", config.lun);
+ switch (config.type) {
+ case AR_RAID0:
+ printf("RAID0 stripesize=%d", config.interleave);
+ break;
+ case AR_RAID1:
+ printf("RAID1");
+ break;
+ case AR_RAID01:
+ printf("RAID0+1 stripesize=%d", config.interleave);
+ break;
+ case AR_RAID5:
+ printf("RAID5 stripesize=%d", config.interleave);
+ break;
+ case AR_JBOD:
+ printf("JBOD");
+ case AR_SPAN:
+ printf("SPAN");
+ break;
+ }
+ printf(" subdisks: ");
+ for (i = 0; i < config.total_disks; i++) {
+ if (config.disks[i] >= 0)
+ printf("ad%d ", config.disks[i]);
+ else
+ printf("DOWN ");
+ }
+ printf("status: ");
+ switch (config.status) {
+ case AR_READY:
+ printf("READY\n");
+ break;
+ case AR_READY | AR_DEGRADED:
+ printf("DEGRADED\n");
+ break;
+ case AR_READY | AR_DEGRADED | AR_REBUILDING:
+ printf("REBUILDING %d%% completed\n",
+ config.progress);
+ break;
+ default:
+ printf("BROKEN\n");
+ }
+ exit(EX_OK);
+ }
+ usage();
+ exit(EX_OK);
+}
diff --git a/sbin/atm/Makefile b/sbin/atm/Makefile
new file mode 100644
index 0000000..ed7ca1a
--- /dev/null
+++ b/sbin/atm/Makefile
@@ -0,0 +1,31 @@
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $Id: Makefile,v 1.5 1998/07/10 16:01:58 jpt Exp $
+# $FreeBSD$
+
+SUBDIR= atm \
+ atmconfig \
+ fore_dnld \
+ ilmid
+
+.include <bsd.subdir.mk>
diff --git a/sbin/atm/Makefile.inc b/sbin/atm/Makefile.inc
new file mode 100644
index 0000000..3d5738a
--- /dev/null
+++ b/sbin/atm/Makefile.inc
@@ -0,0 +1,26 @@
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $Id: Makefile.inc,v 1.5 1998/07/10 16:01:58 jpt Exp $
+# $FreeBSD$
+
+.include "../Makefile.inc"
diff --git a/sbin/atm/atm/Makefile b/sbin/atm/atm/Makefile
new file mode 100644
index 0000000..973e5cc
--- /dev/null
+++ b/sbin/atm/atm/Makefile
@@ -0,0 +1,42 @@
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $Id: Makefile,v 1.5 1998/07/10 16:01:58 jpt Exp $
+# $FreeBSD$
+
+PROG= atm
+SRCS= atm.c atm_fore200.c atm_inet.c atm_print.c \
+ atm_set.c atm_show.c atm_subr.c
+MAN= atm.8
+
+.if ${MACHINE_ARCH} == "arm"
+WARNS?= 3
+.else
+WARNS?= 6
+.endif
+
+CFLAGS+= -I${.CURDIR}/../../../sys
+
+DPADD= ${LIBATM}
+LDADD= -latm
+
+.include <bsd.prog.mk>
diff --git a/sbin/atm/atm/atm.8 b/sbin/atm/atm/atm.8
new file mode 100644
index 0000000..25c4b75
--- /dev/null
+++ b/sbin/atm/atm/atm.8
@@ -0,0 +1,993 @@
+.\"
+.\" ===================================
+.\" HARP | Host ATM Research Platform
+.\" ===================================
+.\"
+.\"
+.\" This Host ATM Research Platform ("HARP") file (the "Software") is
+.\" made available by Network Computing Services, Inc. ("NetworkCS")
+.\" "AS IS". NetworkCS does not provide maintenance, improvements or
+.\" support of any kind.
+.\"
+.\" NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+.\" INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+.\" SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+.\" In no event shall NetworkCS be responsible for any damages, including
+.\" but not limited to consequential damages, arising from or relating to
+.\" any use of the Software or related support.
+.\"
+.\" Copyright 1994-1998 Network Computing Services, Inc.
+.\"
+.\" Copies of this Software may be made, however, the above copyright
+.\" notice must be reproduced on all copies.
+.\"
+.\" @(#) $FreeBSD$
+.\"
+.\"
+.de EX \"Begin example
+.ne 5
+.if n .sp 1
+.if t .sp .5
+.nf
+.in +.5i
+..
+.de EE
+.fi
+.in -.5i
+.if n .sp 1
+.if t .sp .5
+..
+.TH ATM 8 "1998-08-20" "HARP"
+.SH NAME
+atm \- user configuration and display command for HARP ATM interface
+.SH SYNOPSIS
+Interface management subcommands:
+.in +10
+.ti -5
+.B atm attach
+<interface> <sigmgr>
+.ti -5
+.B atm detach
+<interface>
+.ti -5
+.B atm set MAC
+<interface> <MAC/ESI address>
+.ti -5
+.B atm set netif
+<interface> <prefix> <count>
+.ti -5
+.B atm set prefix
+<interface> <NSAP prefix>
+.ti -5
+.B atm show config
+[<interface>]
+.ti -5
+.B atm show interface
+[<interface>]
+.ti -5
+.B atm show netif
+[<netif>]
+.ti -5
+.B atm show stats interface
+[<interface> [phy | dev | atm | aal0 | aal4 | aal5 | driver]]
+.sp
+.ti -10
+VCC management subcommands:
+.ti -5
+.B atm add PVC
+<interface> <vpi> <vci> <aal> <encaps> <owner> ...
+.ti -5
+.B atm delete PVC
+<interface> <vpi> <vci>
+.ti -5
+.B atm delete SVC
+<interface> <vpi> <vci>
+.ti -5
+.B atm show stats VCC
+[<interface> [<vpi> [<vci>]]]
+.ti -5
+.B atm show VCC
+[<interface> [<vpi> [<vci>] | SVC | PVC]]
+.sp
+.ti -10
+IP management subcommands:
+.ti -5
+.B atm add ARP
+[<netif>] <host> <ATM address>
+.ti -5
+.B atm add PVC
+<interface> <vpi> <vci> <aal> <encaps> IP <netif> [<host> | dynamic] <traffic> <params> ...
+.ti -5
+.B atm delete ARP
+[<netif>] <host>
+.ti -5
+.B atm set arpserver
+<netif> <ATM address> | local [<IP prefix> ...]
+.ti -5
+.B atm show ARP
+[<host>]
+.ti -5
+.B atm show arpserver
+[<netif>]
+.ti -5
+.B atm show IPVCC
+[<host> | <netif>]
+.ti -5
+.sp
+.ti -10
+Miscellaneous subcommands:
+.ti -5
+.B atm help
+.ti -5
+.B atm show version
+.in -10
+.fi
+.SH DESCRIPTION
+.I atm
+configures and displays the status of the Host ATM Research Platform
+(HARP) networking software.
+The subcommands fall into several categories:
+.PP
+\fIInterface management\fP subcommands allow manipulation of the
+ATM interface.
+Functions include assigning a signalling manager to an interface,
+setting the ATM address, associating network interfaces with
+an interface, and displaying information about interfaces.
+.PP
+\fIVCC management\fP subcommands allow for managing ATM virtual
+channel connections (VCCs).
+Functions include opening and closing VCCs and displaying information
+about them.
+.PP
+\fIIP management\fP subcommands allow for managing the interface
+between IP and the ATM software.
+Functions include displaying and manipulating the ATMARP cache,
+opening a PVC connected to IP,
+assigning an ATMARP server to a network interface,
+and displaying information about IP VCCs.
+.PP
+\fIMiscellaneous\fP subcommands allow for displaying the version
+of the ATM software and for getting help with the \fIatm\fP command.
+.SS "Signalling Managers"
+The signalling manager is responsible for the opening and closing of
+VCCs.
+Four signalling managers are supported:
+.PP
+.in +10
+.ti -5
+PVC - for PVCs only,
+.ti -5
+SPANS - supports SPANS, FORE's proprietary signalling protocol,
+.ti -5
+UNI 3.0 - supports the signalling protocol from The ATM Forum's
+\fIATM User-Network Interface Specification, Version 3.0\fP.
+.ti -5
+UNI 3.1 - supports the signalling protocol from The ATM Forum's
+\fIATM User-Network Interface Specification, Version 3.1\fP.
+.in -10
+.PP
+All four signalling managers support the opening and closing of PVCs
+(see the \fIadd\fP and \fIdelete\fP subcommands).
+.PP
+A signalling manager must be attached to a physical interface
+(see the \fIattach\fP subcommand)
+before any VCCs can be created on the interface.
+.SS "Physical and Network Interfaces"
+Two types of interfaces are supported:
+physical interfaces and network interfaces.
+A physical interface represents a physical point of attachment to an
+ATM network.
+A physical interface has an ATM address associated with it, except
+when the PVC-only signalling manager is being used.
+.PP
+A network interface is a logical interface.
+One or more network interfaces are associated with a physical
+interface; each network interface has an IP address associated with it.
+For UNI-controlled interfaces, there can be up to 256 network
+interfaces associated with a physical interface.
+In this case, the correspondence between the network interface and
+the ATM address is determined by the selector field (the last
+byte) of the physical interface's ATM address.
+For PVC-only interfaces, there can be up to 256 logical interfaces
+associated with each physical interface.
+For interfaces controlled by the SPANS signalling manager,
+there must be one and
+only one network interface associated with each physical interface.
+.SS "Keyword and Documentation Conventions"
+Command and subcommand keywords can be abbreviated by simply giving
+enough of the first part of the keyword to make it unique.
+Thus, \fIatm sh v\fB gives the same result as \fIatm show vcc\fB.
+.PP
+All keywords are case-insensitive.
+.PP
+Where a host address needs to be given to the \fIatm\fP command,
+either a DNS name or an IP address in dotted decimal format can
+be used.
+.PP
+ATM addresses are specified as strings of hex digits, with an
+optional leading "0x".
+Fields within the address may be separated by periods, but periods
+are for readability only and are ignored.
+SPANS addresses are 8 bytes long, while NSAP-format addresses
+are 20 bytes long.
+The full address, including any leading zeroes, must be given.
+For example:
+.in +5
+0x47.0005.80.ffe100.0000.f21a.0170.0020481a0170.00 (NSAP format)
+.br
+0x00000010.f2050aa9 (SPANS format)
+.in -5
+.fi
+.SH SUBCOMMANDS
+.SS Interface Management Subcommands:
+.in +5
+.ti -5
+\fIatm add PVC <interface> <vpi> <vci> <aal> <encaps> <owner> ...\fP
+.in -5
+.PP
+the format of the \fIadd PVC\fP subcommand varies depending on the
+owner of the PVC.
+See the description under "IP Management Subcommands."
+.PP
+\fIatm attach <interface> <sigmgr>\fP
+.PP
+where:
+.in +10
+.ti -5
+\fI<interface>\fP specifies the physical interface to which the
+signalling manager is to be attached,
+.ti -5
+\fI<sigmgr>\fP specifies which signalling manager is to be attached.
+Valid choices are "SIGPVC", "SPANS", "UNI30", and "UNI31".
+.in -10
+.PP
+This command attaches a signalling manager to an interface.
+Until this is done, VCCs cannot be opened or closed.
+Only one signalling manager at a time can be attached to an interface.
+.PP
+\fIatm detach <interface>\fP
+.PP
+where:
+.in +10
+.ti -5
+\fI<interface>\fP specifies the physical interface whose signalling
+manager is to be detached.
+.in -10
+.PP
+This command detaches a signalling manager from an interface.
+All VCCs that the signalling manager has created will be closed,
+and no new VCCs can be created until a signalling manager (either
+the same or a different one) is attached again.
+.PP
+\fIatm set MAC <interface> <MAC/ESI address>\fP
+.PP
+where:
+.in +10
+.ti -5
+\fI<interface>\fP specifies the physical interface whose
+MAC address is to be set,
+.ti -5
+\fI<MAC/ESI address>\fP specifies the 6-byte MAC part of the NSAP
+address for the interface.
+The MAC address is specified as a string of 12 hexadecimal
+digits with an optional leading "0x".
+Fields in the address may be separated by periods.
+.in -10
+.PP
+This command sets the MAC address for a UNI-controlled interface.
+The first 13 bytes (the prefix) of the 20-byte NSAP-format address
+are set by the \fIatm set prefix\fP command or the ILMI daemon
+(\fIilmid\fP (8)),
+the next 6 bytes (the End System Identifier (ESI)) are set by
+this command,
+and the last byte (the selector) will be determined by which
+network interface is to be associated with the address.
+.PP
+The \fIatm set MAC\fP command can be used to override the MAC
+address in the interface hardware.
+.PP
+\fIatm set netif <interface> <prefix> <count>\fP
+.PP
+where:
+.in +10
+.ti -5
+\fI<interface>\fP specifies the physical interface that the network
+interface(s) are to be associated with,
+.ti -5
+\fI<prefix>\fP specifies the invariant part of the network
+interface name,
+.ti -5
+\fI<count>\fP specifies the number of network interface to be
+created.
+.in -10
+.PP
+This command creates one or more network interfaces and associates them
+with the specified physical interface.
+The network interface names are determined by the prefix and the count.
+The names will be of the form <prefix><nn>, where <prefix> is the
+prefix specified in the \fIset\fP subcommand and <nn> is a number
+in the range 0 - <count>-1. For example, the command:
+.PP
+.ti +5
+atm set netif hfa0 ni 2
+.PP
+would create two network interfaces, named ni0 and ni1, and associate
+them with physical interface hfa0.
+.PP
+\fIatm set prefix <interface> <NSAP prefix>\fP
+.PP
+where:
+.in +10
+.ti -5
+\fI<interface>\fP specifies the physical interface whose NSAP
+prefix is to be set,
+.ti -5
+\fI<NSAP prefix>\fP specifies the first 13 bytes of the NSAP address
+for the interface.
+The prefix is specified as a string of hexadecimal digits with an
+optional leading "0x".
+Fields in the prefix may be separated by periods.
+.in -10
+.PP
+This command sets the address for a UNI-controlled interface.
+The first 13 bytes (the prefix) of the 20-byte NSAP-format address
+are set by this command,
+the next 6 bytes (the End System Identifier (ESI)) will be the
+MAC address taken from the physical interface or set by the
+\fIset MAC\fP subcommand,
+and the last byte (the selector) will be determined by which
+network interface is to be associated with the address.
+.PP
+The NSAP prefix must be set before a UNI-controlled
+interface can become active.
+This can be accomplished either by the ILMI daemon (\fIilmid\fP (8))
+or the \fIset prefix\fP subcommand.
+.PP
+.I atm show config [<interface>]
+.PP
+displays the following information:
+.PP
+.B Interface
+\- the name of the physical interface.
+.PP
+.B Vendor
+\- the name of the adapter vendor.
+.PP
+.B Model
+\- the model of the adapter.
+.PP
+.B Media
+\- the communications medium used by the adapter.
+.PP
+.B Bus
+\- the type of bus the adapter is attached to.
+.PP
+.B Serial No.
+\- the adapter's serial number.
+.PP
+.B MAC address
+\- the MAC address of the interface.
+Note that this is the MAC address encoded in the hardware of
+the adapter, even if the \fIatm set MAC\fP command has been used
+to change the effective MAC address of the interface.
+.PP
+.B Hardware version
+\- the hardware revision level reported by the interface.
+.PP
+.B Firmware version
+\- the firmware revision level reported by the interface.
+.PP
+If no parameters are specified on the \fIshow config\fP subcommand,
+the configurations of all physical interfaces will be displayed.
+If an interface name is specified, only the configuration of the given
+interface is displayed.
+.PP
+.I atm show interface [<interface>]
+.PP
+displays the following information:
+.PP
+.B Interface
+\- the name of the physical interface.
+.PP
+.B Sigmgr
+\- the name of the signalling manager which has been attached to the
+interface.
+A dash (-) is shown if no signalling manager has been attached.
+.PP
+.B State
+\- the state of the signalling manager for the interface.
+Each signalling manager has its own set of states.
+They are:
+.in +21
+.ti -16
+PVC:
+.ti -11
+ACTIVE\ ---\ The signalling manager is active.
+.ti -11
+DETACH\ ---\ The signalling manager is being detached.
+.ti -16
+SPANS:
+.ti -11
+ACTIVE\ ---\ The signalling manager is active.
+.ti -11
+DETACH\ ---\ The signalling manager is being detached.
+.ti -11
+INIT\ -----\ The signalling manager's initial state.
+.ti -11
+PROBE\ ----\ The signalling manager is attempting to make
+contact with the ATM switch.
+.ti -16
+UNI 3.0 or UNI 3.1:
+.ti -11
+NULL\ -----\ The signalling manager's initial state.
+.ti -11
+ADR_WAIT\ -\ The signalling manager is waiting for the NSAP
+prefix to be set.
+.ti -11
+INIT\ -----\ The signalling manager is attempting to establish
+contact with the switch.
+.ti -11
+ACTIVE\ ---\ The signalling manager is active.
+.ti -11
+DETACH\ ---\ The signalling manager is being detached.
+.ti -21
+.PP
+.B ATM address
+\- the ATM address of the interface.
+.PP
+.B Network interfaces
+\- the names of network interfaces, if any, associated with the
+physical interface.
+.PP
+If no parameters are specified on the \fIshow interface\fP subcommand,
+information about all physical interfaces will be displayed.
+If an interface name is specified, only information about the given
+interface is displayed.
+.PP
+.I atm show netif [<netif>]
+.PP
+displays the following information:
+.PP
+.B Net Intf
+\- the name of the network interface.
+.PP
+.B IP Address
+\- the IP address of the network interface.
+.PP
+If no parameters are specified on the \fIshow netif\fP subcommand,
+information about all network interfaces will be displayed.
+If an interface name is specified, only information about the given
+network interface is displayed.
+.PP
+\fIatm show stats interface [<interface> [phy | dev | atm | aal0 |
+aal4 | aal5 | driver]]\fP
+.PP
+displays statistics associated with one or more interfaces.
+Subject-area keywords
+(\fIphy\fP, \fIdev\fP, \fIatm\fP, \fIaal0\fP,
+\fIaal4\fP, \fIaal5\fP, or \fIdriver\fP)
+can be specified to change the scope of the statistics displayed.
+.PP
+If no subject area keyword is specified, the following information is
+displayed:
+.PP
+.B Interface
+\- the name of the physical ATM interface.
+.PP
+.B Input PDUs
+\- the number of Protocol Data Units (PDUs) which have been received
+by the interface.
+.PP
+.B Input Bytes
+\- the number of bytes which have been received by the interface.
+.PP
+.B Input Errs
+\- the number of input errors which the interface has experienced.
+.PP
+.B Output PDUs
+\- the number of Protocol Data Units (PDUs) which have been transmitted
+by the interface.
+.PP
+.B Output Bytes
+\- the number of bytes which have been transmitted by the interface.
+.PP
+.B Output Errs
+\- the number of output errors which the interface has experienced.
+.PP
+.B Cmd Errs
+\- the number of command errors which the interface has experienced.
+.PP
+If a subject-area keyword is specified, then statistics for
+that subject are displayed.
+The statistics displayed depend on the adapter.
+If requested statistics are not available for an adaptor,
+an error will be noted.
+.PP
+If no parameters are specified on the \fIshow stats interface\fP
+subcommand, statistics for all ATM interfaces are displayed.
+If an interface name is specified, only statistics for the given
+interface are displayed.
+.PP
+.SS VCC Management Subcommands:
+.PP
+\fIatm delete PVC <interface> <vpi> <vci>\fP
+.br
+\fIatm delete SVC <interface> <vpi> <vci>\fP
+.PP
+where:
+.in +10
+.ti -5
+\fIPVC\fP specifies that the VCC to be closed is a PVC,
+.ti -5
+\fISVC\fP specifies that the VCC to be closed is an SVC,
+.ti -5
+\fI<interface>\fP specifies the physical interface at which the
+VCC to be closed terminates,
+.ti -5
+\fI<vpi>\fP specifies the Virtual Path Identifier (VPI) of the VCC,
+.ti -5
+\fI<vci>\fP specifies the Virtual Channel Identifier (VCI) of the VCC.
+.in -10
+.PP
+This command closes a VCC.
+The two forms differ only in that the first specifies that the
+VCC is a PVC (that was created by the \fIadd PVC\fP subcommand) and
+the second specifies that the VCC is an SVC.
+Reserved VCCs (with VCI values less than 32) cannot be closed
+with this command.
+.PP
+\fIatm show stats VCC [<interface> [<vpi> [<vci>]]]\fP
+.PP
+displays the following information:
+.PP
+.B Interface
+\- the physical interface on which the VCC terminates.
+.PP
+.B VPI
+\- the Virtual Path Identifier (VPI) for the VCC.
+.PP
+.B VCI
+\- the Virtual Channel Identifier (VCI) for the VCC.
+.PP
+.B Input PDUs
+\- the number of Protocol Data Units (PDUs) which have been received
+on the VCC.
+.PP
+.B Input Bytes
+\- the number of bytes which have been received on the VCC.
+.PP
+.B Input Errs
+\- the number of input errors which the VCC has experienced.
+.PP
+.B Output PDUs
+\- the number of Protocol Data Units (PDUs) which have been transmitted
+on the VCC.
+.PP
+.B Output Bytes
+\- the number of bytes which have been transmitted on the VCC.
+.PP
+.B Output Errs
+\- the number of output errors which the VCC has experienced.
+.PP
+If no parameters are specified on the \fIshow VCC\fP subcommand, all
+active VCCs are displayed.
+If an interface name is specified, all active VCCs for the given
+interface are displayed.
+If an interface and VPI are specified, all active VCCs for the VPI
+on the given interface are displayed.
+If an interface, VPI, and VCI are specified, only the specified VCC on
+the given interface is displayed (note that this could actually be
+two VCCs, since SPANS considers SVCs to be unidirectional).
+.PP
+\fIatm show VCC [<interface> [<vpi> [<vci>] | SVC | PVC]]\fP
+.PP
+displays the following information:
+.PP
+.B Interface
+\- the physical interface on which the VCC terminates.
+.PP
+.B VPI
+\- the Virtual Path Identifier (VPI) for the VCC.
+.PP
+.B VCI
+\- the Virtual Channel Identifier (VCI) for the VCC.
+.PP
+.B AAL
+\- the ATM Adaptation Layer (AAL) in use on the VCC.
+Possible values are null and AAL 1-5.
+.PP
+.B Type
+\- specifies whether the VCC is an SVC or a PVC.
+.PP
+.B Dir
+\- the direction of information flow on the VCC.
+VCCs can be inbound, outbound, or both.
+.PP
+.B State
+\- the state of the VCC, as reported by the signalling manager.
+Each signalling manager has its own set of states.
+They are:
+.in +21
+.ti -16
+PVC:
+.ti -11
+NULL\ -----\ No state.
+.ti -11
+ACTIVE\ ---\ The VCC is active.
+.ti -11
+FREE\ -----\ The VCC is closed and the signalling manager is waiting for
+its resources to be freed.
+.ti -16
+SPANS:
+.ti -11
+NULL\ -----\ No state.
+.ti -11
+ACTIVE\ ---\ The VCC is a PVC and is active.
+.ti -11
+ACT_DOWN\ -\ The VCC is a PVC and the interface is down.
+.ti -11
+POPEN\ ----\ The VCC is being opened.
+.ti -11
+R_POPEN\ --\ The VCC is being opened by a remote host.
+.ti -11
+OPEN\ -----\ The VCC is active.
+.ti -11
+CLOSE\ ----\ The VCC is being closed.
+.ti -11
+ABORT\ ----\ The VCC is being aborted.
+.ti -11
+FREE\ -----\ The VCC is closed and the signalling manager is waiting for
+its resources to be freed.
+.ti -16
+UNI 3.0 or UNI 3.1:
+.ti -11
+NULL\ -----\ No state.
+.ti -11
+C_INIT\ ---\ A VCC is being initiated.
+.ti -11
+C_OUT_PR\ -\ An outgoing VCC request is proceeding.
+.ti -11
+C_PRES\ ---\ A VCC is being initiated by the network.
+.ti -11
+CONN_REQ\ -\ A VCC request has been accepted by a HARP user.
+.ti -11
+C_IN_PR\ --\ An incoming VCC request is proceeding.
+.ti -11
+ACTIVE\ ---\ The VCC is active.
+.ti -11
+REL_REQ\ --\ The VCC is being closed.
+.ti -11
+REL_IND\ --\ The network is clearing a VCC.
+.ti -11
+SSCF_REC\ -\ The SSCF session on the signalling channel is in
+recovery from an error.
+.ti -11
+FREE\ -----\ The VCC is closed and the signalling manager is waiting
+for its resources to be freed.
+.ti -11
+ACT_DOWN\ -\ The VCC is a PVC and the interface is down.
+.ti -21
+.PP
+.B Encaps
+\- the encapsulation in effect on the VCC.
+Possible encapsulations are null and LLC/SNAP.
+.PP
+.B Owner
+\- the owner or owners of the VCC.
+Shows the name(s) of the function(s) using the VCC.
+.PP
+.B Destination
+\- the ATM address of the host at the remote end of the VCC.
+.PP
+If no parameters are specified on the \fIshow VCC\fP subcommand, all
+active VCCs are displayed.
+If an interface name is specified, all active VCCs for the given
+interface are displayed.
+If an interface and VPI are specified, all active VCCs for the VPI
+on the given interface are displayed.
+If an interface, VPI, and VCI are specified, only the specified VCC on
+the given interface is displayed (note that this could actually be
+two VCCs, since SPANS considers SVCs to be unidirectional).
+.PP
+.SS IP Management Subcommands:
+\fIatm add ARP [<netif>] <host> <ATM address>\fP
+.PP
+where:
+.in +10
+.ti -5
+\fI<netif>\fP is the optional name of the network interface the
+ATMARP entry is to be associated with.
+If no name is specified, a network interface is chosen depending
+on the IP address of the host being added.
+.ti -5
+\fI<host>\fP is the host name or IP address of the host to
+be added to the ATMARP table,
+.ti -5
+\fI<ATM address>\fP is the ATM address of the host.
+.in -10
+.PP
+This command adds an entry to the ATMARP table for ATM.
+The given host's IP address is associated with the given ATM address.
+When IP needs to transmit data to the host, the specified ATM
+address will be used to open an SVC.
+.PP
+The entry will be marked as permanent in the ATMARP table and will not
+be subject to aging.
+.PP
+.in +5
+.ti -5
+\fIatm add PVC <interface> <vpi> <vci> <aal> <encaps> IP <netif> [<host> | dynamic] <traffic> <params...>\fP
+.in -5
+.PP
+where:
+.in +10
+.ti -5
+\fI<interface>\fP specifies the physical interface where the PVC
+is to terminate,
+.ti -5
+\fI<vpi>\fP specifies the Virtual Path Identifier (VPI) of the PVC,
+.ti -5
+\fI<vci>\fP specifies the Virtual Channel Identifier (VCI) of the PVC,
+.ti -5
+\fI<aal>\fP specifies the ATM Adaptation Layer (AAL) for the PVC.
+Valid choices are "null" or "AAL0" for the null AAL; "AAL1" for
+AAL 1; "AAL2" for AAL 2; "AAL3", "AAL4", or "AAL3/4" for AAL 3/4;
+and "AAL5" for AAL 5,
+.ti -5
+\fI<encaps>\fP specifies the encapsulation for the PVC.
+Valid choices are "null" or "none" for null encapsulation, and
+"LLC/SNAP", "LLC", or "SNAP" for LLC/SNAP encapsulation,
+.ti -5
+\fIIP\fP specifies that the owner of the PVC is IP.
+.ti -5
+\fI<netif>\fP specifies the network interface which the PVC is
+to be associated with.
+The network interface must exist and be associated with the
+specified physical interface,
+.ti -5
+\fI<host> | dynamic\fP gives the address of the host at
+the far end of the PVC, or the word "dynamic" if its address
+is to be determined with Inverse ARP.
+If "dynamic" is specified, LLC/SNAP encapsulation must also
+be specified.
+.ti -5
+\fI<traffic>\fP is the traffic type of the PVC and may be one of
+UBR, CBR or VBR.
+Following the traffic type the traffic parameters must be given.
+For UBR and CBR this is the peak cell rate and for VBR these
+are the peak and sustainable cell rate and the maximum burst size.
+.PP
+This command creates a PVC with the specified attributes and attaches
+it to IP.
+.PP
+\fIatm delete ARP [<netif>] <host>\fP
+.PP
+where:
+.in +10
+.ti -5
+\fI<netif>\fP is the optional name of the network interface the
+ATMARP entry is associated with.
+If no name is specified, the specified host is deleted from the
+cache regardless of what network interface it is associated with.
+.ti -5
+\fI<host>\fP is the host name or IP address of the host to
+be deleted from the ATMARP table.
+.PP
+This command deletes the specified host's entry from the ATMARP table.
+.PP
+\fIatm set arpserver <netif> <ATM address> | local [<IP prefix> ...]\fP
+.PP
+where:
+.in +10
+.ti -5
+\fI<netif>\fP specifies the network interface for which the
+ATMARP server address is to be set.
+.ti -5
+\fI<ATM address>\fP specifies the ATM address of the host which is to
+provide ATMARP service.
+If "local" is specified instead of an ATM address, the host on
+which the command is issued will become the ATMARP server.
+.ti -5
+\fI<IP prefix> ...\fP is an optional list of IP prefixes
+that the ATMARP server will provide information about.
+An IP prefix is specified as a dotted decimal IP address, followed by
+a slash, followed a number specifying how many bits of the IP address
+are significant.
+For example, 10.0.0.0/8 indicates that the ATMARP server will provide
+services for all addresses on IP network 10.
+The IP subnetwork which the network interface belongs to is
+automatically included.
+.in -10
+.PP
+This command sets the address of the ATMARP server for a network
+interface.
+.PP
+.I atm show ARP [<host>]
+.PP
+displays the following information:
+.PP
+.B Net Intf
+\- the network interface which traffic for the entry will use.
+.PP
+.B Flags
+\- flags showing whether the entry is valid and whether it is
+permanent.
+\- flags giving further information about the ATMARP entry.
+The meanings of the characters in the flags are:
+.PP
+.in +5
+P - the entry is permanent
+.br
+R - the entry has been refreshed
+.br
+V - the entry is valid
+.in -5
+.PP
+.B Age
+\- the number of minutes for which the entry will remain valid.
+.PP
+.B Origin
+\- the source of the ATMARP entry.
+Possible values are:
+.in +16
+.ti -11
+LOCAL\ ----\ The entry is for an interface on the host.
+.ti -11
+PERM\ -----\ The entry is permanent.
+This is used for entries that are created with the
+\fIadd ARP\fP command.
+.ti -11
+REG\ ------\ The entry was created as the result of a host
+registering with the ATMARP server.
+.ti -11
+SCSP\ -----\ The entry was learned via SCSP.
+.ti -11
+LOOKUP\ ---\ The entry was created as the result of a host
+performing an ATMARP lookup.
+.ti -11
+PEER_RSP\ -\ The entry was created as the result of a host
+answering an InARP Request.
+.ti -11
+PEER_REQ\ -\ The entry was created as the result of a host
+sending an InARP Request.
+.in -5
+.PP
+.B ATM address
+\- the ATM address of the host the entry refers to.
+.PP
+.B IP address
+\- the IP address or domain name of the host the entry refers to.
+.PP
+If no parameters are specified on the \fIshow ARP\fP subcommand,
+the whole ATMARP table will be displayed.
+If a host name or IP address is specified, only information about the
+given host is displayed.
+.PP
+This command displays both information that has been learned dynamically
+(through one form or another of ATMARP and via SCSP) and information
+which has been configured by the user (through the \fIadd ARP\fP
+subcommand).
+.PP
+.I atm show arpserver [<netif>]
+.PP
+displays the following information:
+.PP
+.B Net Intf
+\- the network interface for which information is being displayed.
+.PP
+.B State
+\- the state of the connection to the ATMARP server.
+Possible values are:
+.in +16
+.ti -11
+NOT_CONF\ -\ No ATMARP server has been configured for the interface.
+.ti -11
+SERVER\ ---\ The host is the ATMARP server.
+.ti -11
+PEND_ADR\ -\ No ATM address has been set for the interface.
+.ti -11
+POPEN\ ----\ The host is attempting to open a VCC to the ATMARP server.
+.ti -11
+REGISTER\ -\ The host has a VCC open to the ATMARP server and is in
+the process of registering with the server.
+.ti -11
+ACTIVE\ ---\ The ATMARP server connection is active.
+.in -16
+.PP
+.B ATM Address
+\- the ATM address of the ATMARP server.
+.PP
+If no parameters are specified on the \fIshow arpserver\fP subcommand,
+the ATMARP servers for all network interfaces will be displayed.
+If an interface name is specified, only information about the given
+network interface is displayed.
+.PP
+.I atm show IPVCC [<host> | <netif>]
+.PP
+displays the following information:
+.PP
+.B Net Intf
+\- the name of the network interface at which the VCC terminates.
+.PP
+.B VPI
+\- the Virtual Path Identifier (VPI) for the VCC.
+.PP
+.B VCI
+\- the Virtual Channel Identifier (VCI) for the VCC.
+.PP
+.B State
+\- the state of the VCC.
+Possible values are:
+.in +15
+.ti -10
+PMAP\ ----\ The host has an IP packet to send and is waiting for
+an ATMARP mapping.
+.ti -10
+POPEN\ ---\ The VCC is being opened.
+.ti -10
+PACCEPT\ -\ A VCC from a remote host is being accepted.
+.ti -10
+ACTPENT\ -\ A PVC is open, but no ATMARP information is
+available for it yet.
+.ti -10
+ACTIVE\ --\ The VCC is active.
+.in -15
+.PP
+.B Flags
+\- flags giving further information about the VCC.
+The meanings of the characters in the flags are:
+.PP
+.in +5
+S - the VCC is an SVC
+.br
+P - the VCC is a PVC
+.br
+L - the VCC uses LLC/SNAP encapsulation
+.br
+M - the IP-to-ATM address mapping for the VCC is valid
+.br
+N - there is no idle timeout for the VCC
+.in -5
+.PP
+.B IP Address
+\- the name and IP address of the host at the remote end of the VCC.
+.PP
+If no parameters are specified on the \fIshow IPVCC\fP subcommand, all
+active VCCs are displayed.
+If a host name is specified, the active VCC(s) for the given
+host are displayed.
+If a network interface name is specified, the active VCC(s) for the
+given network interface are displayed.
+.PP
+.SS Miscellaneous Subcommands:
+.I atm help
+.PP
+displays a synopsis of the atm command with its subcommands
+and their parameters.
+.PP
+.I atm show version
+displays the version of the running HARP software.
+.fi
+.SH "SEE ALSO"
+\fIilmid\fP (8); \fIscspd\fP (8); \fIatmarpd\fP (8).
+.fi
+.SH BUGS
+Care must be taken to avoid confusing physical interfaces and
+network interfaces.
+.PP
+Please report any bugs to harp-bugs@magic.net.
+.fi
+.SH COPYRIGHT
+Copyright (c) 1994-1998, Network Computing Services, Inc.
+.fi
+.SH AUTHORS
+John Cavanaugh, Network Computing Services, Inc.
+.br
+Mike Spengler, Network Computing Services, Inc.
+.br
+Joe Thomas, Network Computing Services, Inc.
+.fi
+.SH ACKNOWLEDGMENTS
+This software was developed with the support of the Defense
+Advanced Research Projects Agency (DARPA).
diff --git a/sbin/atm/atm/atm.c b/sbin/atm/atm/atm.c
new file mode 100644
index 0000000..94cde38
--- /dev/null
+++ b/sbin/atm/atm/atm.c
@@ -0,0 +1,1143 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ */
+
+/*
+ * User configuration and display program
+ * --------------------------------------
+ *
+ * Main routine
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_cm.h>
+#include <netatm/atm_sigmgr.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+
+#include "atm.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Usage string
+ */
+#define USAGE_STR "Interface management subcommands:\n\
+ attach <intf> <protocol>\n\
+ detach <intf>\n\
+ set mac <intf> <MAC/ESI address>\n\
+ set netif <intf> <prefix> <n>\n\
+ set prefix <intf> <NSAP prefix>\n\
+ show config [<intf>]\n\
+ show interface [<intf>]\n\
+ show netif [<netif>]\n\
+ show stats interface [<intf> phy | dev | atm | aal0 | aal4 |\n\
+ aal5 | driver]\n\
+\n\
+VC management subcommands:\n\
+ add pvc <intf> <vpi> <vci> <aal> <encaps> <owner> ...\n\
+ [UBR <pcr> | CBR <pcr> | VBR <pcr> <scr> <mbs>]\n\
+ delete pvc <intf> <vpi> <vci>\n\
+ delete svc <intf> <vpi> <vci>\n\
+ show stats vcc [<intf> [vpi [vci]]]\n\
+ show vcc [<intf> [<vpi> [<vci>] | SVC | PVC]]\n\
+\n\
+IP management subcommands:\n\
+ add arp [<netif>] <IP addr> <ATM addr>\n\
+ add pvc <intf> <vpi> <vci> <aal> <encaps> IP <netif> <IP addr> |\n\
+ dynamic\n\
+ delete arp [<netif>] <IP addr>\n\
+ set arpserver <netif> <server> <IP prefix> ...\n\
+ show arp [<host>]\n\
+ show arpserver [<netif>]\n\
+ show ipvcc [<IP addr> | <netif>]\n\
+\n\
+Miscellaneous subcommands:\n\
+ help\n\
+ show version\n"
+
+
+/*
+ * Local definitions
+ */
+static int do_cmd(const struct cmd *, int, char **);
+static void usage(const struct cmd *, const char *);
+
+static void attach(int, char **, const struct cmd *);
+static void detach(int, char **, const struct cmd *);
+static void help(int, char **, const struct cmd *);
+static void arp_add(int, char **, const struct cmd *);
+static void pvc_add(int, char **, const struct cmd *);
+static void pvc_dlt(int, char **, const struct cmd *);
+static void svc_dlt(int, char **, const struct cmd *);
+static void arp_dlt(int, char **, const struct cmd *);
+static void vcc_dlt(int, char **, const struct cmd *, struct atmdelreq *);
+
+static const struct cmd add_subcmd[];
+static const struct cmd dlt_subcmd[];
+static const struct cmd set_subcmd[];
+static const struct cmd show_subcmd[];
+static const struct cmd stats_subcmd[];
+
+static const struct cmd cmds[] = {
+ { "add", 0, 0, NULL, (const char *)add_subcmd },
+ { "attach", 2, 2, attach, "<intf> <protocol>" },
+ { "delete", 0, 0, NULL, (const char *)dlt_subcmd },
+ { "detach", 1, 1, detach, "<intf>" },
+ { "set", 0, 0, NULL, (const char *)set_subcmd },
+ { "show", 0, 0, NULL, (const char *)show_subcmd },
+ { "help", 0, 99, help, "" },
+ { 0, 0, 0, NULL, "" }
+};
+
+static const struct cmd add_subcmd[] = {
+ { "arp", 2, 3, arp_add, "[<netif>] <IP addr> <ATM addr>" },
+ { "pvc", 6, 16, pvc_add, "<intf> <vpi> <vci> <aal> <encaps> <owner> <netif> ... [UBR | CBR | VBR]" },
+ { 0, 0, 0, NULL, "" }
+};
+
+static const struct cmd dlt_subcmd[] = {
+ { "arp", 1, 2, arp_dlt, "[<netif>] <IP addr>" },
+ { "pvc", 3, 3, pvc_dlt, "<intf> <vpi> <vci>" },
+ { "svc", 3, 3, svc_dlt, "<intf> <vpi> <vci>" },
+ { 0, 0, 0, NULL, "" }
+};
+
+static const struct cmd set_subcmd[] = {
+ { "arpserver", 2, 18, set_arpserver, "<netif> <server>" },
+ { "mac", 2, 2, set_macaddr, "<intf> <MAC/ESI address>" },
+ { "netif", 3, 3, set_netif, "<intf> <prefix> <n>" },
+ { "prefix", 2, 2, set_prefix, "<intf> <NSAP prefix>" },
+ { 0, 0, 0, NULL, ""}
+};
+
+static const struct cmd show_subcmd[] = {
+ { "arp", 0, 1, show_arp, "[<host>]" },
+ { "arpserver", 0, 1, show_arpserv, "[<netif>]" },
+ { "config", 0, 1, show_config, "[<intf>]" },
+ { "interface", 0, 1, show_intf, "[<intf>]" },
+ { "ipvcc", 0, 3, show_ip_vcc, "[<IP addr> | <netif>]" },
+ { "netif", 0, 1, show_netif, "[<netif>]" },
+ { "stats", 0, 3, NULL, (const char *)stats_subcmd },
+ { "vcc", 0, 3, show_vcc, "[<intf>] [<vpi> [<vci>] | SVC | PVC]" },
+ { "version", 0, 0, show_version, "" },
+ { 0, 0, 0, NULL, "" }
+};
+
+static const struct cmd stats_subcmd[] = {
+ { "interface", 0, 2, show_intf_stats, "[<intf> [cfg | phy | dev | atm | aal0 | aal4 | aal5 | driver]]" },
+ { "vcc", 0, 3, show_vcc_stats, "[<intf> [vpi [vci]]]" },
+ { 0, 0, 0, NULL, "" }
+};
+
+
+/*
+ * Supported signalling protocols
+ */
+static const struct proto protos[] = {
+ { "SIGPVC", ATM_SIG_PVC },
+ { "SPANS", ATM_SIG_SPANS },
+ { "UNI30", ATM_SIG_UNI30 },
+ { "UNI31", ATM_SIG_UNI31 },
+ { "UNI40", ATM_SIG_UNI40 },
+ { 0, 0 }
+};
+
+/*
+ * Supported VCC owners
+ */
+static const struct owner owners[] = {
+ { "IP", ENDPT_IP, ip_pvcadd },
+ { "SPANS", ENDPT_SPANS_SIG,0 },
+ { "SPANS CLS", ENDPT_SPANS_CLS,0 },
+ { "UNI SIG", ENDPT_UNI_SIG, 0 },
+ { 0, 0, 0 }
+};
+
+/*
+ * Supported AAL parameters
+ */
+const struct aal aals[] = {
+ { "Null", ATM_AAL0 },
+ { "AAL0", ATM_AAL0 },
+ { "AAL1", ATM_AAL1 },
+ { "AAL2", ATM_AAL2 },
+ { "AAL4", ATM_AAL3_4 },
+ { "AAL3", ATM_AAL3_4 },
+ { "AAL3/4", ATM_AAL3_4 },
+ { "AAL5", ATM_AAL5 },
+ { 0, 0 },
+};
+
+/*
+ * Supported VCC encapsulations
+ */
+const struct encaps encaps[] = {
+ { "Null", ATM_ENC_NULL },
+ { "None", ATM_ENC_NULL },
+ { "LLC/SNAP", ATM_ENC_LLC },
+ { "LLC", ATM_ENC_LLC },
+ { "SNAP", ATM_ENC_LLC },
+ { 0, 0 },
+};
+
+/*
+ * Supported ATM traffic types
+ */
+struct traffics traffics[] = {
+ { "UBR", T_ATM_UBR, 1, "UBR <pcr>" },
+ { "CBR", T_ATM_CBR, 1, "CBR <pcr>" },
+ { "VBR", T_ATM_VBR, 3, "VBR <pcr> <scr> <mbs>" },
+#ifdef notyet
+ { "ABR", T_ATM_ABR, 2, "ABR <arg1> <arg2>" },
+#endif
+ { NULL, 0, 0, NULL }
+};
+
+char *prog;
+char prefix[128] = "";
+
+int
+main(int argc, char *argv[])
+{
+ int error;
+
+ /*
+ * Save program name, ignoring any path components
+ */
+ if ((prog = (char *)strrchr(argv[0], '/')) != NULL)
+ prog++;
+ else
+ prog = argv[0];
+
+ if (argc < 2) {
+ usage(cmds, "");
+ exit(1);
+ }
+ argc--; argv++;
+
+ /*
+ * Validate and process command
+ */
+ if ((error = do_cmd(cmds, argc, argv)) != 0)
+ usage(cmds, "");
+
+ exit(error);
+}
+
+
+/*
+ * Validate and process user command
+ *
+ * Arguments:
+ * descp pointer to command description array
+ * argc number of arguments left in command
+ * argv pointer to argument strings
+ *
+ * Returns:
+ * none
+ *
+ */
+static int
+do_cmd(const struct cmd *descp, int argc, char **argv)
+{
+ const struct cmd *cmdp = NULL;
+
+ /*
+ * Make sure we have paramaters to process
+ */
+ if (!argc) {
+ usage(cmds, "");
+ exit(1);
+ }
+
+ /*
+ * Figure out what command user wants
+ */
+ for (; descp->name; descp++) {
+ /*
+ * Use an exact match if there is one
+ */
+ if (!strcasecmp(descp->name, argv[0])) {
+ cmdp = descp;
+ break;
+ }
+ /*
+ * Look for a match on the first part of keyword
+ */
+ if (!strncasecmp(descp->name, argv[0], strlen(argv[0]))) {
+ if (cmdp) {
+ fprintf(stderr, "%s: Ambiguous parameter \"%s\"\n",
+ prog, argv[0]);
+ exit(1);
+ }
+ cmdp = descp;
+ }
+ }
+ if (!cmdp)
+ return(1);
+ argc--; argv++;
+
+ /*
+ * See if this command has subcommands
+ */
+ if (cmdp->func == NULL) {
+ strcat(prefix, cmdp->name);
+ strcat(prefix, " ");
+ return (do_cmd((const struct cmd *)(const void *)cmdp->help,
+ argc, argv));
+ }
+
+ /*
+ * Minimal validation
+ */
+ if ((argc < cmdp->minp) || (argc > cmdp->maxp)) {
+ fprintf(stderr, "%s: Invalid number of arguments\n",
+ prog);
+ fprintf(stderr, "\tformat is: %s%s %s\n",
+ prefix, cmdp->name, cmdp->help);
+ exit(1);
+ }
+
+ /*
+ * Process command
+ */
+ (*cmdp->func)(argc, argv, cmdp);
+ return(0);
+}
+
+
+/*
+ * Print command usage information
+ *
+ * Arguments:
+ * cmdp pointer to command description
+ * pref pointer current command prefix
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+usage(const struct cmd *cmdp __unused, const char *pref __unused)
+{
+ fprintf(stderr, "usage: %s command [arg] [arg]...\n", prog);
+ fprintf(stderr, USAGE_STR);
+}
+
+
+/*
+ * Process interface attach command
+ *
+ * Command format:
+ * atm attach <interface_name> <protocol_name>
+ *
+ * Arguments:
+ * argc number of arguments to command
+ * argv pointer to argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+attach(int argc __unused, char **argv, const struct cmd *cmdp __unused)
+{
+ struct atmcfgreq aar;
+ const struct proto *prp;
+ int s;
+
+ /*
+ * Validate interface name
+ */
+ if (strlen(argv[0]) > sizeof(aar.acr_att_intf) - 1) {
+ fprintf(stderr, "%s: Illegal interface name\n", prog);
+ exit(1);
+ }
+
+ /*
+ * Find/validate requested signalling protocol
+ */
+ for (prp = protos; prp->p_name; prp++) {
+ if (strcasecmp(prp->p_name, argv[1]) == 0)
+ break;
+ }
+ if (prp->p_name == NULL) {
+ fprintf(stderr, "%s: Unknown signalling protocol\n", prog);
+ exit(1);
+ }
+
+
+ /*
+ * Build ioctl request
+ */
+ aar.acr_opcode = AIOCS_CFG_ATT;
+ strncpy(aar.acr_att_intf, argv[0], sizeof(aar.acr_att_intf));
+ aar.acr_att_proto = prp->p_id;
+
+ /*
+ * Tell the kernel to do the attach
+ */
+ s = socket(AF_ATM, SOCK_DGRAM, 0);
+ if (s < 0) {
+ sock_error(errno);
+ }
+ if (ioctl(s, AIOCCFG, (caddr_t)&aar) < 0) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case EINVAL:
+ case EOPNOTSUPP:
+ case EPROTONOSUPPORT:
+ perror("Internal error");
+ break;
+ case ENOMEM:
+ fprintf(stderr, "Kernel memory exhausted\n");
+ break;
+ case EEXIST:
+ fprintf(stderr, "Signalling manager already attached to %s\n",
+ argv[0]);
+ break;
+ case ENETDOWN:
+ fprintf(stderr, "ATM network is inoperable\n");
+ break;
+ case EPERM:
+ fprintf(stderr, "Must be super user to use attach subcommand\n");
+ break;
+ case ENXIO:
+ fprintf(stderr, "%s is not an ATM device\n",
+ argv[0]);
+ break;
+ case ETOOMANYREFS:
+ fprintf(stderr, "%s has too few or too many network interfaces\n",
+ argv[0]);
+ break;
+ default:
+ perror("Ioctl (AIOCCFG) attach");
+ break;
+ }
+ exit(1);
+ }
+ (void)close(s);
+}
+
+
+/*
+ * Process interface detach command
+ *
+ * Command format:
+ * atm detach <interface_name>
+ *
+ * Arguments:
+ * argc number of arguments to command
+ * argv pointer to argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+detach(int argc __unused, char **argv, const struct cmd *cmdp __unused)
+{
+ struct atmcfgreq adr;
+ int s;
+
+ /*
+ * Validate interface name
+ */
+ if (strlen(argv[0]) > sizeof(adr.acr_det_intf) - 1) {
+ fprintf(stderr, "%s: Illegal interface name\n", prog);
+ exit(1);
+ }
+
+ /*
+ * Build ioctl request
+ */
+ adr.acr_opcode = AIOCS_CFG_DET;
+ strncpy(adr.acr_det_intf, argv[0], sizeof(adr.acr_det_intf));
+
+ /*
+ * Tell the kernel to do the detach
+ */
+ s = socket(AF_ATM, SOCK_DGRAM, 0);
+ if (s < 0) {
+ sock_error(errno);
+ }
+ if (ioctl(s, AIOCCFG, (caddr_t)&adr) < 0) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case EALREADY:
+ fprintf(stderr, "Signalling manager already detaching from %s\n",
+ argv[0]);
+ break;
+ case EINVAL:
+ perror("Internal error");
+ break;
+ case EPERM:
+ fprintf(stderr, "Must be super user to use detach subcommand\n");
+ break;
+ default:
+ perror("ioctl (AIOCCFG) detach");
+ break;
+ }
+ exit(1);
+ }
+ (void)close(s);
+}
+
+
+/*
+ * Process PVC add command
+ *
+ * Command format:
+ * atm add PVC <interface_name> <vpi> <vci> <aal> <encaps>
+ * <owner_name> ...owner info...
+ * [ubr <PCR> | cbr <PCR> | vbr <PCR> <SCR> <MBS>]
+ *
+ * Arguments:
+ * argc number of arguments to command
+ * argv pointer to argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+pvc_add(int argc, char **argv, const struct cmd *cmdp)
+{
+ struct atmaddreq apr;
+ struct atminfreq air;
+ struct air_int_rsp *int_info;
+ const struct owner *owp;
+ const struct aal *alp;
+ const struct encaps *enp;
+ const struct traffics *trafp;
+ char *cp;
+ u_long v;
+ ssize_t buf_len;
+ int s;
+
+ /*
+ * Initialize opcode and flags
+ */
+ apr.aar_opcode = AIOCS_ADD_PVC;
+ apr.aar_pvc_flags = 0;
+
+ /*
+ * Validate interface name and issue an information
+ * request IOCTL for the interface
+ */
+ if (strlen(argv[0]) > sizeof(apr.aar_pvc_intf) - 1) {
+ fprintf(stderr, "%s: Illegal interface name\n", prog);
+ exit(1);
+ }
+ bzero(air.air_int_intf, sizeof(air.air_int_intf));
+ strcpy(air.air_int_intf, argv[0]);
+ air.air_opcode = AIOCS_INF_INT;
+ buf_len = do_info_ioctl(&air, sizeof(struct air_int_rsp));
+ if (buf_len == -1) {
+ switch (errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ err(1, "Internal error");
+ case ENXIO:
+ errx(1, "%s is not an ATM device", argv[0]);
+ default:
+ err(1, "ioctl (AIOCINFO)");
+ }
+ }
+ int_info = (struct air_int_rsp *)(void *)air.air_buf_addr;
+ strcpy(apr.aar_pvc_intf, argv[0]);
+ argc--;
+ argv++;
+
+ /*
+ * Validate vpi/vci values
+ */
+ errno = 0;
+ v = strtoul(argv[0], &cp, 0);
+ if (errno != 0 || *cp != '\0' || v >= 1 << 8)
+ errx(1, "Invalid VPI value '%s'", argv[0]);
+ apr.aar_pvc_vpi = (u_short)v;
+ argc--;
+ argv++;
+
+ errno = 0;
+ v = strtoul(argv[0], &cp, 0);
+ if (errno != 0 || *cp != '\0' || v < MIN_VCI || v >= 1 << 16)
+ errx(1, "Invalid VCI value '%s'", argv[0]);
+ apr.aar_pvc_vci = (u_short)v;
+ argc--;
+ argv++;
+
+ /*
+ * Validate requested PVC AAL
+ */
+ for (alp = aals; alp->a_name; alp++) {
+ if (strcasecmp(alp->a_name, argv[0]) == 0)
+ break;
+ }
+ if (alp->a_name == NULL)
+ errx(1, "Invalid PVC AAL '%s'", argv[0]);
+ apr.aar_pvc_aal = alp->a_id;
+ argc--;
+ argv++;
+
+ /*
+ * Validate requested PVC encapsulation
+ */
+ for (enp = encaps; enp->e_name; enp++) {
+ if (strcasecmp(enp->e_name, argv[0]) == 0)
+ break;
+ }
+ if (enp->e_name == NULL)
+ errx(1, "Invalid PVC encapsulation '%s'", argv[0]);
+ apr.aar_pvc_encaps = enp->e_id;
+ argc--;
+ argv++;
+
+ /*
+ * Validate requested PVC owner
+ */
+ for (owp = owners; owp->o_name; owp++) {
+ if (strcasecmp(owp->o_name, argv[0]) == 0)
+ break;
+ }
+ if (owp->o_name == NULL)
+ errx(1, "Unknown PVC owner '%s'", argv[0]);
+ apr.aar_pvc_sap = owp->o_sap;
+ if (owp->o_pvcadd == NULL)
+ errx(1, "Unsupported PVC owner '%s'", argv[0]);
+ argc--;
+ argv++;
+
+ /*
+ * Perform service user processing
+ */
+ (*owp->o_pvcadd)(argc, argv, cmdp, &apr, int_info);
+
+ argc -= 2;
+ argv += 2;
+
+ if (argc > 0) {
+ /*
+ * Validate requested traffic
+ */
+ for (trafp = traffics; trafp->t_name; trafp++) {
+ if (strcasecmp(trafp->t_name, argv[0]) == 0)
+ break;
+ }
+ if (trafp->t_name == NULL)
+ errx(1, "Unknown traffic type '%s'", argv[0]);
+ apr.aar_pvc_traffic_type = trafp->t_type;
+ argc--;
+ argv++;
+
+ if (trafp->t_argc != argc)
+ errx(1, "Invalid traffic parameters\n\t %s",
+ trafp->help);
+ switch (trafp->t_type) {
+
+ case T_ATM_UBR:
+ case T_ATM_CBR:
+ errno = 0;
+ v = strtoul(argv[0], &cp, 0);
+ if (errno != 0 || *cp != '\0' || v >= 1 << 24)
+ errx(1, "Invalid PCR value '%s'", argv[0]);
+ apr.aar_pvc_traffic.forward.PCR_high_priority = (int32_t) v;
+ apr.aar_pvc_traffic.forward.PCR_all_traffic = (int32_t) v;
+ apr.aar_pvc_traffic.backward.PCR_high_priority = (int32_t) v;
+ apr.aar_pvc_traffic.backward.PCR_all_traffic = (int32_t) v;
+ argc--;
+ argv++;
+ apr.aar_pvc_traffic.forward.SCR_high_priority = T_ATM_ABSENT;
+ apr.aar_pvc_traffic.forward.SCR_all_traffic = T_ATM_ABSENT;
+ apr.aar_pvc_traffic.backward.SCR_high_priority = T_ATM_ABSENT;
+ apr.aar_pvc_traffic.backward.SCR_all_traffic = T_ATM_ABSENT;
+ apr.aar_pvc_traffic.forward.MBS_high_priority = T_ATM_ABSENT;
+ apr.aar_pvc_traffic.forward.MBS_all_traffic = T_ATM_ABSENT;
+ apr.aar_pvc_traffic.backward.MBS_high_priority = T_ATM_ABSENT;
+ apr.aar_pvc_traffic.backward.MBS_all_traffic = T_ATM_ABSENT;
+ break;
+
+ case T_ATM_VBR: /* VBR pcr scr mbs */
+ errno = 0;
+ v = strtoul(argv[0], &cp, 0);
+ if (errno != 0 || *cp != '\0' || v >= 1 << 24)
+ errx(1, "Invalid PCR value '%s'", argv[0]);
+ apr.aar_pvc_traffic.forward.PCR_high_priority = (int32_t)v;
+ apr.aar_pvc_traffic.forward.PCR_all_traffic = (int32_t)v;
+ apr.aar_pvc_traffic.backward.PCR_high_priority = (int32_t)v;
+ apr.aar_pvc_traffic.backward.PCR_all_traffic = (int32_t)v;
+ argc--;
+ argv++;
+
+ errno = 0;
+ v = strtoul(argv[0], &cp, 0);
+ if (errno != 0 || *cp != '\0' || v >= 1 << 24)
+ errx(1, "Invalid SCR value '%s'", argv[0]);
+ apr.aar_pvc_traffic.forward.SCR_high_priority = (int32_t)v;
+ apr.aar_pvc_traffic.forward.SCR_all_traffic = (int32_t)v;
+ apr.aar_pvc_traffic.backward.SCR_high_priority = (int32_t)v;
+ apr.aar_pvc_traffic.backward.SCR_all_traffic = (int32_t)v;
+ argc--;
+ argv++;
+
+ errno = 0;
+ v = strtol(argv[0], &cp, 0);
+ if (errno != 0 || *cp != '\0' || v >= 1 << 24)
+ errx(1, "Invalid MBS value '%s'", argv[0]);
+ apr.aar_pvc_traffic.forward.MBS_high_priority = (int32_t)v;
+ apr.aar_pvc_traffic.forward.MBS_all_traffic = (int32_t)v;
+ apr.aar_pvc_traffic.backward.MBS_high_priority = (int32_t)v;
+ apr.aar_pvc_traffic.backward.MBS_all_traffic = (int32_t)v;
+ argc--;
+ argv++;
+
+ break;
+
+ case T_ATM_ABR:
+ errx(1, "ABR not yet supported");
+
+ default:
+ errx(1, "Unsupported traffic type '%d'", trafp->t_type);
+ }
+ } else {
+ /*
+ * No PVC traffic type
+ */
+ apr.aar_pvc_traffic_type = T_ATM_NULL;
+ }
+ if (argc > 0)
+ errx(1, "Too many parameters");
+
+ /*
+ * Tell the kernel to add the PVC
+ */
+ s = socket(AF_ATM, SOCK_DGRAM, 0);
+ if (s < 0) {
+ sock_error(errno);
+ }
+ if (ioctl(s, AIOCADD, (caddr_t)&apr) < 0) {
+ switch (errno) {
+ case EPROTONOSUPPORT:
+ case ENOPROTOOPT:
+ err(1, "Internal error");
+ case EINVAL:
+ errx(1, "Invalid parameter");
+ case EEXIST:
+ errx(1, "PVC already exists");
+ break;
+ case ENETDOWN:
+ errx(1, "ATM network is inoperable");
+ break;
+ case ENOMEM:
+ errx(1, "Kernel memory exhausted");
+ break;
+ case EPERM:
+ errx(1, "Must be super user to use add subcommand");
+ break;
+ case ERANGE:
+ errx(1, "Invalid VPI or VCI value");
+ break;
+ default:
+ err(1, "ioctl (AIOCADD) add PVC");
+ }
+ }
+ (void)close(s);
+}
+
+
+/*
+ * Process ARP add command
+ *
+ * Command formats:
+ * atm add arp [<netif>] <IP addr> <ATM addr>
+ *
+ * Arguments:
+ * argc number of arguments to command
+ * argv pointer to argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+arp_add(int argc, char **argv, const struct cmd *cmdp __unused)
+{
+ int len, s;
+ struct atmaddreq apr;
+ Atm_addr host_atm;
+ struct sockaddr_in *sain;
+ union {
+ struct sockaddr_in sain;
+ struct sockaddr sa;
+ } host_ip;
+
+ /*
+ * Initialize add request structure
+ */
+ bzero(&apr, sizeof(apr));
+
+ /*
+ * Get network interface name if one is present
+ */
+ if (argc == 3) {
+ check_netif_name(argv[0]);
+ strcpy(apr.aar_arp_intf, argv[0]);
+ argc--; argv++;
+ }
+
+ /*
+ * Get IP address of specified host name
+ */
+ bzero(&host_ip, sizeof(host_ip));
+ host_ip.sa.sa_family = AF_INET;
+ sain = get_ip_addr(argv[0]);
+ host_ip.sain.sin_addr.s_addr = sain->sin_addr.s_addr;
+ argc--; argv++;
+
+ /*
+ * Get specified ATM address
+ */
+ len = get_hex_atm_addr(argv[0], (u_char *)host_atm.address,
+ sizeof(Atm_addr_nsap));
+ switch(len) {
+ case sizeof(Atm_addr_nsap):
+ host_atm.address_format = T_ATM_ENDSYS_ADDR;
+ host_atm.address_length = sizeof(Atm_addr_nsap);
+ break;
+ case sizeof(Atm_addr_spans):
+ host_atm.address_format = T_ATM_SPANS_ADDR;
+ host_atm.address_length = sizeof(Atm_addr_spans);
+ break;
+ default:
+ fprintf(stderr, "%s: Invalid ATM address\n", prog);
+ exit(1);
+ }
+
+ /*
+ * Build IOCTL request
+ */
+ apr.aar_opcode = AIOCS_ADD_ARP;
+ apr.aar_arp_dst = host_ip.sa;
+ ATM_ADDR_COPY(&host_atm, &apr.aar_arp_addr);
+ apr.aar_arp_origin = ARP_ORIG_PERM;
+
+ /*
+ * Tell the kernel to add the ARP table entry
+ */
+ s = socket(AF_ATM, SOCK_DGRAM, 0);
+ if (s < 0) {
+ sock_error(errno);
+ }
+ if (ioctl(s, AIOCADD, (caddr_t)&apr) < 0) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case EINVAL:
+ fprintf(stderr, "Invalid parameter\n");
+ break;
+ case EPERM:
+ fprintf(stderr, "Must be super user to use add subcommand\n");
+ break;
+ case EADDRNOTAVAIL:
+ fprintf(stderr, "IP address not valid for interface\n");
+ break;
+ default:
+ perror("ioctl (AIOCADD) add");
+ break;
+ }
+ exit(1);
+ }
+ (void)close(s);
+}
+
+
+/*
+ * Process PVC delete command
+ *
+ * Command formats:
+ * atm delete pvc <interface_name> <vpi> <vci>
+ *
+ * Arguments:
+ * argc number of arguments to command
+ * argv pointer to argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+pvc_dlt(int argc, char **argv, const struct cmd *cmdp)
+{
+ struct atmdelreq apr;
+
+ /*
+ * Set opcode
+ */
+ apr.adr_opcode = AIOCS_DEL_PVC;
+
+ /*
+ * Complete request by calling subroutine
+ */
+ vcc_dlt(argc, argv, cmdp, &apr);
+}
+
+
+/*
+ * Process SVC delete command
+ *
+ * Command formats:
+ * atm delete svc <interface_name> <vpi> <vci>
+ *
+ * Arguments:
+ * argc number of arguments to command
+ * argv pointer to argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+svc_dlt(int argc, char **argv, const struct cmd *cmdp)
+{
+ struct atmdelreq apr;
+
+ /*
+ * Set opcode
+ */
+ apr.adr_opcode = AIOCS_DEL_SVC;
+
+ /*
+ * Complete request by calling subroutine
+ */
+ vcc_dlt(argc, argv, cmdp, &apr);
+}
+
+
+/*
+ * Complete an SVC or PVC delete command
+ *
+ * Arguments:
+ * argc number of arguments to command
+ * argv pointer to argument strings
+ * cmdp pointer to command description
+ * apr pointer to ATM delete IOCTL structure
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+vcc_dlt(int argc, char **argv, const struct cmd *cmdp __unused,
+ struct atmdelreq *apr)
+{
+ char *cp;
+ long v;
+ int s;
+
+ /*
+ * Validate interface name
+ */
+ if (strlen(argv[0]) > sizeof(apr->adr_pvc_intf) - 1) {
+ fprintf(stderr, "%s: Illegal interface name\n", prog);
+ exit(1);
+ }
+ strcpy(apr->adr_pvc_intf, argv[0]);
+ argc--; argv++;
+
+ /*
+ * Validate vpi/vci values
+ */
+ v = strtol(argv[0], &cp, 0);
+ if ((*cp != '\0') || (v < 0) || (v >= 1 << 8)) {
+ fprintf(stderr, "%s: Invalid VPI value\n", prog);
+ exit(1);
+ }
+ apr->adr_pvc_vpi = (u_short) v;
+ argc--; argv++;
+
+ v = strtol(argv[0], &cp, 0);
+ if ((*cp != '\0') || (v < MIN_VCI) || (v >= 1 << 16)) {
+ fprintf(stderr, "%s: Invalid VCI value\n", prog);
+ exit(1);
+ }
+ apr->adr_pvc_vci = (u_short) v;
+ argc--; argv++;
+
+ /*
+ * Tell the kernel to delete the VCC
+ */
+ s = socket(AF_ATM, SOCK_DGRAM, 0);
+ if (s < 0) {
+ sock_error(errno);
+ }
+ if (ioctl(s, AIOCDEL, (caddr_t)apr) < 0) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case EINVAL:
+ fprintf(stderr, "Invalid parameter\n");
+ break;
+ case ENOENT:
+ fprintf(stderr, "VCC not found\n");
+ break;
+ case EALREADY:
+ fprintf(stderr, "VCC already being closed\n");
+ break;
+ case ENXIO:
+ fprintf(stderr, "%s is not an ATM device\n",
+ apr->adr_pvc_intf);
+ break;
+ case EPERM:
+ fprintf(stderr, "Must be super user to use delete subcommand\n");
+ break;
+ default:
+ perror("ioctl (AIOCDEL) delete");
+ break;
+ }
+ exit(1);
+ }
+ (void)close(s);
+}
+
+
+/*
+ * Process ARP delete command
+ *
+ * Command formats:
+ * atm delete arp <IP addr>
+ *
+ * Arguments:
+ * argc number of arguments to command
+ * argv pointer to argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+arp_dlt(int argc, char **argv, const struct cmd *cmdp __unused)
+{
+ int s;
+ struct atmdelreq apr;
+ struct sockaddr_in *sain;
+ union {
+ struct sockaddr_in sain;
+ struct sockaddr sa;
+ } host_addr;
+
+ /*
+ * Set opcode
+ */
+ bzero(&apr, sizeof(apr));
+ apr.adr_opcode = AIOCS_DEL_ARP;
+
+ /*
+ * Get network interface name if one is present
+ */
+ if (argc == 2) {
+ check_netif_name(argv[0]);
+ strcpy(apr.adr_arp_intf, argv[0]);
+ argc--; argv++;
+ }
+
+ /*
+ * Get IP address of specified host name
+ */
+ bzero(&host_addr, sizeof(host_addr));
+ host_addr.sa.sa_family = AF_INET;
+ sain = get_ip_addr(argv[0]);
+ host_addr.sain.sin_addr.s_addr = sain->sin_addr.s_addr;
+ apr.adr_arp_dst = host_addr.sa;
+
+ /*
+ * Tell the kernel to delete the ARP table entry
+ */
+ s = socket(AF_ATM, SOCK_DGRAM, 0);
+ if (s < 0) {
+ sock_error(errno);
+ }
+ if (ioctl(s, AIOCDEL, (caddr_t)&apr) < 0) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case EINVAL:
+ fprintf(stderr, "Invalid parameter\n");
+ break;
+ case EPERM:
+ fprintf(stderr, "Must be super user to use delete subcommand\n");
+ break;
+ default:
+ perror("ioctl (AIOCDEL) delete");
+ break;
+ }
+ exit(1);
+ }
+ (void)close(s);
+}
+
+
+/*
+ * Process help command
+ *
+ * Arguments:
+ * argc number of arguments to command
+ * argv pointer to argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+help(int argc __unused, char **argv __unused, const struct cmd *cmdp __unused)
+{
+ usage(cmds, "");
+}
diff --git a/sbin/atm/atm/atm.h b/sbin/atm/atm/atm.h
new file mode 100644
index 0000000..09f5c87
--- /dev/null
+++ b/sbin/atm/atm/atm.h
@@ -0,0 +1,195 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * User configuration and display program
+ * --------------------------------------
+ *
+ * Control blocks
+ *
+ */
+
+#define MAX_NIFS 256 /* Max network interfaces */
+#define MIN_VCI 32 /* Smallest non-reserved VCI */
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+
+/*
+ * User commands
+ */
+struct cmd {
+ const char *name; /* Command name */
+ int minp; /* Minimum number of parameters */
+ int maxp; /* Maximum number of parameters */
+ void (*func)(int, char **,
+ const struct cmd *);/* Processing function */
+ const char *help; /* User help string */
+};
+
+
+/*
+ * Supported signalling protocols
+ */
+struct proto {
+ const char *p_name; /* Protocol name */
+ u_char p_id; /* Protocol id */
+};
+
+
+/*
+ * Table of state names
+ */
+struct state {
+ const char *s_name; /* State name */
+ u_char s_id; /* State id */
+};
+
+
+/*
+ * Supported signalling protocol states
+ */
+struct proto_state {
+ const char *p_name; /* Signalling manager name */
+ const struct state *p_state; /* Protocol state table */
+ const struct state *v_state; /* Protocol VCC state table */
+ u_char p_id; /* Protocol ID */
+};
+
+
+/*
+ * Supported VCC owners
+ */
+struct owner {
+ const char *o_name; /* Owner name */
+ u_int o_sap; /* Owner's SAP */
+ void (*o_pvcadd)(int, char **, const struct cmd *,
+ struct atmaddreq *, struct air_int_rsp *);
+ /* PVC ADD processing function */
+};
+
+
+/*
+ * Supported AALs
+ */
+struct aal {
+ const char *a_name; /* AAL name */
+ u_char a_id; /* AAL code */
+};
+
+
+/*
+ * Supported encapsulations
+ */
+struct encaps {
+ const char *e_name; /* Encapsulation name */
+ u_char e_id; /* Encapsulation code */
+};
+
+/*
+ * Supported traffic type
+ */
+struct traffics {
+ const char *t_name; /* Traffic name: CBR, VBR, UBR, ... */
+ uint8_t t_type; /* HARP code T_ATM_XXX */
+ int t_argc; /* Number of args */
+ const char *help; /* User help string */
+};
+
+/*
+ * External variables
+ */
+extern char *prog; /* Program invocation */
+extern char prefix[]; /* Current command prefix */
+
+/*
+ * Global function declarations
+ */
+
+ /* atm_eni.c */
+void show_eni_stats(char *, int, char **);
+
+ /* atm_fore200.c */
+void show_fore200_stats(char *, int, char **);
+
+ /* atm_inet.c */
+void ip_pvcadd(int, char **, const struct cmd *, struct atmaddreq *,
+ struct air_int_rsp *);
+
+ /* atm_print.c */
+void print_arp_info(struct air_arp_rsp *);
+void print_asrv_info(struct air_asrv_rsp *);
+void print_cfg_info(struct air_cfg_rsp *);
+void print_intf_info(struct air_int_rsp *);
+void print_ip_vcc_info(struct air_ip_vcc_rsp *);
+void print_netif_info(struct air_netif_rsp *);
+void print_intf_stats(struct air_phy_stat_rsp *);
+void print_vcc_stats(struct air_vcc_rsp *);
+void print_vcc_info(struct air_vcc_rsp *);
+void print_version_info(struct air_version_rsp *);
+
+ /* atm_set.c */
+void set_arpserver(int, char **, const struct cmd *);
+void set_macaddr(int, char **, const struct cmd *);
+void set_netif(int, char **, const struct cmd *);
+void set_prefix(int, char **, const struct cmd *);
+
+ /* atm_show.c */
+void show_arp(int, char **, const struct cmd *);
+void show_arpserv(int, char **, const struct cmd *);
+void show_config(int, char **, const struct cmd *);
+void show_intf(int, char **, const struct cmd *);
+void show_ip_vcc(int, char **, const struct cmd *);
+void show_netif(int, char **, const struct cmd *);
+void show_vcc(int, char **, const struct cmd *);
+void show_version(int, char **, const struct cmd *);
+void show_intf_stats(int, char **, const struct cmd *);
+void show_vcc_stats(int, char **, const struct cmd *);
+
+ /* atm_subr.c */
+const char * get_vendor(int);
+const char * get_adapter(int);
+const char * get_media_type(int);
+const char * get_bus_type(int);
+const char * get_bus_slot_info(int, u_long);
+const char * get_adapter_name(const char *);
+int get_hex_addr(char *, u_char *, int);
+const char * format_mac_addr(const Mac_addr *);
+int parse_ip_prefix(const char *, struct in_addr *);
+size_t compress_prefix_list(struct in_addr *, size_t);
+void check_netif_name(const char *);
+void sock_error(int);
+
+extern const struct aal aals[];
+extern const struct encaps encaps[];
diff --git a/sbin/atm/atm/atm_fore200.c b/sbin/atm/atm/atm_fore200.c
new file mode 100644
index 0000000..d6da017
--- /dev/null
+++ b/sbin/atm/atm/atm_fore200.c
@@ -0,0 +1,596 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ */
+
+/*
+ * User configuration and display program
+ * --------------------------------------
+ *
+ * Routines for Fore SBA-200-specific subcommands
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+#include <dev/hfa/fore_aali.h>
+#include <dev/hfa/fore_slave.h>
+#include <dev/hfa/fore_stats.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+
+#include "atm.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Local constants
+ */
+#define SHOW_PHY 1
+#define SHOW_DEV 2
+#define SHOW_ATM 4
+#define SHOW_AAL0 8
+#define SHOW_AAL4 16
+#define SHOW_AAL5 32
+#define SHOW_DRIVER 64
+
+
+/*
+ * Headers for statistics
+ */
+#define TAXI_STATS_HDR \
+"%s TAXI Statistics\n\
+ CRC Errs Framing Errs\n"
+
+#define DEV_STATS_HDR \
+"%s Device Statistics\n\
+Type 1 Type 1 Type 2 Type 2\n\
+Small Buff Large Buff Small Buff Large Buff Receive Receive\n\
+Alloc Fail Alloc Fail Alloc Fail Alloc Fail Queue Full Carrier\n"
+
+#define ATM_STATS_HDR \
+"%s ATM Layer Statistics\n\
+ Cells In Cells Out VPI Range VPI NoConn VCI Range VCI NoConn\n"
+
+#define AAL0_STATS_HDR \
+"%s AAL 0 Statistics\n\
+ Cells In Cells Out Cell Drops\n"
+
+#define AAL4_STATS_HDR \
+"%s AAL 4 Statistics\n\
+ CRC Proto Cell PDU PDU\n\
+ Cells In Cells Out Errs Errs Drops PDUs In PDUs Out Errs Drops\n"
+
+#define AAL5_STATS_HDR \
+"%s AAL 5 Statistics\n\
+ CRC/Len CRC Proto PDU\n\
+ Cells In Cells Out Errs Drops PDUs In PDUs Out Errs Errs Drops\n"
+
+#define DRIVER_STATS_HDR \
+"%s Device Driver Statistics\n\
+ No Xmit Max Seg No No No IQ No Cmd No\n\
+ VCC Queue Seg Not Seg DMA VCC No Mbuf Full DMA Queue DMA\n\
+ Out Full Size Align Pad Out In Buff In In Sup Full Cmd\n"
+
+#define OC3_STATS_HDR \
+"%s OC-3c Statistics\n\
+Section Path Line Line Path Corr Uncorr\n\
+BIP8 BIP8 BIP24 FEBE FEBE HCS HCS\n\
+Errs Errs Errs Errs Errs Errs Errs\n"
+
+static void print_fore200_taxi(struct air_vinfo_rsp *);
+static void print_fore200_oc3(struct air_vinfo_rsp *);
+static void print_fore200_dev(struct air_vinfo_rsp *);
+static void print_fore200_atm(struct air_vinfo_rsp *);
+static void print_fore200_aal0(struct air_vinfo_rsp *);
+static void print_fore200_aal4(struct air_vinfo_rsp *);
+static void print_fore200_aal5(struct air_vinfo_rsp *);
+static void print_fore200_driver(struct air_vinfo_rsp *);
+
+/*
+ * Process show Fore SBA-200 statistics command
+ *
+ * The statistics printed are vendor-specific, depending on the brand of
+ * the interface card.
+ *
+ * Command format:
+ * atm show stats interface [<interface-name> [phy | dev | atm |
+ * aal0 | aal4 | aal5 | driver]]
+ *
+ * Arguments:
+ * intf interface statistics are for
+ * argc number of remaining arguments to command
+ * argv pointer to remaining argument strings
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+show_fore200_stats(intf, argc, argv)
+ char *intf;
+ int argc;
+ char **argv;
+{
+ int stats_type;
+ ssize_t buf_len;
+ struct air_cfg_rsp *cfg;
+ struct air_vinfo_rsp *stats;
+ struct atminfreq air;
+
+ /*
+ * Get statistics type qualifier
+ */
+ if (!strcasecmp("phy", argv[0])) {
+ stats_type = SHOW_PHY;
+ } else if (!strcasecmp("dev", argv[0])) {
+ stats_type = SHOW_DEV;
+ } else if (!strcasecmp("atm", argv[0])) {
+ stats_type = SHOW_ATM;
+ } else if (!strcasecmp("aal0", argv[0])) {
+ stats_type = SHOW_AAL0;
+ } else if (!strcasecmp("aal4", argv[0])) {
+ stats_type = SHOW_AAL4;
+ } else if (!strcasecmp("aal5", argv[0])) {
+ stats_type = SHOW_AAL5;
+ } else if (!strcasecmp("driver", argv[0])) {
+ stats_type = SHOW_DRIVER;
+ } else {
+ errx(1, "Illegal statistics type");
+ }
+ argc--; argv++;
+
+ /*
+ * Get adapter configuration from the kernel
+ */
+ bzero(&air, sizeof(air));
+ air.air_opcode = AIOCS_INF_CFG;
+ strcpy(air.air_cfg_intf, intf);
+ buf_len = do_info_ioctl(&air, sizeof(struct air_cfg_rsp));
+ if (buf_len == -1) {
+ switch (errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ err(1, "Internal error");
+ case ENXIO:
+ errx(1, "%s is not an ATM device", intf);
+ default:
+ err(1, "ioctl (AIOCINFO)");
+ }
+ }
+ cfg = (struct air_cfg_rsp *)(void *)air.air_buf_addr;
+
+ /*
+ * Get vendor-specific statistics from the kernel
+ */
+ bzero(&air, sizeof(air));
+ air.air_opcode = AIOCS_INF_VST;
+ strcpy(air.air_vinfo_intf, intf);
+ buf_len = do_info_ioctl(&air, sizeof(struct air_vinfo_rsp) + 1024);
+ if (buf_len == -1) {
+ switch (errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ err(1, "Internal error");
+ case ENXIO:
+ errx(1, "%s is not an ATM device", intf);
+ default:
+ err(1, "ioctl (AIOCINFO)");
+ }
+ }
+ stats = (struct air_vinfo_rsp *)(void *)air.air_buf_addr;
+
+ /*
+ * Print the statistics
+ */
+ if ((size_t)buf_len < sizeof(struct air_vinfo_rsp) +
+ sizeof(Fore_stats)) {
+ free(stats);
+ free(cfg);
+ return;
+ }
+
+ switch (stats_type) {
+ case SHOW_PHY:
+ switch (cfg->acp_media) {
+ case MEDIA_TAXI_100:
+ case MEDIA_TAXI_140:
+ print_fore200_taxi(stats);
+ break;
+ case MEDIA_OC3C:
+ print_fore200_oc3(stats);
+ break;
+ case MEDIA_OC12C:
+ break;
+ default:
+ break;
+ }
+ break;
+ case SHOW_DEV:
+ print_fore200_dev(stats);
+ break;
+ case SHOW_ATM:
+ print_fore200_atm(stats);
+ break;
+ case SHOW_AAL0:
+ print_fore200_aal0(stats);
+ break;
+ case SHOW_AAL4:
+ print_fore200_aal4(stats);
+ break;
+ case SHOW_AAL5:
+ print_fore200_aal5(stats);
+ break;
+ case SHOW_DRIVER:
+ print_fore200_driver(stats);
+ break;
+ }
+
+ free(stats);
+ free(cfg);
+}
+
+
+/*
+ * Print Fore ASX-200 TAXI statistics
+ *
+ * Arguments:
+ * vi pointer to vendor-specific statistics to print
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_fore200_taxi(vi)
+ struct air_vinfo_rsp *vi;
+{
+ Fore_stats *stats;
+
+ /*
+ * Bump stats pointer past header info
+ */
+ stats = (Fore_stats *)
+ ((u_long) vi + sizeof(struct air_vinfo_rsp));
+
+ /*
+ * Print a header
+ */
+ printf(TAXI_STATS_HDR, get_adapter_name(vi->avsp_intf));
+
+ /*
+ * Print the physical layer info
+ */
+ printf("%10ld %12ld\n",
+ stats->st_taxi.taxi_bad_crc,
+ stats->st_taxi.taxi_framing);
+}
+
+
+/*
+ * Print Fore ASX-200 OC-3c statistics
+ *
+ * Arguments:
+ * vi pointer to vendor-specific statistics to print
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_fore200_oc3(vi)
+ struct air_vinfo_rsp *vi;
+{
+ Fore_stats *stats;
+
+ /*
+ * Bump stats pointer past header info
+ */
+ stats = (Fore_stats *)
+ ((u_long) vi + sizeof(struct air_vinfo_rsp));
+
+ /*
+ * Print a header
+ */
+ printf(OC3_STATS_HDR, get_adapter_name(vi->avsp_intf));
+
+ /*
+ * Print the OC-3c info
+ */
+ printf("%7ld %7ld %7ld %7ld %7ld %7ld %7ld\n",
+ stats->st_oc3.oc3_sect_bip8,
+ stats->st_oc3.oc3_path_bip8,
+ stats->st_oc3.oc3_line_bip24,
+ stats->st_oc3.oc3_line_febe,
+ stats->st_oc3.oc3_path_febe,
+ stats->st_oc3.oc3_hec_corr,
+ stats->st_oc3.oc3_hec_uncorr);
+}
+
+
+/*
+ * Print Fore ASX-200 device statistics
+ *
+ * Arguments:
+ * vi pointer to vendor-specific statistics to print
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_fore200_dev(vi)
+ struct air_vinfo_rsp *vi;
+{
+ Fore_stats *stats;
+
+ /*
+ * Bump stats pointer past header info
+ */
+ stats = (Fore_stats *)
+ ((u_long) vi + sizeof(struct air_vinfo_rsp));
+
+ /*
+ * Print a header
+ */
+ printf(DEV_STATS_HDR, get_adapter_name(vi->avsp_intf));
+
+ /*
+ * Print the device info
+ */
+ printf("%10ld %10ld %10ld %10ld %10ld %s\n",
+ stats->st_misc.buf1_sm_fail,
+ stats->st_misc.buf1_lg_fail,
+ stats->st_misc.buf2_sm_fail,
+ stats->st_misc.buf2_lg_fail,
+ stats->st_misc.rcvd_pdu_fail,
+ (stats->st_misc.carrier_status ? "On" : "Off"));
+}
+
+
+/*
+ * Print Fore ASX-200 ATM statistics
+ *
+ * Arguments:
+ * vi pointer to vendor-specific statistics to print
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_fore200_atm(vi)
+ struct air_vinfo_rsp *vi;
+{
+ Fore_stats *stats;
+
+ /*
+ * Bump stats pointer past header info
+ */
+ stats = (Fore_stats *)
+ ((u_long) vi + sizeof(struct air_vinfo_rsp));
+
+ /*
+ * Print a header
+ */
+ printf(ATM_STATS_HDR, get_adapter_name(vi->avsp_intf));
+
+ /*
+ * Print the ATM layer info
+ */
+ printf("%10ld %10ld %10ld %10ld %10ld %10ld\n",
+ stats->st_atm.atm_rcvd,
+ stats->st_atm.atm_xmit,
+ stats->st_atm.atm_vpi_range,
+ stats->st_atm.atm_vpi_noconn,
+ stats->st_atm.atm_vci_range,
+ stats->st_atm.atm_vci_noconn);
+}
+
+
+/*
+ * Print Fore ASX-200 AAL 0 statistics
+ *
+ * Arguments:
+ * vi pointer to vendor-specific statistics to print
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_fore200_aal0(vi)
+ struct air_vinfo_rsp *vi;
+{
+ Fore_stats *stats;
+
+ /*
+ * Bump stats pointer past header info
+ */
+ stats = (Fore_stats *)
+ ((u_long) vi + sizeof(struct air_vinfo_rsp));
+
+ /*
+ * Print a header
+ */
+ printf(AAL0_STATS_HDR, get_adapter_name(vi->avsp_intf));
+
+ /*
+ * Print the AAL 0 info
+ */
+ printf("%10ld %10ld %10ld\n",
+ stats->st_aal0.aal0_rcvd,
+ stats->st_aal0.aal0_xmit,
+ stats->st_aal0.aal0_drops);
+}
+
+
+/*
+ * Print Fore ASX-200 AAL 4 statistics
+ *
+ * Arguments:
+ * vi pointer to vendor-specific statistics to print
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_fore200_aal4(vi)
+ struct air_vinfo_rsp *vi;
+{
+ Fore_stats *stats;
+
+ /*
+ * Bump stats pointer past header info
+ */
+ stats = (Fore_stats *)
+ ((u_long) vi + sizeof(struct air_vinfo_rsp));
+
+ /*
+ * Print a header
+ */
+ printf(AAL4_STATS_HDR, get_adapter_name(vi->avsp_intf));
+
+ /*
+ * Print the AAL 4 info
+ */
+ printf("%10ld %10ld %5ld %5ld %5ld %9ld %9ld %5ld %5ld\n",
+ stats->st_aal4.aal4_rcvd,
+ stats->st_aal4.aal4_xmit,
+ stats->st_aal4.aal4_crc,
+ stats->st_aal4.aal4_sar_cs,
+ stats->st_aal4.aal4_drops,
+ stats->st_aal4.aal4_pdu_rcvd,
+ stats->st_aal4.aal4_pdu_xmit,
+ stats->st_aal4.aal4_pdu_errs,
+ stats->st_aal4.aal4_pdu_drops);
+}
+
+
+/*
+ * Print Fore ASX-200 AAL 5 statistics
+ *
+ * Arguments:
+ * vi pointer to vendor-specific statistics to print
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_fore200_aal5(vi)
+ struct air_vinfo_rsp *vi;
+{
+ Fore_stats *stats;
+
+ /*
+ * Bump stats pointer past header info
+ */
+ stats = (Fore_stats *)
+ ((u_long) vi + sizeof(struct air_vinfo_rsp));
+
+ /*
+ * Print a header
+ */
+ printf(AAL5_STATS_HDR, get_adapter_name(vi->avsp_intf));
+
+ /*
+ * Print the AAL 5 info
+ */
+ printf("%10ld %10ld %5ld %5ld %9ld %9ld %5ld %5ld %5ld\n",
+ stats->st_aal5.aal5_rcvd,
+ stats->st_aal5.aal5_xmit,
+ stats->st_aal5.aal5_crc_len,
+ stats->st_aal5.aal5_drops,
+ stats->st_aal5.aal5_pdu_rcvd,
+ stats->st_aal5.aal5_pdu_xmit,
+ stats->st_aal5.aal5_pdu_crc,
+ stats->st_aal5.aal5_pdu_errs,
+ stats->st_aal5.aal5_pdu_drops);
+}
+
+
+/*
+ * Print Fore ASX-200 device driver statistics
+ *
+ * Arguments:
+ * vi pointer to vendor-specific statistics to print
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_fore200_driver(vi)
+ struct air_vinfo_rsp *vi;
+{
+ Fore_stats *stats;
+
+ /*
+ * Bump stats pointer past header info
+ */
+ stats = (Fore_stats *)
+ ((u_long) vi + sizeof(struct air_vinfo_rsp));
+
+ /*
+ * Print a header
+ */
+ printf(DRIVER_STATS_HDR, get_adapter_name(vi->avsp_intf));
+
+ /*
+ * Print the driver info
+ */
+ printf("%4ld %4ld %4ld %4ld %4ld %4ld %4ld %4ld %4ld %4ld %4ld %4ld %4ld\n",
+ stats->st_drv.drv_xm_notact,
+ stats->st_drv.drv_xm_full,
+ stats->st_drv.drv_xm_maxpdu,
+ stats->st_drv.drv_xm_segnoal,
+ stats->st_drv.drv_xm_seglen,
+ stats->st_drv.drv_xm_segdma,
+ stats->st_drv.drv_rv_novcc,
+ stats->st_drv.drv_rv_nosbf,
+ stats->st_drv.drv_rv_nomb,
+ stats->st_drv.drv_rv_ifull,
+ stats->st_drv.drv_bf_segdma,
+ stats->st_drv.drv_cm_full,
+ stats->st_drv.drv_cm_nodma);
+
+}
diff --git a/sbin/atm/atm/atm_inet.c b/sbin/atm/atm/atm_inet.c
new file mode 100644
index 0000000..2f916a0
--- /dev/null
+++ b/sbin/atm/atm/atm_inet.c
@@ -0,0 +1,154 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ */
+
+/*
+ * User configuration and display program
+ * --------------------------------------
+ *
+ * IP support
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+
+#include "atm.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Process add command for a TCP/IP PVC
+ *
+ * Command format:
+ * atm add pvc <intf> <vpi> <vci> <aal> <encaps> IP <netif>
+ * <IP addr> | dynamic
+ *
+ * Arguments:
+ * argc number of remaining arguments to command
+ * argv pointer to remaining argument strings
+ * cmdp pointer to command description
+ * app pointer to AIOCAPVC structure
+ * intp pointer to air_int_rsp structure with information
+ * about the physical interface that is the PVC is for.
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+ip_pvcadd(int argc, char **argv, const struct cmd *cmdp,
+ struct atmaddreq *app, struct air_int_rsp *intp)
+{
+ char *cp;
+ char nhelp[128];
+ u_int netif_no;
+ u_int i, netif_pref_len;
+
+ /*
+ * Yet more validation
+ */
+ if (argc < 2) {
+ strcpy(nhelp, cmdp->help);
+ cp = strstr(nhelp, "<netif>");
+ if (cp)
+ strcpy(cp, "ip {dyn|<dst>}");
+ fprintf(stderr, "%s: Invalid number of arguments:\n",
+ prog);
+ fprintf(stderr, "\tformat is: %s%s %s\n",
+ prefix, cmdp->name, nhelp);
+ exit(1);
+ }
+
+ /*
+ * Validate and set network interface
+ */
+ bzero(app->aar_pvc_intf, sizeof(app->aar_pvc_intf));
+ netif_pref_len = strlen(intp->anp_nif_pref);
+ cp = &argv[0][netif_pref_len];
+ netif_no = (u_int)strtoul(cp, NULL, 10);
+ for (i = 0; i < strlen(cp); i++) {
+ if (cp[i] < '0' || cp[i] > '9') {
+ netif_no = -1;
+ break;
+ }
+ }
+ if (strlen(argv[0]) > sizeof(app->aar_pvc_intf) - 1)
+ errx(1, "Illegal network interface name '%s'", argv[0]);
+
+ if (strncasecmp(intp->anp_nif_pref, argv[0], netif_pref_len) ||
+ strlen(argv[0]) <= netif_pref_len || netif_no >= intp->anp_nif_cnt)
+ errx(1, "network interface %s is not associated with "
+ "interface %s", argv[0], intp->anp_intf);
+
+ strcpy(app->aar_pvc_intf, argv[0]);
+ argc--;
+ argv++;
+
+ /*
+ * Set PVC destination address
+ */
+ bzero(&app->aar_pvc_dst, sizeof(struct sockaddr));
+ if (strcasecmp(argv[0], "dynamic") == 0 ||
+ strcasecmp(argv[0], "dyn") == 0) {
+
+ /*
+ * Destination is dynamically determined
+ */
+ app->aar_pvc_flags |= PVC_DYN;
+ } else {
+
+ /*
+ * Get destination IP address
+ */
+ struct sockaddr_in *sain, *ret;
+
+ sain = (struct sockaddr_in *)(void *)&app->aar_pvc_dst;
+ ret = get_ip_addr(argv[0]);
+ if (ret == NULL)
+ errx(1, "%s: bad ip address '%s'", argv[-1], argv[0]);
+ sain->sin_addr.s_addr = ret->sin_addr.s_addr;
+ }
+ argc--; argv++;
+}
+
diff --git a/sbin/atm/atm/atm_print.c b/sbin/atm/atm/atm_print.c
new file mode 100644
index 0000000..a3f0ce3
--- /dev/null
+++ b/sbin/atm/atm/atm_print.c
@@ -0,0 +1,891 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ */
+
+/*
+ * User configuration and display program
+ * --------------------------------------
+ *
+ * Print routines for "show" subcommand
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netatm/port.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sigmgr.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_vc.h>
+#include <netatm/atm_ioctl.h>
+#include <netatm/ipatm/ipatm_var.h>
+#include <netatm/sigpvc/sigpvc_var.h>
+#include <netatm/spans/spans_var.h>
+#include <netatm/uni/uniip_var.h>
+#include <netatm/uni/unisig_var.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "atm.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+#define ARP_HDR \
+"Net Intf Flags Age Origin\n"
+
+#define ASRV_HDR \
+"Net Intf State ATM Address\n"
+
+#define CFG_HDR \
+"Intf Vendor Model Media Bus Serial No\n"
+
+#define IP_VCC_HDR \
+"Net Intf VPI VCI State Flags IP Address\n"
+
+#define INTF_HDR \
+"Interface Sigmgr State\n"
+
+#define NETIF_HDR \
+"Net Intf Phy Intf IP Address\n"
+
+#define VCC_HDR \
+"Interface VPI VCI AAL Type Dir State Encaps Owner\n"
+
+#define VCC_STATS_HDR \
+" Input Input Input Output Output Output\n\
+Interface VPI VCI PDUs Bytes Errs PDUs Bytes Errs\n"
+
+#define VERSION_HDR \
+"Version\n"
+
+#define PHY_STATS_HDR \
+" Input Input Input Output Output Output Cmd\n\
+Interface PDUs Bytes Errs PDUs Bytes Errs Errs\n"
+
+/*
+ * Local variables
+ */
+static int arp_hdr = 0;
+static int asrv_hdr = 0;
+static int cfg_hdr = 0;
+static int ip_vcc_hdr = 0;
+static int netif_hdr = 0;
+static int vcc_hdr = 0;
+static int vcc_stats_hdr = 0;
+static int phy_stats_hdr = 0;
+static int version_hdr = 0;
+
+/*
+ * SIGPVC state definitions
+ */
+static const struct state sigpvc_states[] = {
+ { "ACTIVE", SIGPVC_ACTIVE },
+ { "DETACH", SIGPVC_DETACH },
+ { 0, 0 }
+};
+
+/*
+ * SPANS state definitions
+ */
+static const struct state spans_states[] = {
+ { "ACTIVE", SPANS_ACTIVE },
+ { "DETACH", SPANS_DETACH },
+ { "INIT", SPANS_INIT },
+ { "PROBE", SPANS_PROBE },
+ { 0, 0 }
+};
+
+/*
+ * UNISIG state definitions
+ */
+static const struct state unisig_states[] = {
+ { "NULL", UNISIG_NULL },
+ { "ADR_WAIT", UNISIG_ADDR_WAIT },
+ { "INIT", UNISIG_INIT },
+ { "ACTIVE", UNISIG_ACTIVE },
+ { "DETACH", UNISIG_DETACH },
+ { 0, 0 }
+};
+
+/*
+ * SIGPVC VCC state definitions
+ */
+static const struct state sigpvc_vcc_states[] = {
+ { "NULL", VCCS_NULL },
+ { "ACTIVE", VCCS_ACTIVE },
+ { "FREE", VCCS_FREE },
+ { 0, 0 }
+};
+
+/*
+ * SPANS VCC state definitions
+ */
+static const struct state spans_vcc_states[] = {
+ { "NULL", SPANS_VC_NULL },
+ { "ACTIVE", SPANS_VC_ACTIVE },
+ { "ACT_DOWN", SPANS_VC_ACT_DOWN },
+ { "POPEN", SPANS_VC_POPEN },
+ { "R_POPEN", SPANS_VC_R_POPEN },
+ { "OPEN", SPANS_VC_OPEN },
+ { "CLOSE", SPANS_VC_CLOSE },
+ { "ABORT", SPANS_VC_ABORT },
+ { "FREE", SPANS_VC_FREE },
+ {0, 0 }
+};
+
+/*
+ * UNISIG VCC state definitions
+ */
+static const struct state unisig_vcc_states[] = {
+ { "NULL", UNI_NULL },
+ { "C_INIT", UNI_CALL_INITIATED },
+ { "C_OUT_PR", UNI_CALL_OUT_PROC },
+ { "C_DELIV", UNI_CALL_DELIVERED },
+ { "C_PRES", UNI_CALL_PRESENT },
+ { "C_REC", UNI_CALL_RECEIVED },
+ { "CONN_REQ", UNI_CONNECT_REQUEST },
+ { "C_IN_PR", UNI_CALL_IN_PROC },
+ { "ACTIVE", UNI_ACTIVE },
+ { "REL_REQ", UNI_RELEASE_REQUEST },
+ { "REL_IND", UNI_RELEASE_IND },
+ { "SSCF_REC", UNI_SSCF_RECOV },
+ { "FREE", UNI_FREE },
+ { "ACTIVE", UNI_PVC_ACTIVE },
+ { "ACT_DOWN", UNI_PVC_ACT_DOWN },
+ {0, 0 }
+};
+
+/*
+ * IP VCC state definitions
+ */
+static const struct state ip_vcc_states[] = {
+ { "FREE", IPVCC_FREE },
+ { "PMAP", IPVCC_PMAP },
+ { "POPEN", IPVCC_POPEN },
+ { "PACCEPT", IPVCC_PACCEPT },
+ { "ACTPENT", IPVCC_ACTPENT },
+ { "ACTIVE", IPVCC_ACTIVE },
+ { "CLOSED", IPVCC_CLOSED },
+ { 0, 0 }
+};
+
+/*
+ * ARP server state definitions
+ */
+static const struct state arpserver_states[] = {
+ { "NOT_CONF", UIAS_NOTCONF },
+ { "SERVER", UIAS_SERVER_ACTIVE },
+ { "PEND_ADR", UIAS_CLIENT_PADDR },
+ { "POPEN", UIAS_CLIENT_POPEN },
+ { "REGISTER", UIAS_CLIENT_REGISTER },
+ { "ACTIVE", UIAS_CLIENT_ACTIVE },
+ { 0, 0 }
+};
+
+/*
+ * Supported signalling managers
+ */
+static const struct proto_state proto_states[] = {
+ { "SIGPVC", sigpvc_states, sigpvc_vcc_states, ATM_SIG_PVC },
+ { "SPANS", spans_states, spans_vcc_states, ATM_SIG_SPANS },
+ { "UNI 3.0", unisig_states, unisig_vcc_states, ATM_SIG_UNI30 },
+ { "UNI 3.1", unisig_states, unisig_vcc_states, ATM_SIG_UNI31 },
+ { "UNI 4.0", unisig_states, unisig_vcc_states, ATM_SIG_UNI40 },
+ { 0, 0, 0, 0 }
+};
+
+/*
+ * ATMARP origin values
+ */
+static const struct state arp_origins[] = {
+ { "LOCAL", UAO_LOCAL },
+ { "PERM", UAO_PERM },
+ { "REG", UAO_REGISTER },
+ { "SCSP", UAO_SCSP },
+ { "LOOKUP", UAO_LOOKUP },
+ { "PEER_RSP", UAO_PEER_RSP },
+ { "PEER_REQ", UAO_PEER_REQ },
+ { 0, 0 }
+};
+
+
+/*
+ * Print ARP table information
+ *
+ * Arguments:
+ * ai pointer to a struct air_arp_rsp
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_arp_info(ai)
+ struct air_arp_rsp *ai;
+{
+ int i;
+ const char *atm_addr, *ip_addr, *origin;
+ char age[8], flags[32];
+ struct sockaddr_in *sain;
+
+ /*
+ * Print a header if it hasn't been done yet.
+ */
+ if (!arp_hdr) {
+ printf(ARP_HDR);
+ arp_hdr = 1;
+ }
+
+ /*
+ * Format the addresses
+ */
+ atm_addr = format_atm_addr(&ai->aap_addr);
+ sain = (struct sockaddr_in *)(void *)&ai->aap_arp_addr;
+ ip_addr = format_ip_addr(&sain->sin_addr);
+
+ /*
+ * Decode the flags
+ */
+ bzero(flags, sizeof(flags));
+ if (ai->aap_flags & ARPF_VALID) {
+ strcat(flags, "V");
+ }
+ if (ai->aap_flags & ARPF_REFRESH) {
+ strcat(flags, "R");
+ }
+
+ /*
+ * Format the origin
+ */
+ for (i=0; arp_origins[i].s_name != NULL &&
+ ai->aap_origin != arp_origins[i].s_id;
+ i++);
+ if (arp_origins[i].s_name) {
+ origin = arp_origins[i].s_name;
+ } else {
+ origin = "-";
+ }
+
+ /*
+ * Format the age
+ */
+ bzero(age, sizeof(age));
+ if (!(ai->aap_flags & ARPF_VALID)) {
+ strcpy(age, "-");
+ } else {
+ sprintf(age, "%d", ai->aap_age);
+ }
+
+ /*
+ * Print the ARP information
+ */
+ printf("%-8s %-5s %3s %s\n ATM address = %s\n IP address = %s\n",
+ ai->aap_intf,
+ flags,
+ age,
+ origin,
+ atm_addr,
+ ip_addr);
+}
+
+
+/*
+ * Print ARP server information
+ *
+ * Arguments:
+ * si pointer to a struct air_asrv_rsp
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_asrv_info(si)
+ struct air_asrv_rsp *si;
+{
+ int i;
+ const char *atm_addr, *state;
+ struct in_addr *addr;
+
+ /*
+ * Print a header if it hasn't been done yet.
+ */
+ if (!asrv_hdr) {
+ printf(ASRV_HDR);
+ asrv_hdr = 1;
+ }
+
+ /*
+ * Format the ATM address of the ARP server
+ */
+ atm_addr = format_atm_addr(&si->asp_addr);
+
+ /*
+ * Format the server state
+ */
+ for (i=0; arpserver_states[i].s_name != NULL &&
+ si->asp_state != arpserver_states[i].s_id;
+ i++);
+ if (arpserver_states[i].s_name) {
+ state = arpserver_states[i].s_name;
+ } else {
+ state = "-";
+ }
+
+ /*
+ * Print the ARP server information
+ */
+ printf("%-8s %-8s %s\n",
+ si->asp_intf,
+ state,
+ atm_addr);
+
+ /*
+ * Format and print the LIS prefixes
+ */
+ if (si->asp_nprefix) {
+ addr = (struct in_addr *)((u_long)si +
+ sizeof(struct air_asrv_rsp));
+ printf(" LIS = ");
+ for (i = 0; i < si->asp_nprefix; i++) {
+ printf("%s", inet_ntoa(*addr));
+ addr++;
+ printf("/0x%0lx", (u_long)ntohl(addr->s_addr));
+ addr++;
+ if (i < si->asp_nprefix -1)
+ printf(", ");
+ }
+ printf("\n");
+ }
+}
+
+
+/*
+ * Print adapter configuration information
+ *
+ * Arguments:
+ * si pointer to a struct air_cfg_rsp
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_cfg_info(si)
+ struct air_cfg_rsp *si;
+{
+ const char *adapter, *bus, *media, *vendor;
+
+ /*
+ * Print a header if it hasn't been done yet.
+ */
+ if (!cfg_hdr) {
+ printf(CFG_HDR);
+ cfg_hdr = 1;
+ }
+
+ /*
+ * Format the vendor name and adapter type
+ */
+ vendor = get_vendor(si->acp_vendor);
+ adapter = get_adapter(si->acp_device);
+
+ /*
+ * Format the communications medium
+ */
+ media = get_media_type(si->acp_media);
+ bus = get_bus_type(si->acp_bustype);
+
+ /*
+ * Print the ARP server information
+ */
+ printf("%-8s %-8s %-8s %-14s %-4s %ld\n",
+ si->acp_intf,
+ vendor,
+ adapter,
+ media,
+ bus,
+ si->acp_serial);
+ printf(" MAC address = %s\n",
+ format_mac_addr(&si->acp_macaddr));
+ printf(" Hardware version = %s\n", si->acp_hard_vers);
+ printf(" Firmware version = %s\n", si->acp_firm_vers);
+}
+
+
+/*
+ * Print interface information
+ *
+ * Arguments:
+ * ni pointer to a struct air_int_rsp
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_intf_info(ni)
+ struct air_int_rsp *ni;
+{
+ int i;
+ char nif_names[(IFNAMSIZ *2)+4];
+ char *atm_addr;
+ const char *sigmgr = "-";
+ const char *state_name = "-";
+ const struct state *s_t;
+
+ /*
+ * Print a header
+ */
+ printf(INTF_HDR);
+
+ /*
+ * Translate signalling manager name
+ */
+ for (i=0; proto_states[i].p_state != NULL; i++)
+ if (ni->anp_sig_proto == proto_states[i].p_id)
+ break;
+ if (proto_states[i].p_state != NULL)
+ sigmgr = proto_states[i].p_name;
+
+ /*
+ * Get the signalling manager state
+ */
+ if (proto_states[i].p_state != NULL) {
+ s_t = proto_states[i].p_state;
+ for (i=0; s_t[i].s_name != NULL; i++)
+ if (ni->anp_sig_state == s_t[i].s_id)
+ break;
+ if (s_t[i].s_name != NULL)
+ state_name = s_t[i].s_name;
+ }
+
+ /*
+ * Format the ATM address
+ */
+ atm_addr = format_atm_addr(&ni->anp_addr);
+
+ /*
+ * Get the range of NIFs on the physical interface
+ */
+ bzero(nif_names, sizeof(nif_names));
+ if (strlen(ni->anp_nif_pref) == 0) {
+ strcpy(nif_names, "-");
+ } else {
+ strcpy(nif_names, ni->anp_nif_pref);
+ strcat(nif_names, "0");
+ if (ni->anp_nif_cnt > 1) {
+ strcat(nif_names, " - ");
+ strcat(nif_names, ni->anp_nif_pref);
+ sprintf(&nif_names[strlen(nif_names)], "%d",
+ ni->anp_nif_cnt-1);
+ }
+ }
+
+
+ /*
+ * Print the interface information
+ */
+ printf("%-9s %-7s %s\n",
+ ni->anp_intf,
+ sigmgr,
+ state_name);
+ printf(" ATM address = %s\n", atm_addr);
+ printf(" Network interfaces: %s\n", nif_names);
+}
+
+
+/*
+ * Print IP address map information
+ *
+ * Arguments:
+ * ai pointer to a struct air_arp_rsp
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_ip_vcc_info(ai)
+ struct air_ip_vcc_rsp *ai;
+{
+ int i;
+ const char *ip_addr, *state;
+ char flags[32], vpi_vci[16];
+ struct sockaddr_in *sain;
+
+ /*
+ * Print a header if it hasn't been done yet.
+ */
+ if (!ip_vcc_hdr) {
+ printf(IP_VCC_HDR);
+ ip_vcc_hdr = 1;
+ }
+
+ /*
+ * Format the IP address
+ */
+ sain = (struct sockaddr_in *)(void *)&ai->aip_dst_addr;
+ ip_addr = format_ip_addr(&sain->sin_addr);
+
+ /*
+ * Format the VPI/VCI
+ */
+ if (ai->aip_vpi == 0 && ai->aip_vci == 0) {
+ strcpy(vpi_vci, " - -");
+ } else {
+ sprintf(vpi_vci, "%3d %5d", ai->aip_vpi, ai->aip_vci);
+ }
+
+ /*
+ * Decode VCC flags
+ */
+ bzero(flags, sizeof(flags));
+ if (ai->aip_flags & IVF_PVC) {
+ strcat(flags, "P");
+ }
+ if (ai->aip_flags & IVF_SVC) {
+ strcat(flags, "S");
+ }
+ if (ai->aip_flags & IVF_LLC) {
+ strcat(flags, "L");
+ }
+ if (ai->aip_flags & IVF_MAPOK) {
+ strcat(flags, "M");
+ }
+ if (ai->aip_flags & IVF_NOIDLE) {
+ strcat(flags, "N");
+ }
+
+ /*
+ * Get the state of the VCC
+ */
+ for (i=0; ip_vcc_states[i].s_name != NULL &&
+ ai->aip_state != ip_vcc_states[i].s_id;
+ i++);
+ if (ip_vcc_states[i].s_name) {
+ state = ip_vcc_states[i].s_name;
+ } else {
+ state = "-";
+ }
+
+ /*
+ * Print the IP VCC information
+ */
+ printf("%-8s %9s %-7s %-5s %s\n",
+ ai->aip_intf,
+ vpi_vci,
+ state,
+ flags,
+ ip_addr);
+}
+
+
+/*
+ * Print network interface information
+ *
+ * Arguments:
+ * ni pointer to a struct air_int_rsp
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_netif_info(ni)
+ struct air_netif_rsp *ni;
+{
+ const char *ip_addr;
+ struct sockaddr_in *sain;
+
+ /*
+ * Print a header
+ */
+ if (!netif_hdr) {
+ netif_hdr++;
+ printf(NETIF_HDR);
+ }
+
+ /*
+ * Format the protocol address
+ */
+ sain = (struct sockaddr_in *)(void *)&ni->anp_proto_addr;
+ ip_addr = format_ip_addr(&sain->sin_addr);
+
+ /*
+ * Print the network interface information
+ */
+ printf("%-8s %-8s %s\n",
+ ni->anp_intf,
+ ni->anp_phy_intf,
+ ip_addr);
+}
+
+
+/*
+ * Print physical interface statistics
+ *
+ * Arguments:
+ * pi pointer to a struct air_phy_stat_rsp
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_intf_stats(pi)
+ struct air_phy_stat_rsp *pi;
+{
+ /*
+ * Print a header if it hasn't already been done
+ */
+ if (!phy_stats_hdr) {
+ printf(PHY_STATS_HDR);
+ phy_stats_hdr = 1;
+ }
+
+ /*
+ * Print the interface statistics
+ */
+ printf("%-9s %7lld %8lld %5lld %7lld %8lld %5lld %5lld\n",
+ pi->app_intf,
+ (unsigned long long)pi->app_ipdus,
+ (unsigned long long)pi->app_ibytes,
+ (unsigned long long)pi->app_ierrors,
+ (unsigned long long)pi->app_opdus,
+ (unsigned long long)pi->app_obytes,
+ (unsigned long long)pi->app_oerrors,
+ (unsigned long long)pi->app_cmderrors);
+}
+
+
+/*
+ * Print VCC statistics
+ *
+ * Arguments:
+ * vi pointer to VCC statistics to print
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_vcc_stats(vi)
+ struct air_vcc_rsp *vi;
+{
+
+ /*
+ * Print a header if it hasn't already been done
+ */
+ if (!vcc_stats_hdr) {
+ printf(VCC_STATS_HDR);
+ vcc_stats_hdr = 1;
+ }
+
+ /*
+ * Print the VCC statistics
+ */
+ printf("%-9s %3d %4d",
+ vi->avp_intf,
+ vi->avp_vpi,
+ vi->avp_vci);
+ if ( vi->avp_type & VCC_IN )
+ printf ( " %7ld %8ld %5ld",
+ vi->avp_ipdus,
+ vi->avp_ibytes,
+ vi->avp_ierrors);
+ else
+ printf ( " - - -" );
+
+ if ( vi->avp_type & VCC_OUT )
+ printf ( " %7ld %8ld %5ld\n",
+ vi->avp_opdus,
+ vi->avp_obytes,
+ vi->avp_oerrors);
+ else
+ printf ( " - - -\n" );
+}
+
+
+/*
+ * Print VCC information
+ *
+ * Arguments:
+ * vi pointer to a struct air_vcc_rsp
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_vcc_info(vi)
+ struct air_vcc_rsp *vi;
+{
+ int i;
+ const char *aal_name = "-" , *encaps_name = "-", *owner_name = "-";
+ const char *state_name = "-", *type_name = "-";
+ char dir_name[10];
+ const struct state *s_t;
+
+ /*
+ * Print a header if it hasn't already been done
+ */
+ if (!vcc_hdr) {
+ printf(VCC_HDR);
+ vcc_hdr = 1;
+ }
+
+ /*
+ * Translate AAL
+ */
+ for (i=0; aals[i].a_name != NULL; i++)
+ if (vi->avp_aal == aals[i].a_id)
+ break;
+ if (aals[i].a_name)
+ aal_name = aals[i].a_name;
+
+ /*
+ * Translate VCC type
+ */
+ if (vi->avp_type & VCC_PVC)
+ type_name = "PVC";
+ else if (vi->avp_type & VCC_SVC)
+ type_name = "SVC";
+ /*
+ * Translate VCC direction
+ */
+ bzero(dir_name, sizeof(dir_name));
+ if (vi->avp_type & VCC_IN)
+ strcat(dir_name, "In");
+ if (vi->avp_type & VCC_OUT)
+ strcat(dir_name, "Out");
+ if (strlen(dir_name) == 0)
+ strcpy(dir_name, "-");
+
+ /*
+ * Translate state
+ */
+ for (i=0; proto_states[i].p_state != NULL; i++)
+ if (vi->avp_sig_proto == proto_states[i].p_id)
+ break;
+ if (proto_states[i].p_state) {
+ s_t = proto_states[i].v_state;
+ for (i=0; s_t[i].s_name != NULL; i++)
+ if (vi->avp_state == s_t[i].s_id)
+ break;
+ if (s_t[i].s_name)
+ state_name = s_t[i].s_name;
+ }
+
+ /*
+ * Translate encapsulation
+ */
+ for (i=0; encaps[i].e_name != NULL; i++)
+ if (vi->avp_encaps == encaps[i].e_id)
+ break;
+ if (encaps[i].e_name)
+ encaps_name = encaps[i].e_name;
+
+ /*
+ * Print the VCC information
+ */
+ printf("%-9s %3d %5d %-4s %-4s %-5s %-8s %-8s ",
+ vi->avp_intf,
+ vi->avp_vpi,
+ vi->avp_vci,
+ aal_name,
+ type_name,
+ dir_name,
+ state_name,
+ encaps_name);
+
+ /*
+ * Print VCC owners' names
+ */
+ for (i = 0, owner_name = vi->avp_owners;
+ i < O_CNT - 1 && strlen(owner_name);
+ i++, owner_name += (T_ATM_APP_NAME_LEN + 1)) {
+ if (i > 0)
+ printf(", ");
+ printf("%s", owner_name);
+ }
+ if (i == 0)
+ printf("-");
+ printf("\n");
+
+ /*
+ * Print destination address if it's an SVC
+ */
+ if (vi->avp_type & VCC_SVC) {
+ printf(" Dest = %s\n",
+ format_atm_addr(&vi->avp_daddr));
+ }
+}
+
+
+/*
+ * Print network interface information
+ *
+ * Arguments:
+ * ni pointer to a struct air_int_rsp
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_version_info(vi)
+ struct air_version_rsp *vi;
+{
+ char version_str[80];
+
+ /*
+ * Print a header
+ */
+ if (!version_hdr) {
+ version_hdr++;
+ printf(VERSION_HDR);
+ }
+
+ /*
+ * Print the interface information
+ */
+ sprintf(version_str, "%d.%d",
+ ATM_VERS_MAJ(vi->avp_version),
+ ATM_VERS_MIN(vi->avp_version));
+ printf("%7s\n", version_str);
+}
diff --git a/sbin/atm/atm/atm_set.c b/sbin/atm/atm/atm_set.c
new file mode 100644
index 0000000..790bfc1
--- /dev/null
+++ b/sbin/atm/atm/atm_set.c
@@ -0,0 +1,528 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ */
+
+/*
+ * User configuration and display program
+ * --------------------------------------
+ *
+ * Routines for "set" subcommand
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "atm.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Process ATM ARP server set command
+ *
+ * Command format:
+ * atm set arpserver <interface_name> <atm-address> <IP prefix> ...
+ *
+ * Arguments:
+ * argc number of arguments to command
+ * argv pointer to argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+set_arpserver(int argc, char **argv, const struct cmd *cmdp __unused)
+{
+ int rc, s;
+ u_int i;
+ ssize_t len;
+ size_t prefix_len = 0;
+ char *intf;
+ Atm_addr server;
+ struct sockaddr_in *lis;
+ struct sockaddr_in if_mask;
+ struct atmsetreq asr;
+ struct atminfreq air;
+ struct air_netif_rsp *int_info;
+ struct {
+ struct in_addr ip_addr;
+ struct in_addr ip_mask;
+ } prefix_buf[64];;
+
+ /*
+ * Validate interface name
+ */
+ check_netif_name(argv[0]);
+ intf = argv[0];
+ argc--; argv++;
+
+ /*
+ * Get the ARP server's ATM address
+ */
+ bzero(&server, sizeof(server));
+ if (strcasecmp(argv[0], "local")) {
+ /*
+ * ARP server NSAP address is provided
+ */
+ server.address_format = T_ATM_ENDSYS_ADDR;
+ server.address_length = sizeof(Atm_addr_nsap);
+ if (get_hex_atm_addr(argv[0],
+ (u_char *)server.address,
+ sizeof(Atm_addr_nsap)) !=
+ sizeof(Atm_addr_nsap)) {
+ fprintf(stderr, "%s: Invalid ARP server address\n",
+ prog);
+ exit(1);
+ }
+ if (argc > 1) {
+ fprintf(stderr, "%s: Invalid number of arguments\n",
+ prog);
+ exit(1);
+ }
+ prefix_len = 0;
+ } else {
+ argc--; argv++;
+
+ /*
+ * This host is the ARP server
+ */
+ server.address_format = T_ATM_ABSENT;
+ server.address_length = 0;
+
+ /*
+ * Get interface information from the kernel. We need
+ * to get the IP address and the subnet mask associated
+ * with the network interface and insert them into the
+ * list of permitted LIS prefixes.
+ */
+ bzero(&air, sizeof(air));
+ air.air_opcode = AIOCS_INF_NIF;
+ strcpy(air.air_int_intf, intf);
+ len = do_info_ioctl(&air, sizeof(struct air_netif_rsp));
+ if (len == -1) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ perror("Internal error");
+ break;
+ case ENXIO:
+ fprintf(stderr, "%s is not an ATM device\n",
+ intf);
+ break;
+ default:
+ perror("ioctl (AIOCINFO)");
+ break;
+ }
+ exit(1);
+ }
+ int_info = (struct air_netif_rsp *) air.air_buf_addr;
+ lis = (struct sockaddr_in *)(void *)&int_info->anp_proto_addr;
+ prefix_buf[0].ip_addr = lis->sin_addr;
+ free(int_info);
+
+ rc = get_subnet_mask(intf, &if_mask);
+ if (rc) {
+ fprintf(stderr, "%s: Can't get subnet mask for %s\n",
+ prog, intf);
+ }
+ prefix_buf[0].ip_mask = if_mask.sin_addr;
+ prefix_buf[0].ip_addr.s_addr &=
+ prefix_buf[0].ip_mask.s_addr;
+
+ /*
+ * Get the prefixes of the LISs that we'll support
+ */
+ for (i = 1; argc; i++, argc--, argv++) {
+ rc = parse_ip_prefix(argv[0],
+ (struct in_addr *)&prefix_buf[i]);
+ if (rc != 0) {
+ fprintf(stderr, "%s: Invalid IP prefix value \'%s\'\n",
+ prog, argv[0]);
+ exit(1);
+ }
+ }
+
+ /*
+ * Compress the prefix list
+ */
+ prefix_len = compress_prefix_list((struct in_addr *)prefix_buf,
+ i * sizeof(struct in_addr) * 2);
+ }
+
+ /*
+ * Build ioctl request
+ */
+ bzero(&asr, sizeof(asr));
+ asr.asr_opcode = AIOCS_SET_ASV;
+ strncpy(asr.asr_arp_intf, intf, sizeof(asr.asr_arp_intf));
+ asr.asr_arp_addr = server;
+ asr.asr_arp_subaddr.address_format = T_ATM_ABSENT;
+ asr.asr_arp_subaddr.address_length = 0;
+ if (prefix_len)
+ asr.asr_arp_pbuf = (caddr_t)prefix_buf;
+ else
+ asr.asr_arp_pbuf = (caddr_t)0;
+ asr.asr_arp_plen = prefix_len;
+
+ /*
+ * Pass the new ARP server address to the kernel
+ */
+ s = socket(AF_ATM, SOCK_DGRAM, 0);
+ if (s < 0) {
+ sock_error(errno);
+ }
+ if (ioctl(s, AIOCSET, (caddr_t)&asr) < 0) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case EOPNOTSUPP:
+ case EPROTONOSUPPORT:
+ perror("Internal error");
+ break;
+ case EINVAL:
+ fprintf(stderr, "Invalid parameter\n");
+ break;
+ case ENOMEM:
+ fprintf(stderr, "Kernel memory exhausted\n");
+ break;
+ case ENETDOWN:
+ fprintf(stderr, "ATM network is inoperable\n");
+ break;
+ case EPERM:
+ fprintf(stderr, "Must be super user to use set subcommand\n");
+ break;
+ case ENXIO:
+ fprintf(stderr, "%s is not an ATM interface\n", intf);
+ break;
+ case ENOENT:
+ fprintf(stderr, "Signalling manager not attached\n");
+ break;
+ case ENOPROTOOPT:
+ fprintf(stderr,
+ "%s does not have an IP address configured\n",
+ intf);
+ break;
+ default:
+ perror("Ioctl (AIOCSET) ARPSERVER address");
+ break;
+ }
+ exit(1);
+ }
+
+ (void)close(s);
+}
+
+
+/*
+ * Process set MAC address command
+ *
+ * Command format:
+ * atm set mac <interface_name> <MAC address>
+ *
+ * Arguments:
+ * argc number of remaining arguments to command
+ * argv pointer to remaining argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+set_macaddr(int argc, char **argv, const struct cmd *cmdp __unused)
+{
+ int s;
+ char *intf;
+ struct mac_addr mac;
+ struct atmsetreq asr;
+
+ /*
+ * Validate interface name
+ */
+ if (strlen(argv[0]) > sizeof(asr.asr_mac_intf) - 1) {
+ fprintf(stderr, "%s: Illegal interface name\n", prog);
+ exit(1);
+ }
+ intf = argv[0];
+ argc--; argv++;
+
+ /*
+ * Get the MAC address provided by the user
+ */
+ if (get_hex_atm_addr(argv[0], (u_char *)&mac, sizeof(mac)) !=
+ sizeof(mac)) {
+ fprintf(stderr, "%s: Invalid MAC address\n", prog);
+ exit(1);
+ }
+
+ /*
+ * Build ioctl request
+ */
+ asr.asr_opcode = AIOCS_SET_MAC;
+ strncpy(asr.asr_mac_intf, intf, sizeof(asr.asr_mac_intf));
+ bcopy(&mac, &asr.asr_mac_addr, sizeof(asr.asr_mac_addr));
+
+ /*
+ * Pass the new address to the kernel
+ */
+ s = socket(AF_ATM, SOCK_DGRAM, 0);
+ if (s < 0) {
+ sock_error(errno);
+ }
+ if (ioctl(s, AIOCSET, (caddr_t)&asr) < 0) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case EOPNOTSUPP:
+ case EPROTONOSUPPORT:
+ perror("Internal error");
+ break;
+ case EADDRINUSE:
+ fprintf(stderr, "Interface must be detached to set MAC addres\n");
+ break;
+ case EINVAL:
+ fprintf(stderr, "Invalid parameter\n");
+ break;
+ case ENOMEM:
+ fprintf(stderr, "Kernel memory exhausted\n");
+ break;
+ case ENETDOWN:
+ fprintf(stderr, "ATM network is inoperable\n");
+ break;
+ case EPERM:
+ fprintf(stderr, "Must be super user to use set subcommand\n");
+ break;
+ case ENXIO:
+ fprintf(stderr, "%s is not an ATM device\n",
+ argv[0]);
+ break;
+ default:
+ perror("Ioctl (AIOCSET) MAC address");
+ break;
+ }
+ exit(1);
+ }
+
+ (void)close(s);
+}
+
+
+/*
+ * Process network interface set command
+ *
+ * Command format:
+ * atm set netif <interface_name> <prefix_name> <count>
+ *
+ * Arguments:
+ * argc number of arguments to command
+ * argv pointer to argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+set_netif(int argc, char **argv, const struct cmd *cmdp __unused)
+{
+ struct atmsetreq anr;
+ char str[16];
+ char *cp;
+ int s;
+ u_long nifs;
+
+ /*
+ * Set IOCTL opcode
+ */
+ anr.asr_opcode = AIOCS_SET_NIF;
+
+ /*
+ * Validate interface name
+ */
+ if (strlen(argv[0]) > sizeof(anr.asr_nif_intf) - 1) {
+ fprintf(stderr, "%s: Illegal interface name\n", prog);
+ exit(1);
+ }
+ strcpy(anr.asr_nif_intf, argv[0]);
+ argc--; argv++;
+
+ /*
+ * Validate network interface name prefix
+ */
+ if ((strlen(argv[0]) > sizeof(anr.asr_nif_pref) - 1) ||
+ (strpbrk(argv[0], "0123456789"))) {
+ fprintf(stderr, "%s: Illegal network interface prefix\n", prog);
+ exit(1);
+ }
+ strcpy(anr.asr_nif_pref, argv[0]);
+ argc--; argv++;
+
+ /*
+ * Validate interface count
+ */
+ errno = 0;
+ nifs = strtoul(argv[0], &cp, 0);
+ if (errno != 0 || *cp != '\0' || nifs > MAX_NIFS) {
+ fprintf(stderr, "%s: Invalid interface count\n", prog);
+ exit(1);
+ }
+ anr.asr_nif_cnt = nifs;
+
+ /*
+ * Make sure the resulting name won't be too long
+ */
+ sprintf(str, "%lu", nifs - 1);
+ if ((strlen(str) + strlen(anr.asr_nif_pref)) >
+ sizeof(anr.asr_nif_intf) - 1) {
+ fprintf(stderr, "%s: Network interface prefix too long\n", prog);
+ exit(1);
+ }
+
+ /*
+ * Tell the kernel to do it
+ */
+ s = socket(AF_ATM, SOCK_DGRAM, 0);
+ if (s < 0) {
+ sock_error(errno);
+ }
+ if (ioctl(s, AIOCSET, (caddr_t)&anr) < 0) {
+ fprintf(stderr, "%s: ", prog);
+ perror("ioctl (AIOCSET) set NIF");
+ exit(1);
+ }
+ (void)close(s);
+}
+
+
+/*
+ * Process set NSAP prefix command
+ *
+ * Command format:
+ * atm set nsap <interface_name> <NSAP prefix>
+ *
+ * Arguments:
+ * argc number of remaining arguments to command
+ * argv pointer to remaining argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+set_prefix(int argc, char **argv, const struct cmd *cmdp __unused)
+{
+ int s;
+ char *intf;
+ u_char pfx[13];
+ struct atmsetreq asr;
+
+ /*
+ * Validate interface name
+ */
+ if (strlen(argv[0]) > sizeof(asr.asr_prf_intf) - 1) {
+ fprintf(stderr, "%s: Illegal interface name\n", prog);
+ exit(1);
+ }
+ intf = argv[0];
+ argc--; argv++;
+
+ /*
+ * Get the prefix provided by the user
+ */
+ if (get_hex_atm_addr(argv[0], pfx, sizeof(pfx)) != sizeof(pfx)) {
+ fprintf(stderr, "%s: Invalid NSAP prefix\n", prog);
+ exit(1);
+ }
+
+ /*
+ * Build ioctl request
+ */
+ asr.asr_opcode = AIOCS_SET_PRF;
+ strncpy(asr.asr_prf_intf, intf, sizeof(asr.asr_prf_intf));
+ bcopy(pfx, asr.asr_prf_pref, sizeof(asr.asr_prf_pref));
+
+ /*
+ * Pass the new prefix to the kernel
+ */
+ s = socket(AF_ATM, SOCK_DGRAM, 0);
+ if (s < 0) {
+ sock_error(errno);
+ }
+ if (ioctl(s, AIOCSET, (caddr_t)&asr) < 0) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case EOPNOTSUPP:
+ case EPROTONOSUPPORT:
+ perror("Internal error");
+ break;
+ case EALREADY:
+ fprintf(stderr, "NSAP prefix is already set\n");
+ break;
+ case EINVAL:
+ fprintf(stderr, "Invalid parameter\n");
+ break;
+ case ENOMEM:
+ fprintf(stderr, "Kernel memory exhausted\n");
+ break;
+ case ENETDOWN:
+ fprintf(stderr, "ATM network is inoperable\n");
+ break;
+ case EPERM:
+ fprintf(stderr, "Must be super user to use set subcommand\n");
+ break;
+ case ENXIO:
+ fprintf(stderr, "%s is not an ATM device\n",
+ argv[0]);
+ break;
+ default:
+ perror("Ioctl (AIOCSET) NSAP prefix");
+ break;
+ }
+ exit(1);
+ }
+
+ (void)close(s);
+}
diff --git a/sbin/atm/atm/atm_show.c b/sbin/atm/atm/atm_show.c
new file mode 100644
index 0000000..e744c75
--- /dev/null
+++ b/sbin/atm/atm/atm_show.c
@@ -0,0 +1,1144 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ */
+
+/*
+ * User configuration and display program
+ * --------------------------------------
+ *
+ * Routines for "show" subcommand
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_vc.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "atm.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Local functions
+ */
+static int vcc_compare(const void *, const void *);
+static int ip_vcc_compare(const void *, const void *);
+static int arp_compare(const void *, const void *);
+
+
+/*
+ * Process show ARP command
+ *
+ * Command format:
+ * atm show ARP [<ip-addr>]
+ *
+ * Arguments:
+ * argc number of remaining arguments to command
+ * argv pointer to remaining argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+show_arp(int argc, char **argv, const struct cmd *cmdp __unused)
+{
+ size_t arp_info_len;
+ struct atminfreq air;
+ struct air_arp_rsp *arp_info, *arp_info_base;
+ struct sockaddr_in *sain;
+ union {
+ struct sockaddr_in sain;
+ struct sockaddr sa;
+ } host_addr;
+
+ /*
+ * Get IP address of specified host name
+ */
+ bzero(&host_addr, sizeof(host_addr));
+ host_addr.sa.sa_family = AF_INET;
+ if (argc) {
+ sain = get_ip_addr(argv[0]);
+ if (!sain) {
+ fprintf(stderr, "%s: host \'%s\' not found\n",
+ prog, argv[0]);
+ exit(1);
+ }
+ host_addr.sain.sin_addr.s_addr = sain->sin_addr.s_addr;
+ } else {
+ host_addr.sain.sin_addr.s_addr = INADDR_ANY;
+ }
+
+ /*
+ * Get ARP information from the kernel
+ */
+ bzero(&air, sizeof(air));
+ air.air_opcode = AIOCS_INF_ARP;
+ air.air_arp_addr = host_addr.sa;
+ arp_info_len = do_info_ioctl(&air, sizeof(struct air_arp_rsp) * 10);
+ if ((ssize_t)arp_info_len == -1) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ perror("Internal error");
+ break;
+ case ENXIO:
+ fprintf(stderr, "not an ATM device\n");
+ break;
+ default:
+ perror("ioctl (AIOCINFO)");
+ break;
+ }
+ exit(1);
+ }
+ arp_info_base = arp_info =
+ (struct air_arp_rsp *) air.air_buf_addr;
+
+ /*
+ * Sort the ARP table
+ */
+ qsort((void *) arp_info,
+ arp_info_len / sizeof(struct air_arp_rsp),
+ sizeof(struct air_arp_rsp),
+ arp_compare);
+
+ /*
+ * Print the relevant information
+ */
+ while (arp_info_len >= sizeof(struct air_arp_rsp)) {
+ print_arp_info(arp_info);
+ arp_info++;
+ arp_info_len -= sizeof(struct air_arp_rsp);
+ }
+
+ /*
+ * Release the information from the kernel
+ */
+ free(arp_info_base);
+}
+
+
+/*
+ * Process show ATM ARP server command
+ *
+ * Command format:
+ * atm show arpserver [<interface-name>]
+ *
+ * Arguments:
+ * argc number of remaining arguments to command
+ * argv pointer to remaining argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+show_arpserv(int argc, char **argv, const struct cmd *cmdp __unused)
+{
+ size_t buf_len, asrv_info_len;
+ struct atminfreq air;
+ struct air_asrv_rsp *asrv_info, *asrv_info_base;
+
+ /*
+ * Validate interface name
+ */
+ bzero(air.air_int_intf, sizeof(air.air_int_intf));
+ if (argc) {
+ if (strlen(argv[0]) > IFNAMSIZ - 1) {
+ fprintf(stderr, "%s: Illegal interface name\n",
+ prog);
+ exit(1);
+ }
+ strcpy(air.air_int_intf, argv[0]);
+ argc--; argv++;
+ }
+
+ /*
+ * Get interface information from the kernel
+ */
+ air.air_opcode = AIOCS_INF_ASV;
+ buf_len = do_info_ioctl(&air, sizeof(struct air_asrv_rsp) * 3);
+ if ((ssize_t)buf_len == -1) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ perror("Internal error");
+ break;
+ case ENXIO:
+ fprintf(stderr, "%s is not an ATM device\n",
+ argv[0]);
+ break;
+ default:
+ perror("ioctl (AIOCINFO)");
+ break;
+ }
+ exit(1);
+ }
+
+ /*
+ * Print the interface information
+ */
+ asrv_info_base = asrv_info =
+ (struct air_asrv_rsp *)(void *)air.air_buf_addr;
+ while (buf_len >= sizeof(struct air_asrv_rsp)) {
+ print_asrv_info(asrv_info);
+ asrv_info_len = sizeof(struct air_asrv_rsp) +
+ asrv_info->asp_nprefix * sizeof(struct in_addr) * 2;
+ asrv_info = (struct air_asrv_rsp *)(void *)
+ ((char *)asrv_info + asrv_info_len);
+ buf_len -= asrv_info_len;
+ }
+ free(asrv_info_base);
+}
+
+
+/*
+ * Process show ATM adapter configuration command
+ *
+ * Command format:
+ * atm show config [<interface-name>]
+ *
+ * Arguments:
+ * argc number of remaining arguments to command
+ * argv pointer to remaining argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+show_config(int argc, char **argv, const struct cmd *cmdp __unused)
+{
+ size_t buf_len;
+ struct atminfreq air;
+ struct air_cfg_rsp *cfg_info, *cfg_info_base;
+
+ /*
+ * Validate interface name
+ */
+ bzero(air.air_cfg_intf, sizeof(air.air_cfg_intf));
+ if (argc) {
+ if (strlen(argv[0]) > IFNAMSIZ - 1) {
+ fprintf(stderr, "%s: Illegal interface name\n",
+ prog);
+ exit(1);
+ }
+ strcpy(air.air_cfg_intf, argv[0]);
+ argc--; argv++;
+ }
+
+ /*
+ * Get configuration information from the kernel
+ */
+ air.air_opcode = AIOCS_INF_CFG;
+ buf_len = do_info_ioctl(&air, sizeof(struct air_asrv_rsp) * 3);
+ if ((ssize_t)buf_len == -1) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ perror("Internal error");
+ break;
+ case ENXIO:
+ fprintf(stderr, "%s is not an ATM device\n",
+ argv[0]);
+ break;
+ default:
+ perror("ioctl (AIOCINFO)");
+ break;
+ }
+ exit(1);
+ }
+
+ /*
+ * Print the interface information
+ */
+ cfg_info_base = cfg_info =
+ (struct air_cfg_rsp *)(void *)air.air_buf_addr;
+ for (; buf_len >= sizeof(struct air_cfg_rsp); cfg_info++,
+ buf_len -= sizeof(struct air_cfg_rsp)) {
+ print_cfg_info(cfg_info);
+ }
+ free(cfg_info_base);
+}
+
+
+/*
+ * Process show interface command
+ *
+ * Command format:
+ * atm show interface [<interface-name>]
+ *
+ * Arguments:
+ * argc number of remaining arguments to command
+ * argv pointer to remaining argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+show_intf(int argc, char **argv, const struct cmd *cmdp __unused)
+{
+ size_t buf_len;
+ struct atminfreq air;
+ struct air_int_rsp *int_info, *int_info_base;
+
+ /*
+ * Validate interface name
+ */
+ bzero(&air, sizeof(air));
+ if (argc) {
+ if (strlen(argv[0]) > IFNAMSIZ - 1) {
+ fprintf(stderr, "%s: Illegal interface name\n",
+ prog);
+ exit(1);
+ }
+ strcpy(air.air_int_intf, argv[0]);
+ argc--; argv++;
+ }
+
+ /*
+ * Get interface information from the kernel
+ */
+ air.air_opcode = AIOCS_INF_INT;
+ buf_len = do_info_ioctl(&air, sizeof(struct air_int_rsp) * 3);
+ if ((ssize_t)buf_len == -1) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ perror("Internal error");
+ break;
+ case ENXIO:
+ fprintf(stderr, "%s is not an ATM device\n",
+ argv[0]);
+ break;
+ default:
+ perror("ioctl (AIOCINFO)");
+ break;
+ }
+ exit(1);
+ }
+
+ /*
+ * Print the interface information
+ */
+ int_info_base = int_info =
+ (struct air_int_rsp *)(void *)air.air_buf_addr;
+ for (; buf_len >= sizeof(struct air_int_rsp); int_info++,
+ buf_len -= sizeof(struct air_int_rsp)) {
+ print_intf_info(int_info);
+ }
+ free(int_info_base);
+}
+
+
+/*
+ * Process show IP VCCs command
+ *
+ * Command format:
+ * atm show ipvcc [<ip-addr>]
+ *
+ * Arguments:
+ * argc number of remaining arguments to command
+ * argv pointer to remaining argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+show_ip_vcc(int argc, char **argv, const struct cmd *cmdp __unused)
+{
+ int rc;
+ size_t ip_info_len;
+ char *if_name = (char *)0;
+ struct atminfreq air;
+ struct air_ip_vcc_rsp *ip_info, *ip_info_base;
+ struct sockaddr_in *sain;
+ union {
+ struct sockaddr_in sain;
+ struct sockaddr sa;
+ } host_addr;
+
+ /*
+ * First parameter can be a netif name, an IP host name, or
+ * an IP address. Figure out which it is.
+ */
+ bzero(&host_addr, sizeof(host_addr));
+ host_addr.sa.sa_family = AF_INET;
+ if (argc) {
+ rc = verify_nif_name(argv[0]);
+ if (rc < 0) {
+ /*
+ * Error occured
+ */
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ perror("Internal error");
+ break;
+ case ENXIO:
+ fprintf(stderr, "%s is not an ATM device\n",
+ argv[0]);
+ break;
+ default:
+ perror("ioctl (AIOCINFO)");
+ break;
+ }
+ exit(1);
+ } else if (rc > 0) {
+ /*
+ * Parameter is a valid netif name
+ */
+ if_name = argv[0];
+ } else {
+ /*
+ * Get IP address of specified host name
+ */
+ sain = get_ip_addr(argv[0]);
+ host_addr.sain.sin_addr.s_addr =
+ sain->sin_addr.s_addr;
+ }
+ } else {
+ host_addr.sain.sin_addr.s_addr = INADDR_ANY;
+ }
+
+ /*
+ * Get IP map information from the kernel
+ */
+ air.air_opcode = AIOCS_INF_IPM;
+ air.air_ip_addr = host_addr.sa;
+ ip_info_len = do_info_ioctl(&air, sizeof(struct air_ip_vcc_rsp) * 10);
+ if ((ssize_t)ip_info_len == -1) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ perror("Internal error");
+ break;
+ case ENXIO:
+ fprintf(stderr, "not an ATM device\n");
+ break;
+ default:
+ perror("ioctl (AIOCINFO)");
+ break;
+ }
+ exit(1);
+ }
+ ip_info_base = ip_info =
+ (struct air_ip_vcc_rsp *)(void *)air.air_buf_addr;
+
+ /*
+ * Sort the information
+ */
+ qsort((void *) ip_info,
+ ip_info_len / sizeof(struct air_ip_vcc_rsp),
+ sizeof(struct air_ip_vcc_rsp),
+ ip_vcc_compare);
+
+ /*
+ * Print the relevant information
+ */
+ while (ip_info_len >= sizeof(struct air_ip_vcc_rsp)) {
+ if (!if_name || !strcmp(if_name, ip_info->aip_intf)) {
+ print_ip_vcc_info(ip_info);
+ }
+ ip_info++;
+ ip_info_len -= sizeof(struct air_ip_vcc_rsp);
+ }
+
+ /*
+ * Release the information from the kernel
+ */
+ free(ip_info_base);
+}
+
+
+/*
+ * Process show network interface command
+ *
+ * Command format:
+ * atm show netif [<netif>]
+ *
+ * Arguments:
+ * argc number of remaining arguments to command
+ * argv pointer to remaining argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+show_netif(int argc, char **argv, const struct cmd *cmdp __unused)
+{
+ size_t buf_len;
+ struct atminfreq air;
+ struct air_netif_rsp *int_info, *int_info_base;
+
+ /*
+ * Validate network interface name
+ */
+ bzero(air.air_int_intf, sizeof(air.air_int_intf));
+ if (argc) {
+ if (strlen(argv[0]) > IFNAMSIZ - 1) {
+ fprintf(stderr, "%s: Illegal interface name\n", prog);
+ exit(1);
+ }
+ strcpy(air.air_int_intf, argv[0]);
+ argc--; argv++;
+ }
+
+ /*
+ * Get network interface information from the kernel
+ */
+ air.air_opcode = AIOCS_INF_NIF;
+ buf_len = do_info_ioctl(&air, sizeof(struct air_netif_rsp) * 3);
+ if ((ssize_t)buf_len == -1) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ perror("Internal error");
+ break;
+ case ENXIO:
+ fprintf(stderr, "%s is not an ATM device\n",
+ argv[0]);
+ break;
+ default:
+ perror("ioctl (AIOCINFO)");
+ break;
+ }
+ exit(1);
+ }
+
+ /*
+ * Print the network interface information
+ */
+ int_info_base = int_info =
+ (struct air_netif_rsp *) air.air_buf_addr;
+ for (; buf_len >= sizeof(struct air_netif_rsp); int_info++,
+ buf_len -= sizeof(struct air_netif_rsp)) {
+ print_netif_info(int_info);
+ }
+ free(int_info_base);
+}
+
+
+/*
+ * Process interface statistics command
+ *
+ * Command format:
+ * atm show stats interface [<interface-name>]
+ *
+ * Arguments:
+ * argc number of remaining arguments to command
+ * argv pointer to remaining argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+show_intf_stats(int argc, char **argv, const struct cmd *cmdp __unused)
+{
+ size_t buf_len;
+ char intf[IFNAMSIZ];
+ struct atminfreq air;
+ struct air_phy_stat_rsp *pstat_info, *pstat_info_base;
+ struct air_cfg_rsp *cfg_info;
+
+ /*
+ * Validate interface name
+ */
+ bzero(intf, sizeof(intf));
+ if (argc) {
+ if (strlen(argv[0]) > IFNAMSIZ - 1) {
+ fprintf(stderr, "%s: Illegal interface name\n",
+ prog);
+ exit(1);
+ }
+ strcpy(intf, argv[0]);
+ argc--; argv++;
+ }
+
+ /*
+ * If there are parameters remaining, the request is for
+ * vendor-specific adaptor statistics
+ */
+ if (argc) {
+ /*
+ * Get adapter configuration information
+ */
+ air.air_opcode = AIOCS_INF_CFG;
+ strcpy(air.air_cfg_intf, intf);
+ buf_len = do_info_ioctl(&air, sizeof(struct air_cfg_rsp));
+ if ((ssize_t)buf_len == -1) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ perror("Internal error");
+ break;
+ case ENXIO:
+ fprintf(stderr, "%s is not an ATM device\n",
+ intf);
+ break;
+ default:
+ perror("ioctl (AIOCINFO)");
+ break;
+ }
+ exit(1);
+ }
+ cfg_info = (struct air_cfg_rsp *)(void *)air.air_buf_addr;
+
+ /*
+ * Call the appropriate vendor-specific routine
+ */
+ switch(cfg_info->acp_vendor) {
+ case VENDOR_FORE:
+ show_fore200_stats(intf, argc, argv);
+ break;
+ default:
+ fprintf(stderr, "%s: Unknown adapter vendor\n",
+ prog);
+ break;
+ }
+
+ free(cfg_info);
+ } else {
+ /*
+ * Get generic interface statistics
+ */
+ air.air_opcode = AIOCS_INF_PIS;
+ strcpy(air.air_physt_intf, intf);
+ buf_len = do_info_ioctl(&air,
+ sizeof(struct air_phy_stat_rsp) * 3);
+ if ((ssize_t)buf_len == -1) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ perror("Internal error");
+ break;
+ case ENXIO:
+ fprintf(stderr, "%s is not an ATM device\n",
+ intf);
+ break;
+ default:
+ perror("ioctl (AIOCINFO)");
+ break;
+ }
+ exit(1);
+ }
+
+ /*
+ * Display the interface statistics
+ */
+ pstat_info_base = pstat_info = (struct air_phy_stat_rsp *)
+ (void *)air.air_buf_addr;
+ for (; buf_len >= sizeof(struct air_phy_stat_rsp);
+ pstat_info++,
+ buf_len-=sizeof(struct air_phy_stat_rsp)) {
+ print_intf_stats(pstat_info);
+ }
+ free((caddr_t)pstat_info_base);
+ }
+}
+
+
+/*
+ * Process VCC statistics command
+ *
+ * Command format:
+ * atm show stats VCC [<interface-name> [<vpi> [<vci>]]]
+ *
+ * Arguments:
+ * argc number of remaining arguments to command
+ * argv pointer to remaining argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+show_vcc_stats(int argc, char **argv, const struct cmd *cmdp __unused)
+{
+ size_t vcc_info_len;
+ int vpi = -1, vci = -1;
+ char *cp, *intf = NULL;
+ struct air_vcc_rsp *vcc_info, *vcc_info_base;
+
+ /*
+ * Validate interface name
+ */
+ if (argc) {
+ if (strlen(argv[0]) > IFNAMSIZ - 1) {
+ fprintf(stderr, "%s: Illegal interface name\n",
+ prog);
+ exit(1);
+ }
+ intf = argv[0];
+ argc--; argv++;
+ }
+
+ /*
+ * Validate VPI value
+ */
+ if (argc) {
+ vpi = strtol(argv[0], &cp, 0);
+ if ((*cp != '\0') || (vpi < 0) || (vpi >= 1 << 8)) {
+ fprintf(stderr, "%s: Invalid VPI value\n", prog);
+ exit(1);
+ }
+ argc--; argv++;
+ }
+
+ /*
+ * Validate VCI value
+ */
+ if (argc) {
+ vci = strtol(argv[0], &cp, 0);
+ if ((*cp != '\0') || (vci <= 0) || (vci >= 1 << 16)) {
+ fprintf(stderr, "%s: Invalid VCI value\n",
+ prog);
+ exit(1);
+ }
+ argc--; argv++;
+ }
+
+ /*
+ * Get VCC information
+ */
+ vcc_info_len = get_vcc_info(intf, &vcc_info);
+ if (vcc_info_len == 0)
+ exit(1);
+ else if ((ssize_t)vcc_info_len == -1) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ perror("Internal error");
+ break;
+ case ENXIO:
+ fprintf(stderr, "Not an ATM device\n");
+ break;
+ default:
+ perror("ioctl (AIOCINFO)");
+ break;
+ }
+ exit(1);
+ }
+
+ /*
+ * Sort the VCC information
+ */
+ qsort((void *) vcc_info,
+ vcc_info_len / sizeof(struct air_vcc_rsp),
+ sizeof(struct air_vcc_rsp),
+ vcc_compare);
+
+ /*
+ * Display the VCC statistics
+ */
+ vcc_info_base = vcc_info;
+ for (; vcc_info_len >= sizeof(struct air_vcc_rsp);
+ vcc_info_len-=sizeof(struct air_vcc_rsp),
+ vcc_info++) {
+ if (vpi != -1 && vcc_info->avp_vpi != vpi)
+ continue;
+ if (vci != -1 && vcc_info->avp_vci != vci)
+ continue;
+ print_vcc_stats(vcc_info);
+ }
+ free(vcc_info_base);
+}
+
+
+/*
+ * Process VCC information command
+ *
+ * Command format:
+ * atm show VCC [<interface-name> [<vpi> [<vci>] | PVC | SVC]]
+ *
+ * Arguments:
+ * argc number of remaining arguments to command
+ * argv pointer to remaining argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+show_vcc(int argc, char **argv, const struct cmd *cmdp __unused)
+{
+ size_t vcc_info_len;
+ int vpi = -1, vci = -1, show_pvc = 0, show_svc = 0;
+ char *cp, *intf = NULL;
+ struct air_vcc_rsp *vcc_info, *vcc_info_base;
+
+ /*
+ * Validate interface name
+ */
+ if (argc) {
+ if (strlen(argv[0]) > IFNAMSIZ - 1) {
+ fprintf(stderr, "%s: Illegal interface name\n",
+ prog);
+ exit(1);
+ }
+ intf = argv[0];
+ argc--; argv++;
+ }
+
+ /*
+ * Validate VPI value
+ */
+ if (argc) {
+ if (strcasecmp(argv[0], "pvc"))
+ show_pvc = 1;
+ else if (strcasecmp(argv[0], "svc"))
+ show_svc = 1;
+ else {
+ vpi = strtol(argv[0], &cp, 0);
+ if ((*cp != '\0') || (vpi < 0) ||
+ (vpi >= 1 << 8)) {
+ fprintf(stderr, "%s: Invalid VPI value\n", prog);
+ exit(1);
+ }
+ }
+ argc--; argv++;
+ }
+
+ /*
+ * Validate VCI value
+ */
+ if (argc) {
+ vci = strtol(argv[0], &cp, 0);
+ if ((*cp != '\0') || (vci <= 0) || (vci >= 1 << 16)) {
+ fprintf(stderr, "%s: Invalid VCI value\n",
+ prog);
+ exit(1);
+ }
+ argc--; argv++;
+ }
+
+ /*
+ * Get VCC information
+ */
+ vcc_info_len = get_vcc_info(intf, &vcc_info);
+ if (vcc_info_len == 0)
+ exit(1);
+ else if ((ssize_t)vcc_info_len == -1) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ perror("Internal error");
+ break;
+ case ENXIO:
+ fprintf(stderr, "Not an ATM device\n");
+ break;
+ default:
+ perror("ioctl (AIOCINFO)");
+ break;
+ }
+ exit(1);
+ }
+
+ /*
+ * Sort the VCC information
+ */
+ qsort((void *) vcc_info,
+ vcc_info_len/sizeof(struct air_vcc_rsp),
+ sizeof(struct air_vcc_rsp),
+ vcc_compare);
+
+ /*
+ * Display the VCC information
+ */
+ vcc_info_base = vcc_info;
+ for (; vcc_info_len >= sizeof(struct air_vcc_rsp);
+ vcc_info_len-=sizeof(struct air_vcc_rsp),
+ vcc_info++) {
+ if (vpi != -1 && vcc_info->avp_vpi != vpi)
+ continue;
+ if (vci != -1 && vcc_info->avp_vci != vci)
+ continue;
+ if (show_pvc && vcc_info->avp_type & VCC_PVC)
+ continue;
+ if (show_svc && vcc_info->avp_type & VCC_SVC)
+ continue;
+ print_vcc_info(vcc_info);
+ }
+ free(vcc_info_base);
+}
+
+
+/*
+ * Process version command
+ *
+ * Command format:
+ * atm show version
+ *
+ * Arguments:
+ * argc number of remaining arguments to command
+ * argv pointer to remaining argument strings
+ * cmdp pointer to command description
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+show_version(int argc __unused, char **argv __unused,
+ const struct cmd *cmdp __unused)
+{
+ size_t buf_len;
+ struct atminfreq air;
+ struct air_version_rsp *ver_info, *ver_info_base;
+
+ /*
+ * Get network interface information from the kernel
+ */
+ air.air_opcode = AIOCS_INF_VER;
+ buf_len = do_info_ioctl(&air, sizeof(struct air_version_rsp));
+ if ((ssize_t)buf_len == -1) {
+ fprintf(stderr, "%s: ", prog);
+ switch (errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ perror("Internal error");
+ break;
+ case ENXIO:
+ fprintf(stderr, "Not an ATM device\n");
+ break;
+ default:
+ perror("ioctl (AIOCINFO)");
+ break;
+ }
+ exit(1);
+ }
+
+ /*
+ * Print the network interface information
+ */
+ ver_info_base = ver_info =
+ (struct air_version_rsp *)(void *)air.air_buf_addr;
+ for (; buf_len >= sizeof(struct air_version_rsp); ver_info++,
+ buf_len -= sizeof(struct air_version_rsp)) {
+ print_version_info(ver_info);
+ }
+ free(ver_info_base);
+}
+
+
+/*
+ * Comparison function for qsort
+ *
+ * Arguments:
+ * p1 pointer to the first VCC response
+ * p2 pointer to the second VCC response
+ *
+ * Returns:
+ * int a number less than, greater than, or equal to zero,
+ * depending on whether *p1 is less than, greater than, or
+ * equal to *p2
+ *
+ */
+static int
+vcc_compare(p1, p2)
+ const void *p1, *p2;
+{
+ int rc;
+ const struct air_vcc_rsp *c1, *c2;
+
+ c1 = (const struct air_vcc_rsp *) p1;
+ c2 = (const struct air_vcc_rsp *) p2;
+
+ /*
+ * Compare the interface names
+ */
+ rc = strcmp(c1->avp_intf, c2->avp_intf);
+ if (rc)
+ return(rc);
+
+ /*
+ * Compare the VPI values
+ */
+ rc = c1->avp_vpi - c2->avp_vpi;
+ if (rc)
+ return(rc);
+
+ /*
+ * Compare the VCI values
+ */
+ rc = c1->avp_vci - c2->avp_vci;
+ if (rc)
+ return(rc);
+
+ /*
+ * Compare the types
+ */
+ rc = c1->avp_type - c2->avp_type;
+ return(rc);
+}
+
+
+/*
+ * Comparison function for qsort
+ *
+ * Arguments:
+ * p1 pointer to the first VCC response
+ * p2 pointer to the second VCC response
+ *
+ * Returns:
+ * int a number less than, greater than, or equal to zero,
+ * depending on whether *p1 is less than, greater than, or
+ * equal to *p2
+ *
+ */
+static int
+ip_vcc_compare(p1, p2)
+ const void *p1, *p2;
+{
+ int rc;
+ const struct air_ip_vcc_rsp *c1, *c2;
+
+ c1 = (const struct air_ip_vcc_rsp *) p1;
+ c2 = (const struct air_ip_vcc_rsp *) p2;
+
+ /*
+ * Compare the interface names
+ */
+ rc = strcmp(c1->aip_intf, c2->aip_intf);
+ if (rc)
+ return(rc);
+
+ /*
+ * Compare the VPI values
+ */
+ rc = c1->aip_vpi - c2->aip_vpi;
+ if (rc)
+ return(rc);
+
+ /*
+ * Compare the VCI values
+ */
+ rc = c1->aip_vci - c2->aip_vci;
+ return(rc);
+}
+
+
+/*
+ * Comparison function for qsort
+ *
+ * Arguments:
+ * p1 pointer to the first ARP or IP map entry
+ * p2 pointer to the second ARP or IP map entry
+ *
+ * Returns:
+ * int a number less than, greater than, or equal to zero,
+ * depending on whether *p1 is less than, greater than, or
+ * equal to *p2
+ *
+ */
+static int
+arp_compare(p1, p2)
+ const void *p1, *p2;
+{
+ int rc;
+ const struct air_arp_rsp *c1, *c2;
+ const struct sockaddr_in *sin1, *sin2;
+
+ c1 = (const struct air_arp_rsp *)p1;
+ c2 = (const struct air_arp_rsp *)p2;
+ sin1 = (const struct sockaddr_in *)(const void *)&c1->aap_arp_addr;
+ sin2 = (const struct sockaddr_in *)(const void *)&c2->aap_arp_addr;
+
+ /*
+ * Compare the IP addresses
+ */
+ if ((rc = sin1->sin_family - sin2->sin_family) != 0)
+ return(rc);
+ if ((rc = sin1->sin_addr.s_addr - sin2->sin_addr.s_addr) != 0)
+ return(rc);
+
+ /*
+ * Compare the ATM addresses
+ */
+ if ((rc = c1->aap_addr.address_format - c2->aap_addr.address_format) != 0)
+ return(rc);
+ if ((rc = c1->aap_addr.address_length - c2->aap_addr.address_length) != 0)
+ return(rc);
+ switch(c1->aap_addr.address_format) {
+ case T_ATM_ABSENT:
+ rc = 0;
+ break;
+ case T_ATM_ENDSYS_ADDR:
+ rc = bcmp(c1->aap_addr.address, c2->aap_addr.address,
+ sizeof(Atm_addr_nsap));
+ break;
+ case T_ATM_E164_ADDR:
+ rc = bcmp(c1->aap_addr.address, c2->aap_addr.address,
+ sizeof(Atm_addr_e164));
+ break;
+ case T_ATM_SPANS_ADDR:
+ rc = bcmp(c1->aap_addr.address, c2->aap_addr.address,
+ sizeof(Atm_addr_spans));
+ break;
+ }
+
+ return(rc);
+}
diff --git a/sbin/atm/atm/atm_subr.c b/sbin/atm/atm/atm_subr.c
new file mode 100644
index 0000000..a612bee
--- /dev/null
+++ b/sbin/atm/atm/atm_subr.c
@@ -0,0 +1,626 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ */
+
+/*
+ * User configuration and display program
+ * --------------------------------------
+ *
+ * General subroutines
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "atm.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Table entry definition
+ */
+typedef struct {
+ int type;
+ const char *name;
+} tbl_ent;
+
+
+/*
+ * Table to translate vendor codes to ASCII
+ */
+static const tbl_ent vendors[] = {
+ { VENDOR_UNKNOWN, "Unknown" },
+ { VENDOR_FORE, "Fore" },
+ { VENDOR_ENI, "ENI" },
+ { VENDOR_IDT, "IDT" },
+ { VENDOR_PROSUM, "ProSum" },
+ { VENDOR_NETGRAPH, "Netgraph" },
+ { 0, 0 },
+};
+
+
+/*
+ * Table to translate adapter codes to ASCII
+ */
+static const tbl_ent adapter_types[] = {
+ { DEV_UNKNOWN, "Unknown" },
+ { DEV_FORE_SBA200E, "SBA-200E" },
+ { DEV_FORE_SBA200, "SBA-200" },
+ { DEV_FORE_PCA200E, "PCA-200E" },
+ { DEV_FORE_ESA200E, "ESA-200E" },
+ { DEV_ENI_155P, "ENI-155p" },
+ { DEV_IDT_155, "IDT" },
+ { DEV_PROATM_25, "PROATM-25" },
+ { DEV_PROATM_155, "PROATM-155" },
+ { DEV_VATMPIF, "VATMPIF" },
+ { DEV_FORE_LE25, "ForeLE-25" },
+ { DEV_FORE_LE155, "ForeLE-155" },
+ { DEV_IDT_25, "NICStAR-25" },
+ { DEV_IDTABR_25, "IDT77252-25" },
+ { DEV_IDTABR_155, "IDT77252-155" },
+ { DEV_FORE_HE155, "ForeHE-155" },
+ { DEV_FORE_HE622, "ForeHE-622" },
+ { 0, 0 },
+};
+
+/*
+ * Table to translate medium types to ASCII
+ */
+static const tbl_ent media_types[] = {
+ { MEDIA_UNKNOWN, "Unknown" },
+ { MEDIA_TAXI_100, "100 Mbps 4B/5B" },
+ { MEDIA_TAXI_140, "140 Mbps 4B/5B" },
+ { MEDIA_OC3C, "OC-3c" },
+ { MEDIA_OC12C, "OC-12c" },
+ { MEDIA_UTP155, "155 Mbps UTP" },
+ { MEDIA_UTP25, "25.6 Mbps UTP" },
+ { MEDIA_VIRTUAL, "Virtual Link" },
+ { MEDIA_DSL, "xDSL" },
+ { 0, 0 },
+};
+
+/*
+ * Table to translate bus types to ASCII
+ */
+static const tbl_ent bus_types[] = {
+ { BUS_UNKNOWN, "Unknown" },
+ { BUS_SBUS_B16, "SBus" },
+ { BUS_SBUS_B32, "SBus" },
+ { BUS_PCI, "PCI" },
+ { BUS_EISA, "EISA" },
+ { BUS_USB, "USB" },
+ { BUS_VIRTUAL, "Virtual" },
+ { 0, 0 },
+};
+
+
+/*
+ * Get interface vendor name
+ *
+ * Return a character string with a vendor name, given a vendor code.
+ *
+ * Arguments:
+ * vendor vendor ID
+ *
+ * Returns:
+ * char * pointer to a string with the vendor name
+ *
+ */
+const char *
+get_vendor(int vendor)
+{
+ int i;
+
+ for(i=0; vendors[i].name; i++) {
+ if (vendors[i].type == vendor)
+ return(vendors[i].name);
+ }
+
+ return("-");
+}
+
+
+/*
+ * Get adapter type
+ *
+ * Arguments:
+ * dev adapter code
+ *
+ * Returns:
+ * char * pointer to a string with the adapter type
+ *
+ */
+const char *
+get_adapter(int dev)
+{
+ int i;
+
+ for(i=0; adapter_types[i].name; i++) {
+ if (adapter_types[i].type == dev)
+ return(adapter_types[i].name);
+ }
+
+ return("-");
+}
+
+
+/*
+ * Get communication medium type
+ *
+ * Arguments:
+ * media medium code
+ *
+ * Returns:
+ * char * pointer to a string with the name of the medium
+ *
+ */
+const char *
+get_media_type(int media)
+{
+ int i;
+
+ for(i=0; media_types[i].name; i++) {
+ if (media_types[i].type == media)
+ return(media_types[i].name);
+ }
+
+ return("-");
+}
+
+
+/*
+ * Get bus type
+ *
+ * Arguments:
+ * bus bus type code
+ *
+ * Returns:
+ * char * pointer to a string with the bus type
+ *
+ */
+const char *
+get_bus_type(int bus)
+{
+ int i;
+
+ for(i=0; bus_types[i].name; i++) {
+ if (bus_types[i].type == bus)
+ return(bus_types[i].name);
+ }
+
+ return("-");
+}
+
+
+/*
+ * Get adapter ID
+ *
+ * Get a string giving the adapter's vendor and type.
+ *
+ * Arguments:
+ * intf interface name
+ *
+ * Returns:
+ * char * pointer to a string identifying the adapter
+ *
+ */
+const char *
+get_adapter_name(const char *intf)
+{
+ size_t buf_len;
+ struct atminfreq air;
+ struct air_cfg_rsp *cfg;
+ static char name[256];
+
+ /*
+ * Initialize
+ */
+ bzero(&air, sizeof(air));
+ bzero(name, sizeof(name));
+
+ /*
+ * Get configuration information from the kernel
+ */
+ air.air_opcode = AIOCS_INF_CFG;
+ strcpy(air.air_cfg_intf, intf);
+ buf_len = do_info_ioctl(&air, sizeof(struct air_cfg_rsp));
+ if (buf_len < sizeof(struct air_cfg_rsp))
+ return("-");
+ cfg = (struct air_cfg_rsp *)(void *)air.air_buf_addr;
+
+ /*
+ * Build a string describing the adapter
+ */
+ strcpy(name, get_vendor(cfg->acp_vendor));
+ strcat(name, " ");
+ strcat(name, get_adapter(cfg->acp_device));
+
+ free(cfg);
+
+ return(name);
+}
+
+
+/*
+ * Format a MAC address into a string
+ *
+ * Arguments:
+ * addr pointer to a MAC address
+ *
+ * Returns:
+ * the address of a string representing the MAC address
+ *
+ */
+const char *
+format_mac_addr(const Mac_addr *addr)
+{
+ static char str[256];
+
+ /*
+ * Check for null pointer
+ */
+ if (!addr)
+ return("-");
+
+ /*
+ * Clear the returned string
+ */
+ bzero(str, sizeof(str));
+
+ /*
+ * Format the address
+ */
+ sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
+ addr->ma_data[0],
+ addr->ma_data[1],
+ addr->ma_data[2],
+ addr->ma_data[3],
+ addr->ma_data[4],
+ addr->ma_data[5]);
+
+ return(str);
+}
+
+
+/*
+ * Parse an IP prefix designation in the form nnn.nnn.nnn.nnn/mm
+ *
+ * Arguments:
+ * cp pointer to prefix designation string
+ * op pointer to a pair of in_addrs for the result
+ *
+ * Returns:
+ * 0 success
+ * -1 prefix was invalid
+ *
+ */
+int
+parse_ip_prefix(const char *cp, struct in_addr *op)
+{
+ int len;
+ char *mp;
+ struct in_addr ip_addr;
+
+ static u_long masks[33] = {
+ 0x0,
+ 0x80000000,
+ 0xc0000000,
+ 0xe0000000,
+ 0xf0000000,
+ 0xf8000000,
+ 0xfc000000,
+ 0xfe000000,
+ 0xff000000,
+ 0xff800000,
+ 0xffc00000,
+ 0xffe00000,
+ 0xfff00000,
+ 0xfff80000,
+ 0xfffc0000,
+ 0xfffe0000,
+ 0xffff0000,
+ 0xffff8000,
+ 0xffffc000,
+ 0xffffe000,
+ 0xfffff000,
+ 0xfffff800,
+ 0xfffffc00,
+ 0xfffffe00,
+ 0xffffff00,
+ 0xffffff80,
+ 0xffffffc0,
+ 0xffffffe0,
+ 0xfffffff0,
+ 0xfffffff8,
+ 0xfffffffc,
+ 0xfffffffe,
+ 0xffffffff
+ };
+
+ /*
+ * Find the slash that marks the start of the mask
+ */
+ mp = strchr(cp, '/');
+ if (mp) {
+ *mp = '\0';
+ mp++;
+ }
+
+ /*
+ * Convert the IP-address part of the prefix
+ */
+ ip_addr.s_addr = inet_addr(cp);
+ if (ip_addr.s_addr == INADDR_NONE)
+ return(-1);
+
+ /*
+ * Set the default mask length
+ */
+ if (IN_CLASSA(ntohl(ip_addr.s_addr)))
+ len = 8;
+ else if (IN_CLASSB(ntohl(ip_addr.s_addr)))
+ len = 16;
+ else if (IN_CLASSC(ntohl(ip_addr.s_addr)))
+ len = 24;
+ else
+ return(-1);
+
+ /*
+ * Get the mask length
+ */
+ if (mp) {
+ len = atoi(mp);
+ if (len < 1 || len > 32)
+ return(-1);
+ }
+
+ /*
+ * Select the mask and copy the IP address into the
+ * result buffer, ANDing it with the mask
+ */
+ op[1].s_addr = htonl(masks[len]);
+ op[0].s_addr = ip_addr.s_addr & op[1].s_addr;
+
+ return(0);
+}
+
+
+/*
+ * Compress a list of IP network prefixes
+ *
+ * Arguments:
+ * ipp pointer to list of IP address/mask pairs
+ * ipc length of list
+ *
+ * Returns:
+ * length of compressed list
+ *
+ */
+size_t
+compress_prefix_list(struct in_addr *ipp, size_t ilen)
+{
+ u_int i, j, n;
+ struct in_addr *ip1, *ip2, *m1, *m2;
+
+ /*
+ * Figure out how many pairs there are
+ */
+ n = ilen / (sizeof(struct in_addr) * 2);
+
+ /*
+ * Check each pair of address/mask pairs to make sure
+ * none contains the other
+ */
+ for (i = 0; i < n; i++) {
+ ip1 = &ipp[i*2];
+ m1 = &ipp[i*2+1];
+
+ /*
+ * If we've already eliminated this address,
+ * skip the checks
+ */
+ if (ip1->s_addr == 0)
+ continue;
+
+ /*
+ * Try all possible second members of the pair
+ */
+ for (j = i + 1; j < n; j++) {
+ ip2 = &ipp[j*2];
+ m2 = &ipp[j*2+1];
+
+ /*
+ * If we've already eliminated the second
+ * address, just skip the checks
+ */
+ if (ip2->s_addr == 0)
+ continue;
+
+ /*
+ * Compare the address/mask pairs
+ */
+ if (m1->s_addr == m2->s_addr) {
+ /*
+ * Masks are equal
+ */
+ if (ip1->s_addr == ip2->s_addr) {
+ ip2->s_addr = 0;
+ m2->s_addr = 0;
+ }
+ } else if (ntohl(m1->s_addr) <
+ ntohl(m2->s_addr)) {
+ /*
+ * m1 is shorter
+ */
+ if ((ip2->s_addr & m1->s_addr) ==
+ ip1->s_addr) {
+ ip2->s_addr = 0;
+ m2->s_addr = 0;
+ }
+ } else {
+ /*
+ * m1 is longer
+ */
+ if ((ip1->s_addr & m2->s_addr) ==
+ ip2->s_addr) {
+ ip1->s_addr = 0;
+ m1->s_addr = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * Now pull up the list, eliminating zeroed entries
+ */
+ for (i = 0, j = 0; i < n; i++) {
+ ip1 = &ipp[i*2];
+ m1 = &ipp[i*2+1];
+ ip2 = &ipp[j*2];
+ m2 = &ipp[j*2+1];
+ if (ip1->s_addr != 0) {
+ if (i != j) {
+ *ip2 = *ip1;
+ *m2 = *m1;
+ }
+ j++;
+ }
+ }
+
+ return(j * sizeof(struct in_addr) * 2);
+}
+
+
+/*
+ * Make sure a user-supplied parameter is a valid network interface
+ * name
+ *
+ * When a socket call fails, print an error message and exit
+ *
+ * Arguments:
+ * nif pointer to network interface name
+ *
+ * Returns:
+ * none exits if name is not valid
+ *
+ */
+void
+check_netif_name(const char *nif)
+{
+ int rc;
+
+ /*
+ * Look up the name in the kernel
+ */
+ rc = verify_nif_name(nif);
+
+ /*
+ * Check the result
+ */
+ if (rc > 0) {
+ /*
+ * Name is OK
+ */
+ return;
+ } else if (rc == 0) {
+ /*
+ * Name is not valid
+ */
+ fprintf(stderr, "%s: Invalid network interface name %s\n",
+ prog, nif);
+ } else {
+ /*
+ * Error performing IOCTL
+ */
+ fprintf(stderr, "%s: ", prog);
+ switch(errno) {
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ perror("Internal error");
+ break;
+ case ENXIO:
+ fprintf(stderr, "%s is not an ATM device\n",
+ nif);
+ break;
+ default:
+ perror("ioctl (AIOCINFO)");
+ break;
+ }
+ }
+
+ exit(1);
+}
+
+
+/*
+ * Socket error handler
+ *
+ * When a socket call fails, print an error message and exit
+ *
+ * Arguments:
+ * err an errno describing the error
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+sock_error(int err)
+{
+ fprintf(stderr, "%s: ", prog);
+
+ switch (err) {
+
+ case EPROTONOSUPPORT:
+ fprintf(stderr, "ATM protocol support not loaded\n");
+ break;
+
+ default:
+ perror("socket");
+ break;
+ }
+
+ exit(1);
+}
diff --git a/sbin/atm/atmconfig/Makefile b/sbin/atm/atmconfig/Makefile
new file mode 100644
index 0000000..1db0fa4
--- /dev/null
+++ b/sbin/atm/atmconfig/Makefile
@@ -0,0 +1,46 @@
+# Copyright (c) 2001-2003
+# Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+# All rights reserved.
+# Author: Harti Brandt <brandt@fokus.gmd.de>
+#
+# $FreeBSD$
+
+PROG= atmconfig
+.ifndef RESCUE
+SRCS= ${.OBJDIR}/oid.h
+.endif
+SRCS+= main.c diag.c natm.c
+.ifndef RESCUE
+SRCS+= atmconfig_device.c
+.endif
+MAN= atmconfig.8
+# CFLAGS+= -DPATH_HELP='".:/usr/share/doc/atm:/usr/local/share/doc/atm"'
+
+CFLAGS+= -I${.OBJDIR}
+
+.ifndef RESCUE
+DPADD= ${LIBBSNMP}
+LDADD= -lbsnmp
+.endif
+
+.ifndef RESCUE
+CLEANFILES+= oid.h
+.endif
+
+.if ${MACHINE_ARCH} == "arm"
+WARNS?= 3
+.else
+WARNS?= 6
+.endif
+
+FILES= atmconfig.help atmconfig_device.help
+FILESDIR= /usr/share/doc/atm
+
+SNMP_ATM_DEF= ${.CURDIR}/../../../contrib/ngatm/snmp_atm/atm_tree.def \
+ ${.CURDIR}/../../../usr.sbin/bsnmpd/modules/snmp_atm/atm_freebsd.def
+
+${.OBJDIR}/oid.h: atm_oid.list ${SNMP_ATM_DEF}
+ cat ${SNMP_ATM_DEF} | gensnmptree -e `tail -n +2 ${.CURDIR}/atm_oid.list` \
+ > ${.OBJDIR}/oid.h
+
+.include <bsd.prog.mk>
diff --git a/sbin/atm/atmconfig/atm_oid.list b/sbin/atm/atmconfig/atm_oid.list
new file mode 100644
index 0000000..ca64afa
--- /dev/null
+++ b/sbin/atm/atmconfig/atm_oid.list
@@ -0,0 +1,20 @@
+# $FreeBSD$
+begemotAtmIfTable
+begemotAtmIfName
+begemotAtmIfNodeId
+begemotAtmIfPcr
+begemotAtmIfMedia
+begemotAtmIfVpiBits
+begemotAtmIfVciBits
+begemotAtmIfMaxVpcs
+begemotAtmIfMaxVccs
+begemotAtmIfEsi
+begemotAtmIfCarrierStatus
+begemotAtmIfMode
+begemotAtmIfTableLastChange
+begemotAtmHWTable
+begemotAtmHWVendor
+begemotAtmHWDevice
+begemotAtmHWSerial
+begemotAtmHWVersion
+begemotAtmHWSoftVersion
diff --git a/sbin/atm/atmconfig/atmconfig.8 b/sbin/atm/atmconfig/atmconfig.8
new file mode 100644
index 0000000..1ff76a1
--- /dev/null
+++ b/sbin/atm/atmconfig/atmconfig.8
@@ -0,0 +1,319 @@
+.\"
+.\" Copyright (c) 2001-2003
+.\" Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+.\" 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.
+.\"
+.\" Author: Hartmut Brandt <harti@FreeBSD.org>
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 11, 2003
+.Dt ATMCONFIG 8
+.Os
+.Sh NAME
+.Nm atmconfig
+.Nd "ATM configuration tool"
+.Sh SYNOPSIS
+.Nm
+.Op Fl htv
+.Op Ar command Op Ar sub-command Op ...
+.Op Ar options
+.Op Ar arg ...
+.Sh DESCRIPTION
+The
+.Nm
+tool is used to configure the Netgraph ATM network sub-system.
+.Pp
+The command line of
+.Nm
+generally consists of common options followed by a command string, optionally
+followed by sub-command strings, optional command specific options and
+command specific arguments.
+Commands and sub-commands as well as command
+specific options may be abbreviated as
+long as there is only one match possible.
+.Ss Common Options
+The following common options change the overall behaviour of
+.Nm :
+.Bl -tag -width indent
+.It Fl h
+Print a very short usage info and exit.
+.It Fl t
+Several show-type commands output a header and then several lines
+of information.
+If this option is given, the header is omitted, simplifying the parsing
+of the output.
+.It Fl v
+Be more verbose.
+.El
+.Ss Obtaining Help
+The
+.Ic help
+command has a number of useful sub-commands.
+.Pp
+To get general help use:
+.D1 Nm Ic help
+.Pp
+To get a list of available commands use:
+.D1 Nm Ic help Cm commands
+.Pp
+To get a list of available sub-commands use:
+.D1 Nm Ic help Ar command
+.Pp
+or (if there are deeper levels of sub-commands):
+.D1 Nm Ic help Ar command sub-command ...
+.Pp
+To get a list of options and arguments for a command use:
+.D1 Nm Ic help Ar command sub-command ...
+(given that there are no further sub-command levels).
+.Pp
+To get a list of common options use:
+.D1 Nm Ic help Cm options
+.Ss The Ic diag Ss Command
+The
+.Ic diag
+command allows the inspection of the ATM interfaces on the local host
+and the modification of device parameters.
+Sub-commands are:
+.Cm list
+(print a list of interfaces),
+.Cm config
+(print hardware configuration),
+.Cm phy
+(access PHY chip),
+.Cm stats
+(print statistics) and
+.Cm vcc
+(print list of VCCs).
+.Bl -tag -width indent
+.\"----------------------------------------
+.It Nm Ic diag Cm list
+This sub-command lists all ATM interfaces in the system.
+It takes no options or arguments.
+.\"----------------------------------------
+.It Xo
+.Nm Ic diag Cm config
+.Op Fl atm
+.Op Fl hardware
+.Op Ar device ...
+.Xc
+This command prints the configuration of ATM interfaces.
+If no
+.Ar device
+is given, all devices are listed, otherwise only the specified devices.
+The option
+.Fl atm
+instructs the command to print ATM layer configuration parameters like
+the number of VCI and VPI bits, whereas the
+.Fl hardware
+option requests card specific information like the vendor or the serial
+number.
+If none of the options is given, the defaults is to assume
+.Fl atm .
+.\"----------------------------------------
+.It Xo
+.Nm Ic diag Cm phy print
+.Op Fl numeric
+.Ar device
+.Xc
+This command prints the PHY registers in a (potential)
+human comprehensible format.
+If
+.Fl numeric
+is given, the format is hex bytes.
+Otherwise, textual representation will be printed.
+.\"----------------------------------------
+.It Nm Ic diag Cm phy show Op Ar device ...
+This sub-command prints static information about the PHY device used
+in the ATM card like the type of the PHY and the media.
+.\"----------------------------------------
+.It Xo
+.Nm Ic diag Cm phy set
+.Ar device
+.Ar reg
+.Ar mask
+.Ar val
+.Xc
+This sub-command allows one to change bits in PHY registers.
+This should be used with great care.
+The bits of the given PHY chip register for which the corresponding bit in
+.Ar mask
+is one are set to the values of the corresponding bits in
+.Ar val .
+All register bits that have a zero in
+.Ar mask
+are written back with their original value.
+.\"----------------------------------------
+.It Xo
+.Nm Ic diag Cm phy stats
+.Op Fl clear
+.Ar device
+.Xc
+Print the PHY statistics for the given
+.Ar device .
+When the optional
+.Fl clear
+is given, the statistics are cleared atomically.
+.\"----------------------------------------
+.It Xo
+.Nm Ic diag Cm vcc
+.Op Fl abr
+.Op Fl channel
+.Op Fl traffic
+.Op Ar device
+.Xc
+Retrieve the list of currently active channels on either all
+or the specified interfaces.
+For each channel, the following information is printed depending
+on the options (default is
+.Fl channel ) .
+.Bl -tag -width ".Fl traffic"
+.It Fl abr
+Print ABR specific traffic parameters: ICR, TBE, NRM, TRM, ADTF, RIF, RDF,
+CDF.
+.It Fl channel
+Print basic information: VPI, VCI, AAL, traffic type, MTU and flags.
+.It Fl traffic
+Print traffic parameters: PCR, SCR, MBS, MCR.
+.El
+.\"----------------------------------------
+.It Nm Ic diag Cm stats Ar device
+Print driver specific statistics.
+.El
+.Ss The Ic natm Ss Command
+The
+.Ic natm
+command is used to change
+.Xr natmip 4
+routes on the local host.
+The sub-commands for the routing table are:
+.Cm add
+(to add a new route),
+.Cm delete
+(to delete an existing route) and
+.Cm show
+(to print the currently installed NATM routes).
+.Pp
+.Bl -tag -width indent -compact
+.\"----------------------------------------
+.It Xo
+.Nm Ic natm Cm add
+.Ar dest
+.Ar device
+.Ar vpi
+.Ar vci
+.Ar encaps
+.Xc
+.It Xo
+.Nm Ic natm Cm add
+.Ar dest
+.Ar device
+.Ar vpi
+.Ar vci
+.Ar encaps
+.Cm ubr Op Ar pcr
+.Xc
+.It Xo
+.Nm Ic natm Cm add
+.Ar dest
+.Ar device
+.Ar vpi
+.Ar vci
+.Ar encaps
+.Cm cbr Ar pcr
+.Xc
+.It Xo
+.Nm Ic natm Cm add
+.Ar dest
+.Ar device
+.Ar vpi
+.Ar vci
+.Ar encaps
+.Cm vbr Ar pcr scr mbs
+.Xc
+.It Xo
+.Nm Ic natm Cm add
+.Ar dest
+.Ar device
+.Ar vpi
+.Ar vci
+.Ar encaps
+.Cm abr Ar pcr mcr icr tbe nrm trm adtf rif rdf cdf
+.Xc
+Add a new route to the routing table.
+The destination address (the address
+on the other end of the link) is given in
+.Ar dest .
+The
+.Ar device ,
+.Ar vpi
+and
+.Ar vci
+arguments
+are the name of the ATM device and the VPI and VCI values for the link.
+The
+.Ar encaps
+argument
+may be either
+.Cm AAL5
+or
+.Cm LLC/SNAP
+both of which specify AAL5 encapsulation, the first one without additional
+encapsulation, the second one with LLC/SNAP headers.
+The first two forms of the command add an UBR (unspecified bit rate) channel,
+where the second form allows the optional specification of a peak cell
+rate (PCR).
+The third form adds a CBR (constant bit rate) channel where a PCR
+must be given.
+The fourth form adds a VBR (variable bit rate) channel.
+The arguments are the peak cell rate, the sustainable cell rate and the
+maximum bursts size.
+The last form of the command adds an ABR (available bit rate) channel.
+.\"----------------------------------------
+.Pp
+.It Nm Ic natm Cm delete Ar dest
+.It Xo
+.Nm Ic natm Cm delete
+.Ar device
+.Ar vpi
+.Ar vci
+.Xc
+This commands deletes an NATM route.
+The route may be specified either by the destination address or
+by the
+.Ar device , vpi
+and
+.Ar vci
+triple.
+.\"----------------------------------------
+.Pp
+.It Nm Ic natm Cm show
+List all NATM routes.
+.El
+.Sh SEE ALSO
+.Xr natm 4 ,
+.Xr natmip 4 ,
+.Xr atm 8
+.Sh AUTHORS
+.An Hartmut Brandt Aq harti@FreeBSD.org
diff --git a/sbin/atm/atmconfig/atmconfig.h b/sbin/atm/atmconfig/atmconfig.h
new file mode 100644
index 0000000..5e5b041
--- /dev/null
+++ b/sbin/atm/atmconfig/atmconfig.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * 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.
+ *
+ * Author: Hartmut Brandt <harti@freebsd.org>
+ *
+ * $FreeBSD$
+ */
+#ifndef _ATMCONFIG_H
+#define _ATMCONFIG_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <netgraph/ng_message.h>
+
+#define DEFAULT_INTERFACE "hatm0"
+
+struct cmdtab {
+ const char *string;
+ const struct cmdtab *sub;
+ void (*func)(int, char *[]);
+};
+
+/*
+ * client configuration info
+ */
+struct amodule {
+ const struct cmdtab *cmd;
+};
+
+#define DEF_MODULE(CMDTAB) \
+struct amodule amodule_1 = { CMDTAB }
+
+/* for compiled-in modules */
+void register_module(const struct amodule *);
+
+/* print a message if we are verbose */
+void verb(const char *, ...) __printflike(1, 2);
+
+/* print heading */
+void heading(const char *, ...) __printflike(1, 2);
+
+/* before starting output */
+void heading_init(void);
+
+/* stringify an enumerated value */
+struct penum {
+ int32_t value;
+ const char *str;
+};
+const char *penum(int32_t value, const struct penum *strtab, char *buf);
+int pparse(int32_t *, const struct penum *, const char *);
+
+enum {
+ OPT_NONE,
+ OPT_UINT,
+ OPT_INT,
+ OPT_UINT32,
+ OPT_INT32,
+ OPT_UINT64,
+ OPT_INT64,
+ OPT_FLAG,
+ OPT_VCI,
+ OPT_STRING,
+ OPT_SIMPLE,
+};
+struct option {
+ const char *optstr;
+ int opttype;
+ void *optarg;
+};
+
+int parse_options(int *_pargc, char ***_pargv,
+ const struct option *_opts);
+
+/* XXX while this is compiled in */
+void device_register(void);
+
+#endif /* _ATMCONFIG_H */
diff --git a/sbin/atm/atmconfig/atmconfig.help b/sbin/atm/atmconfig/atmconfig.help
new file mode 100644
index 0000000..8c6e7ce
--- /dev/null
+++ b/sbin/atm/atmconfig/atmconfig.help
@@ -0,0 +1,223 @@
+#
+# Copyright (c) 2001-2003
+# Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+# All rights reserved.
+# Copyright (c) 2004
+# Hartmut Brandt.
+# All rights reserved.
+#
+# Author: Hartmut Brandt <harti@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.
+#
+# Help file for the atmconfig utility
+#
+# $FreeBSD$
+#
+^0 intro
+ATM configuration utility.
+usage:
+ atmconfig [common-options] command [subcommand] [options]
+
+Use 'atmconfig help' for general help or 'atmconfig help <command>' for
+help on 'command' or 'atmconfig help commands' for a list of commands.
+
+^0 help
+^^ help show help information
+Use one of the following commands to get help on atmconfig:
+
+ atmconfig help options
+ gives you help on common command line options
+ atmconfig help commands
+ prints a list of available commands (and help items)
+ atmconfig help <command>
+ prints help on the given command (including a list of subcommands)
+ atmconfig help <command> <subcommand>
+ gives help on the given subcommand
+
+^0 options
+^^ help options list common options
+Common command line options can be specified for all commands. They
+are written immediately before the command. The following options are
+available:
+
+ -h print short help
+ -t don't print headings for 'show'-type commands
+ -v be verbose about all actions.
+
+^0 commands
+^^ help commands show available commands
+The following commands are available:
+
+$MAIN
+
+^0 diag
+^^ diag show/modify ATM hardware interfaces
+This command shows information about the ATM hardware interfaces in the
+system. A list of ATM devices is obtained by:
+
+ atmconfig [common-options] diag list
+
+Information about the hardware configuration of the ATM interfaces is
+reported by:
+
+ atmconfig [common-options] diag config [options] [<device> ...]
+
+The phy chip can be access with:
+
+ atmconfig [common-options] diag phy print [options] <device>
+ atmconfig [common-options] diag phy show <device>
+ atmconfig [common-options] diag phy set <device> <reg> <msk> <val>
+ atmconfig [common-options] diag phy set stats [options] <device>
+
+A list of open VCCs can be obtained with:
+
+ atmconfig [common-options] diag vcc [<device> ...]
+
+Driver internal statistics are printed with
+
+ atmconfig [common-options] diag stats <device>
+
+^1 list
+usage: atmconfig [common-options] diag list
+
+List all known ATM devices in the system.
+
+^1 config
+usage: atmconfig [common-options] diag config [-hardware] [-atm] [device ...]
+options:
+ -hardware print hardware related information
+ -atm print ATM related information
+
+If now device is given as argument information about all devices is shown.
+The default is to print only ATM related information.
+
+^1 phy
+To show the type of the PHY and its state:
+
+ atmconfig [common-options] diag phy show <device>
+
+Change a PHY register (use with care):
+
+ atmconfig [common-options] diag phy set <device> <reg> <msk> <val>
+
+Print the PHY registers in a human readable form:
+
+ atmconfig [common-options] diag phy print [-numeric] <device>
+
+The PHY statistics can be printed with:
+
+ atmconfig [common-options] diag phy stats [-clear] <device>
+
+^2 show
+usage: atmconfig [common-options] diag phy show <device>
+
+Show configuration and state information about the PHY chip on the given
+ATM interface.
+
+^2 set
+usage: atmconfig [common-options] diag phy set <device> <reg> <msk> <val>
+
+Set the bits of given PHY chip register for which the corresponding bit in
+<msk> is one to the value of the corresponding bit in <val>. All register
+bits that have a zero in <msk> are written back with there original value.
+
+^2 print
+usage: atmconfig [common-options] diag phy print [-numeric] <device>
+options:
+ -numeric print registers in hex
+
+Print the registers of the PHY chip in a human readable format.
+
+^2 stats
+usage: atmconfig [common-options] diag phy stats [-clear] <device>
+options:
+ -clear clear the statistics atomically after reading them
+
+Prints the PHY layer statistics of the PHY chip and optionally clears them.
+
+^1 vcc
+usage: atmconfig [common-options] diag vcc [-abr] [-channel] [-traffic]
+ [<device> ...]
+options:
+ -abr print ABR specific traffic parameters
+ -channel print VPI, VCI, AAL, traffic type and flags (default)
+ -traffic print traffic parameters
+
+Prints a list of all open vccs. The default output is -channel.
+
+^1 stats
+usage: atmconfig [common-options] diag stats <device>
+
+Prints the driver-internal statistics.
+
+^0 natm
+^^ natm simple IP over ATM management (see natmip(4))
+The group of CLIP commands is used to manage classical IP over ATM
+networking via NATM (see natm(4) and natmip(4)). A new PVC is added
+to a CLIP via:
+
+ atmconfig [common-options] natm add <dest> <device> <vpi> <vci>
+ <encaps> [<traffic> [<params> ...]]
+
+The PVC can be deleted with:
+
+ atmconfig [common-options] natm del <device> <vpi> <vci>
+
+The list of PVC that are currently active is retrieved with:
+
+ atmconfig [common-options] natm show
+
+^1 add
+usage: atmconfig [common-options] natm add [-printonly] <dest> <device>
+ <vpi> <vci> <encaps> [<traffic> [<params> ...]]
+options:
+ -printonly don't execute, print the route(8) command
+
+This subcommand adds a new CLIP PVC on the ATM interface <device>. The
+host on the other end of the PVC has IP address <addr>. <encaps> is one
+of llc/snap (LLC/SNAP encapsulated frames in AAL5) or aal5 (AAL5 frames
+without LLC/SNAP). <traffic> specifies the traffic type of the PVC
+and is one of UBR, CBR, VBR or ABR. If not given UBR is assumed. Depending
+on the traffic type none or more parameters can follow:
+
+ ubr [<pcr>]
+ cbr <pcr>
+ vbr <pcr> <scr> <mbs>
+ abr <pcr> <mcr> <icr> <tbe> <nrm> <trm> <adtf> <rif> <rdf> <cdf>
+
+^1 delete
+usage: atmconfig [common-options] natm delete [-printonly] <dest>
+ or: atmconfig [common-options] natm delete [-printonly] <device> <vpi> <vci>
+options:
+ -printonly don't execute, print the route(8) command
+
+This subcommand deletes and existing CLIP PVC that can bei either identified
+by the destination address or by the <device><vpi><vci> triple.
+
+^1 show
+usage: atmconfig [common-options] natm show [-abr] [-numeric]
+options:
+ -abr show ABR parameters for ABR connections
+ -numeric print IP addresses numerically
+
+This subcommand prints all ATM routes.
diff --git a/sbin/atm/atmconfig/atmconfig_device.c b/sbin/atm/atmconfig/atmconfig_device.c
new file mode 100644
index 0000000..082494d
--- /dev/null
+++ b/sbin/atm/atmconfig/atmconfig_device.c
@@ -0,0 +1,444 @@
+/*
+ * Copyright (c) 2001-2002
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ * Copyright (c) 2003-2004
+ * Hartmut Brandt.
+ * All rights reserved.
+ *
+ * Author: Hartmut Brandt <harti@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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "atmconfig.h"
+#include "atmconfig_device.h"
+#include "private.h"
+#include "oid.h"
+
+#include <bsnmp/asn1.h>
+#include <bsnmp/snmp.h>
+#include <bsnmp/snmpclient.h>
+
+/*
+ * Description of the begemotAtmIfTable
+ */
+static const struct snmp_table atmif_table = {
+ OIDX_begemotAtmIfTable,
+ OIDX_begemotAtmIfTableLastChange, 2,
+ sizeof(struct atmif),
+ 1, 0x7ffULL,
+ {
+ { 0,
+ SNMP_SYNTAX_INTEGER, offsetof(struct atmif, index) },
+ { OID_begemotAtmIfName,
+ SNMP_SYNTAX_OCTETSTRING, offsetof(struct atmif, ifname) },
+ { OID_begemotAtmIfPcr,
+ SNMP_SYNTAX_GAUGE, offsetof(struct atmif, pcr) },
+ { OID_begemotAtmIfMedia,
+ SNMP_SYNTAX_INTEGER, offsetof(struct atmif, media) },
+ { OID_begemotAtmIfVpiBits,
+ SNMP_SYNTAX_GAUGE, offsetof(struct atmif, vpi_bits) },
+ { OID_begemotAtmIfVciBits,
+ SNMP_SYNTAX_GAUGE, offsetof(struct atmif, vci_bits) },
+ { OID_begemotAtmIfMaxVpcs,
+ SNMP_SYNTAX_GAUGE, offsetof(struct atmif, max_vpcs) },
+ { OID_begemotAtmIfMaxVccs,
+ SNMP_SYNTAX_GAUGE, offsetof(struct atmif, max_vccs) },
+ { OID_begemotAtmIfEsi,
+ SNMP_SYNTAX_OCTETSTRING, offsetof(struct atmif, esi) },
+ { OID_begemotAtmIfCarrierStatus,
+ SNMP_SYNTAX_INTEGER, offsetof(struct atmif, carrier) },
+ { OID_begemotAtmIfMode,
+ SNMP_SYNTAX_INTEGER, offsetof(struct atmif, mode) },
+ { 0, SNMP_SYNTAX_NULL, 0 }
+ }
+};
+
+/* List of all ATM interfaces */
+struct atmif_list atmif_list = TAILQ_HEAD_INITIALIZER(atmif_list);
+
+/*
+ * ATM hardware table
+ */
+struct atmhw {
+ TAILQ_ENTRY(atmhw) link;
+ uint64_t found;
+ int32_t index;
+ u_char *vendor;
+ size_t vendorlen;
+ u_char *device;
+ size_t devicelen;
+ uint32_t serial;
+ uint32_t version;
+ uint32_t soft_version;
+};
+TAILQ_HEAD(atmhw_list, atmhw);
+
+/* list of ATM hardware */
+static struct atmhw_list atmhw_list;
+
+/*
+ * Read ATM hardware table
+ */
+const struct snmp_table atmhw_table = {
+ OIDX_begemotAtmHWTable,
+ OIDX_begemotAtmIfTableLastChange, 2,
+ sizeof(struct atmhw),
+ 1, 0x3fULL,
+ {
+ { 0,
+ SNMP_SYNTAX_INTEGER, offsetof(struct atmhw, index) },
+ { OID_begemotAtmHWVendor,
+ SNMP_SYNTAX_OCTETSTRING, offsetof(struct atmhw, vendor) },
+ { OID_begemotAtmHWDevice,
+ SNMP_SYNTAX_OCTETSTRING, offsetof(struct atmhw, device) },
+ { OID_begemotAtmHWSerial,
+ SNMP_SYNTAX_GAUGE, offsetof(struct atmhw, serial) },
+ { OID_begemotAtmHWVersion,
+ SNMP_SYNTAX_GAUGE, offsetof(struct atmhw, version) },
+ { OID_begemotAtmHWSoftVersion,
+ SNMP_SYNTAX_GAUGE, offsetof(struct atmhw, soft_version) },
+ { 0, SNMP_SYNTAX_NULL, 0 }
+ }
+};
+
+static void device_status(int, char *[]);
+static void device_hardware(int, char *[]);
+static void device_modify(int, char *[]);
+
+static const struct cmdtab device_tab[] = {
+ { "hardware", NULL, device_hardware },
+ { "status", NULL, device_status },
+ { "modify", NULL, device_modify },
+ { NULL, NULL, NULL }
+};
+
+static const struct cmdtab entry =
+ { "device", device_tab, NULL };
+
+static DEF_MODULE(&entry);
+
+/*
+ * Carrier state to string
+ */
+static const struct penum strcarrier[] = {
+ { 1, "on" },
+ { 2, "off" },
+ { 3, "unknown" },
+ { 4, "none" },
+ { 0, NULL }
+};
+/*
+ * SUNI mode to string
+ */
+static const struct penum strsunimode[] = {
+ { 1, "sonet" },
+ { 2, "sdh" },
+ { 3, "unknown" },
+ { 0, NULL }
+};
+
+/*
+ * OIDs
+ */
+static const struct asn_oid
+ oid_begemotAtmIfMode = OIDX_begemotAtmIfMode;
+
+/*
+ * Print 1st status line
+ */
+static void
+dev_status1(const struct atmif *aif)
+{
+ char buf[100];
+
+ printf("%-5u %-8s %-6u %-4u %-5u %-4u %-5u "
+ "%02x:%02x:%02x:%02x:%02x:%02x %s\n", aif->index,
+ aif->ifname, aif->pcr,
+ (1 << aif->vpi_bits) - 1, (1 << aif->vci_bits) - 1,
+ aif->max_vpcs, aif->max_vccs, aif->esi[0],
+ aif->esi[1], aif->esi[2], aif->esi[3], aif->esi[4], aif->esi[5],
+ penum(aif->carrier, strcarrier, buf));
+}
+
+/*
+ * Print 2nd status line
+ */
+static void
+dev_status2(const struct atmif *aif)
+{
+ char buf[100];
+
+ printf("%-5u %-8s %s\n", aif->index, aif->ifname,
+ penum(aif->mode, strsunimode, buf));
+}
+
+/*
+ * Implement the 'device status' command
+ */
+static void
+device_status(int argc, char *argv[])
+{
+ int opt, i;
+ struct atmif *aif;
+ static const struct option opts[] = {
+ { NULL, 0, NULL }
+ };
+
+ const char dev1[] =
+ "Interface Max Max\n"
+ "Index Name PCR VPI VCI VPCs VCCs ESI Carrier\n";
+ const char dev2[] =
+ "Interface\n"
+ "Index Name Mode\n";
+
+ while ((opt = parse_options(&argc, &argv, opts)) != -1)
+ switch (opt) {
+ }
+
+ snmp_open(NULL, NULL, NULL, NULL);
+ atexit(snmp_close);
+
+ atmif_fetchtable();
+
+ if (TAILQ_EMPTY(&atmif_list))
+ errx(1, "no ATM interfaces found");
+
+ if (argc > 0) {
+ heading_init();
+ for (i = 0; i < argc; i++) {
+ if ((aif = atmif_find_name(argv[i])) == NULL) {
+ warnx("%s: no such ATM interface", argv[i]);
+ continue;
+ }
+ heading(dev1);
+ dev_status1(aif);
+ }
+ heading_init();
+ for (i = 0; i < argc; i++) {
+ if ((aif = atmif_find_name(argv[i])) == NULL)
+ continue;
+ heading(dev2);
+ dev_status2(aif);
+ }
+ } else {
+ heading_init();
+ TAILQ_FOREACH(aif, &atmif_list, link) {
+ heading(dev1);
+ dev_status1(aif);
+ }
+ heading_init();
+ TAILQ_FOREACH(aif, &atmif_list, link) {
+ heading(dev2);
+ dev_status2(aif);
+ }
+ }
+}
+
+/*
+ * Print hardware info line
+ */
+static void
+dev_hardware(const struct atmif *aif)
+{
+ const struct atmhw *hw;
+
+ TAILQ_FOREACH(hw, &atmhw_list, link)
+ if (aif->index == hw->index)
+ break;
+ if (hw == NULL) {
+ warnx("hardware info not found for '%s'", aif->ifname);
+ return;
+ }
+
+ printf("%-5u %-8s %-16s%-10s %-10u %-10u %u\n", aif->index,
+ aif->ifname, hw->vendor, hw->device, hw->serial,
+ hw->version, hw->soft_version);
+}
+
+/*
+ * Show hardware configuration
+ */
+static void
+device_hardware(int argc, char *argv[])
+{
+ int opt, i;
+ struct atmif *aif;
+
+ static const struct option opts[] = {
+ { NULL, 0, NULL }
+ };
+
+ static const char headline[] =
+ "Interface \n"
+ "Index Name Vendor Card Serial HW SW\n";
+
+ while ((opt = parse_options(&argc, &argv, opts)) != -1)
+ switch (opt) {
+ }
+
+ snmp_open(NULL, NULL, NULL, NULL);
+ atexit(snmp_close);
+
+ atmif_fetchtable();
+
+ if (snmp_table_fetch(&atmhw_table, &atmhw_list) != 0)
+ errx(1, "AtmHW table: %s", snmp_client.error);
+
+ if (argc > 0) {
+ heading_init();
+ for (i = 0; i < argc; i++) {
+ if ((aif = atmif_find_name(argv[i])) == NULL) {
+ warnx("interface not found '%s'", argv[i]);
+ continue;
+ }
+ heading(headline);
+ dev_hardware(aif);
+ }
+ } else {
+ heading_init();
+ TAILQ_FOREACH(aif, &atmif_list, link) {
+ heading(headline);
+ dev_hardware(aif);
+ }
+ }
+}
+
+/*
+ * Change device parameters
+ */
+static void
+device_modify(int argc, char *argv[])
+{
+ int opt;
+ struct atmif *aif;
+ int mode = 0;
+ int n;
+ struct snmp_pdu pdu, resp;
+
+ static const struct option opts[] = {
+#define MODIFY_MODE 0
+ { "mode", OPT_STRING, NULL },
+ { NULL, 0, NULL }
+ };
+
+ while ((opt = parse_options(&argc, &argv, opts)) != -1)
+ switch (opt) {
+
+ case MODIFY_MODE:
+ if (pparse(&mode, strsunimode, optarg) == -1 ||
+ mode == 3)
+ errx(1, "illegal mode for -m '%s'", optarg);
+ break;
+ }
+
+ if (argc != 1)
+ errx(1, "device modify needs one argument");
+
+ snmp_open(NULL, NULL, NULL, NULL);
+
+ atexit(snmp_close);
+ atmif_fetchtable();
+
+ if ((aif = atmif_find_name(argv[0])) == NULL)
+ errx(1, "%s: no such ATM interface", argv[0]);
+
+ snmp_pdu_create(&pdu, SNMP_PDU_SET);
+ if (mode != 0) {
+ n = snmp_add_binding(&pdu,
+ &oid_begemotAtmIfMode, SNMP_SYNTAX_INTEGER,
+ NULL);
+ snmp_oid_append(&pdu.bindings[n + 0].var, "i",
+ (asn_subid_t)aif->index);
+ pdu.bindings[n + 0].v.integer = mode;
+ }
+
+ if (pdu.nbindings == 0)
+ errx(1, "must specify something to modify");
+
+ if (snmp_dialog(&pdu, &resp))
+ errx(1, "No response from '%s': %s", snmp_client.chost,
+ snmp_client.error);
+
+ if (snmp_pdu_check(&pdu, &resp) <= 0)
+ errx(1, "Error modifying device");
+
+ snmp_pdu_free(&resp);
+ snmp_pdu_free(&pdu);
+}
+
+/* XXX while this is compiled in */
+void
+device_register(void)
+{
+ register_module(&amodule_1);
+}
+
+/*
+ * Fetch the ATM interface table
+ */
+void
+atmif_fetchtable(void)
+{
+ struct atmif *aif;
+
+ while ((aif = TAILQ_FIRST(&atmif_list)) != NULL) {
+ free(aif->ifname);
+ free(aif->esi);
+ TAILQ_REMOVE(&atmif_list, aif, link);
+ free(aif);
+ }
+
+ if (snmp_table_fetch(&atmif_table, &atmif_list) != 0)
+ errx(1, "AtmIf table: %s", snmp_client.error);
+}
+
+/*
+ * Find a named ATM interface
+ */
+struct atmif *
+atmif_find_name(const char *ifname)
+{
+ struct atmif *atmif;
+
+ TAILQ_FOREACH(atmif, &atmif_list, link)
+ if (strcmp(atmif->ifname, ifname) == 0)
+ return (atmif);
+ return (NULL);
+}
+/*
+ * find an ATM interface by index
+ */
+struct atmif *
+atmif_find(u_int idx)
+{
+ struct atmif *atmif;
+
+ TAILQ_FOREACH(atmif, &atmif_list, link)
+ if (atmif->index == (int32_t)idx)
+ return (atmif);
+ return (NULL);
+}
diff --git a/sbin/atm/atmconfig/atmconfig_device.h b/sbin/atm/atmconfig/atmconfig_device.h
new file mode 100644
index 0000000..1c099fc
--- /dev/null
+++ b/sbin/atm/atmconfig/atmconfig_device.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2001-2002
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ * Copyright (c) 2003-2004
+ * Hartmut Brandt.
+ * All rights reserved.
+ *
+ * Author: Hartmut Brandt <harti@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.
+ *
+ * $FreeBSD$
+ */
+#ifndef ATMCONFIG_DEVICE_H_
+#define ATMCONFIG_DEVICE_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <stdint.h>
+
+/*
+ * ATM interface table
+ */
+struct atmif {
+ TAILQ_ENTRY(atmif) link;
+ uint64_t found;
+ int32_t index;
+ char *ifname;
+ size_t ifnamelen;
+ uint32_t pcr;
+ int32_t media;
+ uint32_t vpi_bits;
+ uint32_t vci_bits;
+ uint32_t max_vpcs;
+ uint32_t max_vccs;
+ u_char *esi;
+ size_t esilen;
+ int32_t carrier;
+ int32_t mode;
+};
+TAILQ_HEAD(atmif_list, atmif);
+
+/* list of all ATM interfaces */
+extern struct atmif_list atmif_list;
+
+/* fetch this table */
+void atmif_fetchtable(void);
+
+/* find an ATM interface by name */
+struct atmif *atmif_find_name(const char *);
+
+/* find an ATM interface by index */
+struct atmif *atmif_find(u_int);
+
+#endif
diff --git a/sbin/atm/atmconfig/atmconfig_device.help b/sbin/atm/atmconfig/atmconfig_device.help
new file mode 100644
index 0000000..27237c8
--- /dev/null
+++ b/sbin/atm/atmconfig/atmconfig_device.help
@@ -0,0 +1,62 @@
+# Copyright (c) 2003-2004
+# Hartmut Brandt.
+# All rights reserved.
+#
+# Author: Hartmut Brandt <harti@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.
+#
+# Help file for the atmconfig utility
+#
+# $FreeBSD$
+#
+^0 device
+^^ device show information about ATM hardware interfaces
+This command shows information about the ATM hardware interfaces on the
+system. Status information can be obtained by:
+
+ atmconfig [common-options] device status [options] [device ...]
+
+Information about the hardware of the ATM interfaces is reported by:
+
+ atmconfig [common-options] device hardware [options] [device ...]
+
+The parameters of the a device can be changed by:
+
+ atmconfig [common-options] device modify [options] <device>
+
+^1 status
+usage: atmconfig [common-options] device status [device ...]
+
+If no device is given as argument information about all devices is shown.
+
+^1 hardware
+usage: atmconfig [common-options] device hardware [device ...]
+
+If now device is given as argument information about all devices is shown.
+
+^1 modify
+usage: atmconfig [common-options] device modify [-mode mode] <device>
+
+options:
+ -mode switch the SUNI mode to either 'sonet' or 'sdh'.
+
diff --git a/sbin/atm/atmconfig/diag.c b/sbin/atm/atmconfig/diag.c
new file mode 100644
index 0000000..547d364
--- /dev/null
+++ b/sbin/atm/atmconfig/diag.c
@@ -0,0 +1,1122 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * 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.
+ *
+ * Author: Hartmut Brandt <harti@freebsd.org>
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <net/if.h>
+#include <net/if_mib.h>
+#include <net/if_types.h>
+#include <net/if_atm.h>
+#include <net/if_media.h>
+#include <netnatm/natm.h>
+#include <dev/utopia/utopia.h>
+#include <dev/utopia/suni.h>
+#include <dev/utopia/idtphy.h>
+
+#include "atmconfig.h"
+#include "private.h"
+#include "diag.h"
+
+static void diag_list(int, char *[]);
+static void diag_config(int, char *[]);
+static void diag_vcc(int, char *[]);
+static void diag_phy_show(int, char *[]);
+static void diag_phy_set(int, char *[]);
+static void diag_phy_print(int, char *[]);
+static void diag_phy_stats(int, char *[]);
+static void diag_stats(int, char *[]);
+
+const struct cmdtab diag_phy_tab[] = {
+ { "show", NULL, diag_phy_show },
+ { "set", NULL, diag_phy_set },
+ { "stats", NULL, diag_phy_stats },
+ { "print", NULL, diag_phy_print },
+ { NULL, NULL, NULL },
+};
+
+const struct cmdtab diag_tab[] = {
+ { "list", NULL, diag_list },
+ { "config", NULL, diag_config },
+ { "phy", diag_phy_tab, NULL },
+ { "stats", NULL, diag_stats },
+ { "vcc", NULL, diag_vcc },
+ { NULL, NULL, NULL }
+};
+
+static const struct utopia_print suni_lite[] = { SUNI_PRINT_LITE };
+static const struct utopia_print suni_ultra[] = { SUNI_PRINT_ULTRA };
+static const struct utopia_print suni_622[] = { SUNI_PRINT_622 };
+static const struct utopia_print idt77105[] = { IDTPHY_PRINT_77105 };
+static const struct utopia_print idt77155[] = { IDTPHY_PRINT_77155 };
+
+static const struct {
+ const struct utopia_print *tab;
+ u_int len;
+ u_int type;
+} phy_print[] = {
+ { suni_lite, sizeof(suni_lite) / sizeof(suni_lite[0]),
+ UTP_TYPE_SUNI_LITE },
+ { suni_ultra, sizeof(suni_ultra) / sizeof(suni_ultra[0]),
+ UTP_TYPE_SUNI_ULTRA },
+ { suni_622, sizeof(suni_622) / sizeof(suni_622[0]),
+ UTP_TYPE_SUNI_622 },
+ { idt77105, sizeof(idt77105) / sizeof(idt77105[0]),
+ UTP_TYPE_IDT77105 },
+ { idt77155, sizeof(idt77155) / sizeof(idt77155[0]),
+ UTP_TYPE_IDT77155 },
+};
+
+static const u_int utopia_addreg[] = { UTP_REG_ADD };
+
+/*
+ * Driver statistics printing
+ */
+static const char *const print_stats_pca200e[] = {
+ "cmd_queue_full:",
+ "get_stat_errors:",
+ "clr_stat_errors:",
+ "get_prom_errors:",
+ "suni_reg_errors:",
+ "tx_queue_full:",
+ "tx_queue_almost_full:",
+ "tx_pdu2big:",
+ "tx_too_many_segs:",
+ "tx_retry:",
+ "fix_empty:",
+ "fix_addr_copy:",
+ "fix_addr_noext:",
+ "fix_addr_ext:",
+ "fix_len_noext:",
+ "fix_len_copy:",
+ "fix_len:",
+ "rx_badvc:",
+ "rx_closed:",
+ NULL
+};
+static const char *const print_stats_he[] = {
+ "tdprq_full:",
+ "hbuf_error:",
+ "crc_error:",
+ "len_error:",
+ "flow_closed:",
+ "flow_drop:",
+ "tpd_no_mem:",
+ "rx_seg:",
+ "empty_hbuf:",
+ "short_aal5:",
+ "badlen_aal5:",
+ "bug_bad_isw:",
+ "bug_no_irq_upd:",
+ "itype_tbrq:",
+ "itype_tpd:",
+ "itype_rbps:",
+ "itype_rbpl:",
+ "itype_rbrq:",
+ "itype_rbrqt:",
+ "itype_unknown:",
+ "itype_phys:",
+ "itype_err:",
+ "defrag:",
+ "mcc:",
+ "oec:",
+ "dcc:",
+ "cec:",
+ "no_rcv_mbuf:",
+ NULL
+};
+static const char *const print_stats_eni[] = {
+ "ttrash:",
+ "mfixaddr:",
+ "mfixlen:",
+ "mfixfail:",
+ "txmbovr:",
+ "dmaovr:",
+ "txoutspace:",
+ "txdtqout:",
+ "launch:",
+ "hwpull:",
+ "swadd:",
+ "rxqnotus:",
+ "rxqus:",
+ "rxdrqout:",
+ "rxmbufout:",
+ "txnomap:",
+ "vtrash:",
+ "otrash:",
+ NULL
+};
+
+static const char *const print_stats_idt77211[] = {
+ "need_copy:",
+ "copy_failed:",
+ "out_of_tbds:",
+ "no_txmaps:",
+ "tx_load_err:",
+ "tx_qfull:",
+ NULL
+};
+static const char *const print_stats_idt77252[] = {
+ "raw_cells:",
+ "raw_no_vcc:",
+ "raw_no_buf:",
+ "tx_qfull:",
+ "tx_out_of_tbds:",
+ "tx_out_of_maps:",
+ "tx_load_err:",
+ NULL
+};
+static const char *const print_stats_virtual[] = {
+ "dummy:",
+ NULL
+};
+static const char *const *const print_stats[] = {
+ [ATM_DEVICE_UNKNOWN] = NULL,
+ [ATM_DEVICE_PCA200E] = print_stats_pca200e,
+ [ATM_DEVICE_HE155] = print_stats_he,
+ [ATM_DEVICE_HE622] = print_stats_he,
+ [ATM_DEVICE_ENI155P] = print_stats_eni,
+ [ATM_DEVICE_ADP155P] = print_stats_eni,
+ [ATM_DEVICE_FORELE25] = print_stats_idt77211,
+ [ATM_DEVICE_FORELE155] = print_stats_idt77211,
+ [ATM_DEVICE_NICSTAR25] = print_stats_idt77211,
+ [ATM_DEVICE_NICSTAR155] = print_stats_idt77211,
+ [ATM_DEVICE_IDTABR25] = print_stats_idt77252,
+ [ATM_DEVICE_IDTABR155] = print_stats_idt77252,
+ [ATM_DEVICE_PROATM25] = print_stats_idt77252,
+ [ATM_DEVICE_PROATM155] = print_stats_idt77252,
+ [ATM_DEVICE_VIRTUAL] = print_stats_virtual,
+};
+
+struct diagif_list diagif_list = TAILQ_HEAD_INITIALIZER(diagif_list);
+
+/*
+ * Fetch a phy sysctl
+ */
+static int
+phy_fetch(const char *ifname, const char *var, void *val, size_t len,
+ int err_fatal)
+{
+ char *str;
+
+ if (asprintf(&str, "hw.atm.%s.phy_%s", ifname, var) == -1)
+ err(1, NULL);
+ if (sysctlbyname(str, val, &len, NULL, 0) == -1) {
+ if (err_fatal || errno != ENOENT)
+ err(1, "%s", str);
+ free(str);
+ return (-1);
+ }
+ free(str);
+ return (0);
+}
+
+/*
+ * Fetch the list of all ATM network interfaces and their MIBs.
+ */
+void
+diagif_fetch(void)
+{
+ size_t len;
+ int count;
+ int name[6];
+ struct ifmibdata mib;
+ struct ifatm_mib atm;
+ int idx;
+ struct diagif *d;
+
+ while ((d = TAILQ_FIRST(&diagif_list)) != NULL) {
+ if (d->vtab != NULL)
+ free(d->vtab);
+ TAILQ_REMOVE(&diagif_list, d, link);
+ free(d);
+ }
+
+ len = sizeof(count);
+ if (sysctlbyname("net.link.generic.system.ifcount", &count, &len,
+ NULL, 0) == -1)
+ err(1, "ifcount");
+
+ name[0] = CTL_NET;
+ name[1] = PF_LINK;
+ name[2] = NETLINK_GENERIC;
+ name[3] = IFMIB_IFDATA;
+
+ for (idx = 1; idx <= count; idx++) {
+ name[4] = idx;
+ name[5] = IFDATA_GENERAL;
+ len = sizeof(mib);
+ if (sysctl(name, 6, &mib, &len, NULL, 0) == -1)
+ err(1, "interface %d: general mib", idx);
+ if (mib.ifmd_data.ifi_type == IFT_ATM) {
+ name[5] = IFDATA_LINKSPECIFIC;
+ len = sizeof(atm);
+ if (sysctl(name, 6, &atm, &len, NULL, 0) == -1)
+ err(1, "interface %d: ATM mib", idx);
+
+ d = malloc(sizeof(*d));
+ if (d == NULL)
+ err(1, NULL);
+ bzero(d, sizeof(*d));
+ d->mib = atm;
+ d->index = idx;
+ strcpy(d->ifname, mib.ifmd_name);
+ TAILQ_INSERT_TAIL(&diagif_list, d, link);
+
+ if (phy_fetch(d->ifname, "type", &d->phy_type,
+ sizeof(d->phy_type), 0) == 0) {
+ d->phy_present = 1;
+ phy_fetch(d->ifname, "loopback",
+ &d->phy_loopback,
+ sizeof(d->phy_loopback), 1);
+ phy_fetch(d->ifname, "name", &d->phy_name,
+ sizeof(d->phy_name), 1);
+ phy_fetch(d->ifname, "state", &d->phy_state,
+ sizeof(d->phy_state), 1);
+ phy_fetch(d->ifname, "carrier", &d->phy_carrier,
+ sizeof(d->phy_carrier), 1);
+ }
+ }
+ }
+}
+
+/*
+ * "<radix><bit>STRING\011<mask><pattern>STRING\012<mask><radix>STRING"
+ */
+static char *
+printb8(uint32_t val, const char *descr)
+{
+ static char buffer[1000];
+ char *ptr;
+ int tmp = 0;
+ u_char mask, pattern;
+
+ if (*descr++ == '\010')
+ sprintf(buffer, "%#o", val);
+ else
+ sprintf(buffer, "%#x", val);
+ ptr = buffer + strlen(buffer);
+
+ *ptr++ = '<';
+ while (*descr) {
+ if (*descr == '\11') {
+ descr++;
+ mask = *descr++;
+ pattern = *descr++;
+ if ((val & mask) == pattern) {
+ if (tmp++)
+ *ptr++ = ',';
+ while (*descr >= ' ')
+ *ptr++ = *descr++;
+ } else {
+ while (*descr >= ' ')
+ descr++;
+ }
+ } else if (*descr == '\12') {
+ descr++;
+ mask = *descr++;
+ pattern = *descr++;
+ if (tmp++)
+ *ptr++ = ',';
+ while (*descr >= ' ')
+ *ptr++ = *descr++;
+ *ptr++ = '=';
+ if (pattern == 8)
+ sprintf(ptr, "%#o",
+ (val & mask) >> (ffs(mask)-1));
+ else if (pattern == 10)
+ sprintf(ptr, "%u",
+ (val & mask) >> (ffs(mask)-1));
+ else
+ sprintf(ptr, "%#x",
+ (val & mask) >> (ffs(mask)-1));
+ ptr += strlen(ptr);
+ } else {
+ if (val & (1 << (*descr++ - 1))) {
+ if (tmp++)
+ *ptr++ = ',';
+ while (*descr >= ' ')
+ *ptr++ = *descr++;
+ } else {
+ while (*descr >= ' ')
+ descr++;
+ }
+ }
+ }
+ *ptr++ = '>';
+ *ptr++ = '\0';
+
+ return (buffer);
+}
+
+/*
+ * "<radix><bit>STRING<bit>STRING"
+ */
+static char *
+printb(uint32_t val, const char *descr)
+{
+ static char buffer[1000];
+ char *ptr;
+ int tmp = 0;
+
+ if (*descr++ == '\010')
+ sprintf(buffer, "%#o", val);
+ else
+ sprintf(buffer, "%#x", val);
+ ptr = buffer + strlen(buffer);
+
+ *ptr++ = '<';
+ while (*descr) {
+ if (val & (1 << (*descr++ - 1))) {
+ if (tmp++)
+ *ptr++ = ',';
+ while (*descr > ' ')
+ *ptr++ = *descr++;
+ } else {
+ while (*descr > ' ')
+ descr++;
+ }
+ }
+ *ptr++ = '>';
+ *ptr++ = '\0';
+
+ return (buffer);
+}
+
+
+static void
+diag_loop(int argc, char *argv[], const char *text,
+ void (*func)(const struct diagif *))
+{
+ int i;
+ struct diagif *aif;
+
+ heading_init();
+ if (argc > 0) {
+ for (i = 0; i < argc; i++) {
+ TAILQ_FOREACH(aif, &diagif_list, link) {
+ if (strcmp(argv[i], aif->ifname) == 0) {
+ heading(text);
+ (*func)(aif);
+ break;
+ }
+ }
+ if (aif == NULL)
+ warnx("%s: no such ATM interface", argv[i]);
+ }
+ } else {
+ TAILQ_FOREACH(aif, &diagif_list, link) {
+ heading(text);
+ (*func)(aif);
+ }
+ }
+}
+
+/*
+ * Print the config line for the given interface
+ */
+static void
+config_line1(const struct diagif *aif)
+{
+ printf("%-6u%-9s%-8u%-5u%-6u%-5u%-6u%02x:%02x:%02x:%02x:%02x:%02x\n",
+ aif->index, aif->ifname, aif->mib.pcr, (1 << aif->mib.vpi_bits) - 1,
+ (1 << aif->mib.vci_bits) - 1, aif->mib.max_vpcs, aif->mib.max_vccs,
+ aif->mib.esi[0], aif->mib.esi[1], aif->mib.esi[2],
+ aif->mib.esi[3], aif->mib.esi[4], aif->mib.esi[5]);
+}
+
+static void
+config_line2(const struct diagif *aif)
+{
+ u_int d, i;
+
+ static const struct {
+ const char *dev;
+ const char *vendor;
+ } devs[] = {
+ ATM_DEVICE_NAMES
+ };
+ static const struct {
+ u_int media;
+ const char *const name;
+ } medias[] = IFM_SUBTYPE_ATM_DESCRIPTIONS;
+
+ for (i = 0; medias[i].name; i++)
+ if (aif->mib.media == medias[i].media)
+ break;
+
+ if ((d = aif->mib.device) >= sizeof(devs) / sizeof(devs[0]))
+ d = 0;
+
+ printf("%-6u%-9s%-12.11s%-13.12s%-8u%-6x%-6x %s\n", aif->index,
+ aif->ifname, devs[d].vendor, devs[d].dev, aif->mib.serial,
+ aif->mib.hw_version, aif->mib.sw_version,
+ medias[i].name ? medias[i].name : "unknown");
+}
+
+static void
+diag_config(int argc, char *argv[])
+{
+ int opt;
+
+ static int hardware;
+ static int atm;
+
+ static const struct option opts[] = {
+ { "hardware", OPT_SIMPLE, &hardware },
+ { "atm", OPT_SIMPLE, &atm },
+ { NULL, 0, NULL }
+ };
+
+ static const char config_text1[] =
+ "Interface Max Max\n"
+ "Index Name PCR VPI VCI VPCs VCCs ESI\n";
+ static const char config_text2[] =
+ "Interface Version\n"
+ "Index Name Vendor Card "
+ "Serial HW SW Media\n";
+
+ while ((opt = parse_options(&argc, &argv, opts)) != -1)
+ switch (opt) {
+ }
+
+ diagif_fetch();
+ if (TAILQ_EMPTY(&diagif_list))
+ errx(1, "no ATM interfaces found");
+
+ if (!atm && !hardware)
+ atm = 1;
+
+ if (atm)
+ diag_loop(argc, argv, config_text1, config_line1);
+ if (hardware)
+ diag_loop(argc, argv, config_text2, config_line2);
+
+}
+
+static void
+diag_list(int argc, char *argv[])
+{
+ int opt;
+ struct diagif *aif;
+
+ static const struct option opts[] = {
+ { NULL, 0, NULL }
+ };
+
+ while ((opt = parse_options(&argc, &argv, opts)) != -1)
+ switch (opt) {
+ }
+
+ if (argc > 0)
+ errx(1, "no arguments required for 'diag list'");
+
+ diagif_fetch();
+ if (TAILQ_EMPTY(&diagif_list))
+ errx(1, "no ATM interfaces found");
+
+ TAILQ_FOREACH(aif, &diagif_list, link)
+ printf("%s ", aif->ifname);
+ printf("\n");
+}
+
+/*
+ * Print the config line for the given interface
+ */
+static void
+phy_show_line(const struct diagif *aif)
+{
+ printf("%-6u%-9s", aif->index, aif->ifname);
+ if (aif->phy_present)
+ printf("%-5u%-25s0x%-9x", aif->phy_type,
+ aif->phy_name, aif->phy_loopback);
+ printf("\n");
+}
+
+static void
+diag_phy_show(int argc, char *argv[])
+{
+ int opt;
+
+ static const struct option opts[] = {
+ { NULL, 0, NULL }
+ };
+
+ static const char phy_show_text[] =
+ "Interface Phy\n"
+ "Index Name Type Name Loopback State\n";
+
+ while ((opt = parse_options(&argc, &argv, opts)) != -1)
+ switch (opt) {
+ }
+
+ diagif_fetch();
+ if (TAILQ_EMPTY(&diagif_list))
+ errx(1, "no ATM interfaces found");
+
+ diag_loop(argc, argv, phy_show_text, phy_show_line);
+}
+
+/*
+ * Make sure the interface exists and has a phy
+ */
+static struct diagif *
+diagif_get_phy(const char *arg)
+{
+ struct diagif *aif;
+
+ diagif_fetch();
+ TAILQ_FOREACH(aif, &diagif_list, link)
+ if (strcmp(aif->ifname, arg) == 0)
+ break;
+ if (aif == NULL)
+ errx(1, "no such interface: %s", arg);
+ if (!aif->phy_present)
+ errx(1, "interface %s has no phy", arg);
+
+ return (aif);
+}
+
+static void
+diag_phy_set(int argc, char *argv[])
+{
+ int opt;
+ uint8_t reg[3];
+ u_long res;
+ char *end;
+ char *str;
+
+ static const struct option opts[] = {
+ { NULL, 0, NULL }
+ };
+
+ while ((opt = parse_options(&argc, &argv, opts)) != -1)
+ switch (opt) {
+ }
+
+ if (argc != 4)
+ errx(1, "missing arguments for 'diag phy set'");
+
+ errno = 0;
+ res = strtoul(argv[1], &end, 0);
+ if (errno != 0)
+ err(1, "register number");
+ if (*end != '\0')
+ errx(1, "malformed register number '%s'", argv[1]);
+ if (res > 0xff)
+ errx(1, "register number too large");
+ reg[0] = res;
+
+ errno = 0;
+ res = strtoul(argv[2], &end, 0);
+ if (errno != 0)
+ err(1, "mask");
+ if (*end != '\0')
+ errx(1, "malformed mask '%s'", argv[1]);
+ if (res > 0xff)
+ errx(1, "mask too large");
+ reg[1] = res;
+
+ errno = 0;
+ res = strtoul(argv[3], &end, 0);
+ if (errno != 0)
+ err(1, "value");
+ if (*end != '\0')
+ errx(1, "malformed value '%s'", argv[1]);
+ if (res > 0xff)
+ errx(1, "value too large");
+ reg[2] = res;
+
+ (void)diagif_get_phy(argv[0]);
+
+ if (asprintf(&str, "hw.atm.%s.phy_regs", argv[0]) == -1)
+ err(1, NULL);
+
+ if (sysctlbyname(str, NULL, NULL, reg, 3 * sizeof(uint8_t)))
+ err(1, "%s", str);
+
+ free(str);
+}
+
+static void
+diag_phy_print(int argc, char *argv[])
+{
+ int opt;
+ char *str;
+ size_t len, len1;
+ uint8_t *regs;
+ u_int type, i;
+ const struct utopia_print *p;
+
+ static int numeric;
+
+ static const struct option opts[] = {
+ { "numeric", OPT_SIMPLE, &numeric },
+ { NULL, 0, NULL }
+ };
+
+ while ((opt = parse_options(&argc, &argv, opts)) != -1)
+ switch (opt) {
+ }
+
+ if (argc != 1)
+ errx(1, "need device name for 'diag phy print'");
+
+ (void)diagif_get_phy(argv[0]);
+
+ if (asprintf(&str, "hw.atm.%s.phy_regs", argv[0]) == -1)
+ err(1, NULL);
+ len = 0;
+ if (sysctlbyname(str, NULL, &len, NULL, 0))
+ err(1, "'%s' not found", str);
+
+ regs = malloc(len);
+ if (regs == NULL)
+ err(1, NULL);
+
+ if (sysctlbyname(str, regs, &len, NULL, 0))
+ err(1, "'%s' not found", str);
+ free(str);
+
+ if (numeric) {
+ for (i = 0; i < len; i++) {
+ if (i % 16 == 0)
+ printf("%02x: ", i);
+ if (i % 16 == 8)
+ printf(" ");
+ printf(" %02x", regs[i]);
+ if (i % 16 == 15)
+ printf("\n");
+ }
+ if (i % 16 != 0)
+ printf("\n");
+ } else {
+ if (asprintf(&str, "hw.atm.%s.phy_type", argv[0]) == -1)
+ err(1, NULL);
+ len1 = sizeof(type);
+ if (sysctlbyname(str, &type, &len1, NULL, 0))
+ err(1, "'%s' not found", str);
+ free(str);
+
+ for (i = 0; i < sizeof(phy_print) / sizeof(phy_print[0]); i++)
+ if (type == phy_print[i].type)
+ break;
+ if (i == sizeof(phy_print) / sizeof(phy_print[0]))
+ errx(1, "unknown PHY chip type %u\n", type);
+
+ for (p = phy_print[i].tab;
+ p < phy_print[i].tab + phy_print[i].len;
+ p++) {
+ if (p->reg + utopia_addreg[p->type] > len)
+ /* don't have this register */
+ continue;
+
+ printf("%s:%*s", p->name, 40 - (int)strlen(p->name),"");
+
+ switch (p->type) {
+
+ case UTP_REGT_BITS:
+ printf("%s\n", printb8(regs[p->reg], p->fmt));
+ break;
+
+ case UTP_REGT_INT8:
+ printf("%#x\n", regs[p->reg]);
+ break;
+
+ case UTP_REGT_INT10BITS:
+ printf("%#x %s\n", regs[p->reg] |
+ ((regs[p->reg + 1] & 0x3) << 8),
+ printb8(regs[p->reg + 1], p->fmt));
+ break;
+
+ case UTP_REGT_INT12:
+ printf("%#x\n", regs[p->reg] |
+ ((regs[p->reg + 1] & 0xf) << 8));
+ break;
+
+ case UTP_REGT_INT16:
+ printf("%#x\n", regs[p->reg] |
+ (regs[p->reg + 1] << 8));
+ break;
+
+ case UTP_REGT_INT19:
+ printf("%#x\n", regs[p->reg] |
+ (regs[p->reg + 1] << 8) |
+ ((regs[p->reg + 2] & 0x7) << 16));
+ break;
+
+ case UTP_REGT_INT20:
+ printf("%#x\n", regs[p->reg] |
+ (regs[p->reg + 1] << 8) |
+ ((regs[p->reg + 2] & 0xf) << 16));
+ break;
+
+ case UTP_REGT_INT21:
+ printf("%#x\n", regs[p->reg] |
+ (regs[p->reg + 1] << 8) |
+ ((regs[p->reg + 2] & 0x1f) << 16));
+ break;
+
+ default:
+ abort();
+ }
+ }
+ }
+ free(regs);
+}
+
+static void
+diag_phy_stats(int argc, char *argv[])
+{
+ int opt;
+ size_t len;
+ char *str;
+ struct utopia_stats1 stats1;
+ u_int foo;
+
+ static int clear;
+
+ static const struct option opts[] = {
+ { "clear", OPT_SIMPLE, &clear },
+ { NULL, 0, NULL }
+ };
+
+ while ((opt = parse_options(&argc, &argv, opts)) != -1)
+ switch (opt) {
+ }
+
+ if (argc != 1)
+ errx(1, "need device name for 'diag phy stats'");
+
+ (void)diagif_get_phy(argv[0]);
+
+ if (asprintf(&str, "hw.atm.%s.phy_stats", argv[0]) == -1)
+ err(1, NULL);
+
+ len = sizeof(stats1);
+ if (sysctlbyname(str, &stats1, &len,
+ clear ? &foo : NULL, clear ? sizeof(foo) : 0))
+ err(1, "'%s' not found", str);
+ if (len < sizeof(stats1.version))
+ errx(1, "phy statistics too short %zu", len);
+
+ switch (stats1.version) {
+
+ case 1:
+ if (len != sizeof(stats1))
+ errx(1, "bad phy stats length %zu (expecting %zu)",
+ len, sizeof(stats1));
+ break;
+
+ default:
+ errx(1, "unknown phy stats version %u", stats1.version);
+ }
+
+ free(str);
+
+ printf("rx_sbip: %llu\n", (unsigned long long)stats1.rx_sbip);
+ printf("rx_lbip: %llu\n", (unsigned long long)stats1.rx_lbip);
+ printf("rx_lfebe: %llu\n", (unsigned long long)stats1.rx_lfebe);
+ printf("rx_pbip: %llu\n", (unsigned long long)stats1.rx_pbip);
+ printf("rx_pfebe: %llu\n", (unsigned long long)stats1.rx_pfebe);
+ printf("rx_cells: %llu\n", (unsigned long long)stats1.rx_cells);
+ printf("rx_corr: %llu\n", (unsigned long long)stats1.rx_corr);
+ printf("rx_uncorr: %llu\n", (unsigned long long)stats1.rx_uncorr);
+ printf("rx_symerr: %llu\n", (unsigned long long)stats1.rx_symerr);
+ printf("tx_cells: %llu\n", (unsigned long long)stats1.tx_cells);
+}
+
+/*
+ * Fetch the table of open vccs
+ */
+void
+diagif_fetch_vcc(struct diagif *aif, int fd)
+{
+ struct ifreq ifr;
+
+ if (aif->vtab != NULL)
+ return;
+
+ strncpy(ifr.ifr_name, aif->ifname, IFNAMSIZ);
+ ifr.ifr_name[IFNAMSIZ] = '\0';
+
+ aif->vtab = malloc(sizeof(*aif->vtab) + sizeof(aif->vtab->vccs[0]) *
+ aif->mib.max_vccs);
+ if (aif->vtab == NULL)
+ err(1, NULL);
+ ifr.ifr_data = (caddr_t)aif->vtab;
+
+ if (ioctl(fd, SIOCATMGVCCS, &ifr) == -1)
+ err(1, "SIOCATMGVCCS");
+}
+
+/*
+ * Print the VCC table for this interface.
+ */
+static void
+print_channel(const struct diagif *aif)
+{
+ const struct atmio_vcc *v;
+
+ static const char *const aal_tab[] = {
+ [ATMIO_AAL_0] = "0",
+ [ATMIO_AAL_34] = "3/4",
+ [ATMIO_AAL_5] = "5",
+ [ATMIO_AAL_RAW] = "raw",
+ };
+ static const char *const traffic_tab[] = {
+ [ATMIO_TRAFFIC_UBR] = "ubr",
+ [ATMIO_TRAFFIC_CBR] = "cbr",
+ [ATMIO_TRAFFIC_ABR] = "abr",
+ [ATMIO_TRAFFIC_VBR] = "vbr",
+ };
+
+ for (v = aif->vtab->vccs; v < &aif->vtab->vccs[aif->vtab->count]; v++) {
+ printf("%-6u%-9s%-4u%-6u", aif->index, aif->ifname,
+ v->vpi, v->vci);
+
+ if (v->aal >= sizeof(aal_tab)/sizeof(aal_tab[0]) ||
+ aal_tab[v->aal] == NULL)
+ printf("bad ");
+ else
+ printf("%-4s", aal_tab[v->aal]);
+
+ if (v->traffic >= sizeof(traffic_tab)/sizeof(traffic_tab[0]) ||
+ traffic_tab[v->traffic] == NULL)
+ printf("bad ");
+ else
+ printf("%-8s", traffic_tab[v->traffic]);
+
+ printf("%-6u%-6u%s\n", v->rmtu, v->tmtu,
+ printb(v->flags, ATMIO_FLAGS));
+ }
+}
+
+/*
+ * Print the VCC table for this interface, traffic parameters.
+ */
+static void
+print_traffic(const struct diagif *aif)
+{
+ const struct atmio_vcc *v;
+
+ for (v = aif->vtab->vccs; v < &aif->vtab->vccs[aif->vtab->count]; v++) {
+ printf("%-6u%-9s%-4u%-6u", aif->index, aif->ifname,
+ v->vpi, v->vci);
+
+ switch (v->traffic) {
+
+ case ATMIO_TRAFFIC_CBR:
+ printf("%u", v->tparam.pcr);
+ break;
+
+ case ATMIO_TRAFFIC_UBR:
+ printf("%-8u %u", v->tparam.pcr,
+ v->tparam.mcr);
+ break;
+
+ case ATMIO_TRAFFIC_VBR:
+ printf("%-8u%-8u%-8u", v->tparam.pcr, v->tparam.scr,
+ v->tparam.mbs);
+ break;
+
+ case ATMIO_TRAFFIC_ABR:
+ printf("%-8u %-8u",
+ v->tparam.pcr, v->tparam.mcr);
+ break;
+ }
+ printf("\n");
+ }
+}
+
+/*
+ * Print the VCC table for this interface, ABR traffic parameters.
+ */
+static void
+print_abr(const struct diagif *aif)
+{
+ const struct atmio_vcc *v;
+
+ for (v = aif->vtab->vccs; v < &aif->vtab->vccs[aif->vtab->count]; v++) {
+ printf("%-6u%-9s%-4u%-6u", aif->index, aif->ifname,
+ v->vpi, v->vci);
+
+ if (v->traffic == ATMIO_TRAFFIC_ABR) {
+ printf("%-8u%-8u%-4u%-4u%-5u%-5u%-5u%u",
+ v->tparam.icr, v->tparam.tbe, v->tparam.nrm,
+ v->tparam.trm, v->tparam.adtf, v->tparam.rif,
+ v->tparam.rdf, v->tparam.cdf);
+ }
+ printf("\n");
+ }
+}
+
+static void
+diag_vcc_loop(void (*func)(const struct diagif *), const char *text,
+ int argc, char *argv[], int fd)
+{
+ struct diagif *aif;
+
+ heading_init();
+ if (argc == 0) {
+ TAILQ_FOREACH(aif, &diagif_list, link) {
+ diagif_fetch_vcc(aif, fd);
+ if (aif->vtab->count != 0) {
+ heading(text);
+ (*func)(aif);
+ }
+ }
+
+ } else {
+ for (optind = 0; optind < argc; optind++) {
+ TAILQ_FOREACH(aif, &diagif_list, link)
+ if (strcmp(aif->ifname, argv[optind]) == 0) {
+ diagif_fetch_vcc(aif, fd);
+ if (aif->vtab->count != 0) {
+ heading(text);
+ (*func)(aif);
+ }
+ break;
+ }
+ if (aif == NULL)
+ warnx("no such interface '%s'", argv[optind]);
+ }
+ }
+}
+
+static void
+diag_vcc(int argc, char *argv[])
+{
+ int opt, fd;
+
+ static int channel, traffic, abr;
+ static const struct option opts[] = {
+ { "abr", OPT_SIMPLE, &abr },
+ { "channel", OPT_SIMPLE, &channel },
+ { "traffic", OPT_SIMPLE, &traffic },
+ { NULL, 0, NULL }
+ };
+ static const char head_channel[] =
+ "Interface\n"
+ "Index Name VPI VCI AAL Traffic RxMTU TxMTU Flags\n";
+ static const char head_traffic[] =
+ "Interface Traffic parameters\n"
+ "Index Name VPI VCI PCR SCR MBS MCR\n";
+ static const char head_abr[] =
+ "Interface ABR traffic parameters\n"
+ "Index Name VPI VCI ICR TBE NRM TRM ADTF RIF RDF "
+ "CDF\n";
+
+ while ((opt = parse_options(&argc, &argv, opts)) != -1)
+ switch (opt) {
+ }
+
+ fd = socket(PF_NATM, SOCK_STREAM, PROTO_NATMAAL5);
+ if (fd < 0)
+ err(1, "socket");
+
+ diagif_fetch();
+ if (TAILQ_EMPTY(&diagif_list))
+ errx(1, "no ATM interfaces found");
+
+ if (!channel && !traffic && !abr)
+ channel = 1;
+
+ if (channel)
+ diag_vcc_loop(print_channel, head_channel, argc, argv, fd);
+ if (traffic)
+ diag_vcc_loop(print_traffic, head_traffic, argc, argv, fd);
+ if (abr)
+ diag_vcc_loop(print_abr, head_abr, argc, argv, fd);
+}
+
+/*
+ * Print driver-internal statistics
+ */
+static void
+diag_stats(int argc, char *argv[])
+{
+ int opt;
+ char *str;
+ size_t len;
+ uint32_t *stats;
+ struct diagif *aif;
+ u_int i;
+
+ static const struct option opts[] = {
+ { NULL, 0, NULL }
+ };
+
+ while ((opt = parse_options(&argc, &argv, opts)) != -1)
+ switch (opt) {
+ }
+
+ if (argc != 1)
+ errx(1, "need one arg for 'diag stats'");
+
+ diagif_fetch();
+ TAILQ_FOREACH(aif, &diagif_list, link)
+ if (strcmp(aif->ifname, argv[0]) == 0)
+ break;
+
+ if (aif == NULL)
+ errx(1, "interface '%s' not found", argv[0]);
+
+ if (asprintf(&str, "hw.atm.%s.istats", argv[0]) == -1)
+ err(1, NULL);
+ len = 0;
+ if (sysctlbyname(str, NULL, &len, NULL, 0))
+ err(1, "'%s' not found", str);
+
+ stats = malloc(len);
+ if (stats == NULL)
+ err(1, NULL);
+
+ if (sysctlbyname(str, stats, &len, NULL, 0))
+ err(1, "'%s' not found", str);
+ free(str);
+
+ if (aif->mib.device >= sizeof(print_stats) / sizeof(print_stats[0]) ||
+ print_stats[aif->mib.device] == NULL)
+ errx(1, "unknown stats format (%u)", aif->mib.device);
+
+ for (i = 0; print_stats[aif->mib.device][i] != NULL; i++) {
+ if (i * sizeof(uint32_t) >= len)
+ errx(1, "debug info too short (version mismatch?)");
+ printf("%-22s%u\n", print_stats[aif->mib.device][i], stats[i]);
+ }
+ free(stats);
+
+ if (i != len / sizeof(uint32_t))
+ errx(1, "debug info too long (version mismatch?)");
+}
diff --git a/sbin/atm/atmconfig/diag.h b/sbin/atm/atmconfig/diag.h
new file mode 100644
index 0000000..8b36cd4
--- /dev/null
+++ b/sbin/atm/atmconfig/diag.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * 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.
+ *
+ * Author: Hartmut Brandt <harti@freebsd.org>
+ *
+ * $FreeBSD$
+ */
+
+struct diagif {
+ TAILQ_ENTRY(diagif) link;
+ char ifname[IFNAMSIZ];
+ u_int index;
+ struct ifatm_mib mib;
+ int phy_present : 1;
+ u_int phy_type;
+ u_int phy_loopback;
+ char phy_name[100];
+ u_int phy_state;
+ u_int phy_carrier;
+ struct atmio_vcctable *vtab;
+};
+TAILQ_HEAD(diagif_list, diagif);
+extern struct diagif_list diagif_list;
+
+void diagif_fetch(void);
+void diagif_fetch_vcc(struct diagif *aif, int fd);
diff --git a/sbin/atm/atmconfig/main.c b/sbin/atm/atmconfig/main.c
new file mode 100644
index 0000000..d2543f7
--- /dev/null
+++ b/sbin/atm/atmconfig/main.c
@@ -0,0 +1,880 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * 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.
+ *
+ * Author: Hartmut Brandt <harti@freebsd.org>
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdint.h>
+#include <fnmatch.h>
+#include <dirent.h>
+#ifndef RESCUE
+#include <bsnmp/asn1.h>
+#include <bsnmp/snmp.h>
+#include <bsnmp/snmpclient.h>
+#endif
+
+#include "atmconfig.h"
+#include "private.h"
+
+/* verbosity level */
+int verbose;
+
+/* notitle option */
+static int notitle;
+
+/* need to put heading before next output */
+static int need_heading;
+
+/*
+ * TOP LEVEL commands
+ */
+static void help_func(int argc, char *argv[]) __dead2;
+
+static const struct cmdtab static_main_tab[] = {
+ { "help", NULL, help_func },
+ { "options", NULL, NULL },
+ { "commands", NULL, NULL },
+ { "diag", diag_tab, NULL },
+ { "natm", natm_tab, NULL },
+ { NULL, NULL, NULL }
+};
+
+static struct cmdtab *main_tab = NULL;
+static size_t main_tab_size = sizeof(static_main_tab) /
+ sizeof(static_main_tab[0]);
+
+static int
+substr(const char *s1, const char *s2)
+{
+ return (strlen(s1) <= strlen(s2) && strncmp(s1, s2, strlen(s1)) == 0);
+}
+
+/*
+ * Current help file state
+ */
+struct help_file {
+ int file_state; /* 0:looking for main file, 1:found, 2:other */
+ const char *p_start; /* current path pointer */
+ const char *p_end; /* end of current path in path */
+ char *dirname; /* directory name */
+ DIR *dir; /* open directory */
+ char *fname; /* current filename */
+ FILE *fp; /* open file */
+ char line[LINE_MAX]; /* current line */
+ u_int fcnt; /* count of files found */
+};
+
+struct help_pos {
+ off_t pos; /* file position */
+ u_int fcnt; /* number of file */
+ char *fname; /* name of file */
+ const char *p_start; /* current path pointer */
+ const char *p_end; /* end of current path in path */
+};
+
+static int
+help_next_file(struct help_file *hp)
+{
+ const char *fpat;
+ struct dirent *ent;
+
+ if (hp->file_state == 3)
+ return (-1);
+
+ if (hp->file_state == 0)
+ fpat = FILE_HELP;
+ else
+ fpat = FILE_HELP_OTHERS;
+
+ if (hp->file_state == 0 || hp->file_state == 1) {
+ /* start from beginning */
+ hp->p_start = PATH_HELP;
+ hp->file_state++;
+ }
+
+ try_file:
+ if (hp->dir != NULL) {
+ /* directory open (must be state 2) */
+ while ((ent = readdir(hp->dir)) != NULL) {
+ if (fnmatch(fpat, ent->d_name, FNM_NOESCAPE) != 0)
+ continue;
+ if (asprintf(&hp->fname, "%s/%s", hp->dirname,
+ ent->d_name) == -1)
+ err(1, NULL);
+ if ((hp->fp = fopen(hp->fname, "r")) != NULL) {
+ hp->fcnt++;
+ return (0);
+ }
+ free(hp->fname);
+ }
+ /* end of directory */
+ closedir(hp->dir);
+ hp->dir = NULL;
+ free(hp->dirname);
+ goto next_path;
+ }
+
+ /* nothing open - advanc to new path element */
+ try_path:
+ for (hp->p_end = hp->p_start; *hp->p_end != '\0' &&
+ *hp->p_end != ':'; hp->p_end++)
+ ;
+
+ if (asprintf(&hp->dirname, "%.*s", (int)(hp->p_end - hp->p_start),
+ hp->p_start) == -1)
+ err(1, NULL);
+
+ if (hp->file_state == 1) {
+ /* just try to open */
+ if (asprintf(&hp->fname, "%s/%s", hp->dirname, fpat) == -1)
+ err(1, NULL);
+ if ((hp->fp = fopen(hp->fname, "r")) != NULL) {
+ hp->fcnt++;
+ return (0);
+ }
+ free(hp->fname);
+
+ goto next_path;
+ }
+
+ /* open directory */
+ if ((hp->dir = opendir(hp->dirname)) != NULL)
+ goto try_file;
+
+ free(hp->dirname);
+
+ next_path:
+ hp->p_start = hp->p_end;
+ if (*hp->p_start == '\0') {
+ /* end of path */
+ if (hp->file_state == 1)
+ errx(1, "help file not found");
+ return (-1);
+ }
+ hp->p_start++;
+ goto try_path;
+
+}
+
+/*
+ * Save current file position
+ */
+static void
+help_file_tell(struct help_file *hp, struct help_pos *pos)
+{
+ if (pos->fname != NULL)
+ free(pos->fname);
+ if ((pos->fname = strdup(hp->fname)) == NULL)
+ err(1, NULL);
+ pos->fcnt = hp->fcnt;
+ pos->p_start = hp->p_start;
+ pos->p_end = hp->p_end;
+ if ((pos->pos = ftello(hp->fp)) == -1)
+ err(1, "%s", pos->fname);
+}
+
+/*
+ * Go to that position
+ *
+ * We can go either to the original help file or back in the current file.
+ */
+static void
+help_file_seek(struct help_file *hp, struct help_pos *pos)
+{
+ hp->p_start = pos->p_start;
+ hp->p_end = pos->p_end;
+ hp->fcnt = pos->fcnt;
+
+ if (hp->dir != NULL) {
+ free(hp->dirname);
+ closedir(hp->dir);
+ hp->dir = NULL;
+ }
+
+ if (hp->fp != NULL &&strcmp(hp->fname, pos->fname) != 0) {
+ free(hp->fname);
+ fclose(hp->fp);
+ hp->fp = NULL;
+ }
+ if (hp->fp == NULL) {
+ if ((hp->fname = strdup(pos->fname)) == NULL)
+ err(1, NULL);
+ if ((hp->fp = fopen(hp->fname, "r")) == NULL)
+ err(1, "reopen %s", hp->fname);
+ }
+ if (fseeko(hp->fp, pos->pos, SEEK_SET) == -1)
+ err(1, "seek %s", hp->fname);
+
+ if (pos->fcnt == 1)
+ /* go back to state 1 */
+ hp->file_state = 1;
+ else
+ /* lock */
+ hp->file_state = 3;
+}
+
+/*
+ * Rewind to position 0
+ */
+static void
+help_file_rewind(struct help_file *hp)
+{
+
+ if (hp->file_state == 1) {
+ if (fseeko(hp->fp, (off_t)0, SEEK_SET) == -1)
+ err(1, "rewind help file");
+ return;
+ }
+
+ if (hp->dir != NULL) {
+ free(hp->dirname);
+ closedir(hp->dir);
+ hp->dir = NULL;
+ }
+
+ if (hp->fp != NULL) {
+ free(hp->fname);
+ fclose(hp->fp);
+ hp->fp = NULL;
+ }
+ memset(hp, 0, sizeof(*hp));
+}
+
+/*
+ * Get next line from a help file
+ */
+static const char *
+help_next_line(struct help_file *hp)
+{
+ for (;;) {
+ if (hp->fp != NULL) {
+ if (fgets(hp->line, sizeof(hp->line), hp->fp) != NULL)
+ return (hp->line);
+ if (ferror(hp->fp))
+ err(1, "%s", hp->fname);
+ free(hp->fname);
+
+ fclose(hp->fp);
+ hp->fp = NULL;
+ }
+ if (help_next_file(hp) == -1)
+ return (NULL);
+ }
+
+}
+
+/*
+ * This function prints the available 0-level help topics from all
+ * other help files by scanning the files. It assumes, that this is called
+ * only from the main help file.
+ */
+static void
+help_get_0topics(struct help_file *hp)
+{
+ struct help_pos save;
+ const char *line;
+
+ memset(&save, 0, sizeof(save));
+ help_file_tell(hp, &save);
+
+ help_file_rewind(hp);
+ while ((line = help_next_line(hp)) != NULL) {
+ if (line[0] == '^' && line[1] == '^')
+ printf("%s", line + 2);
+ }
+ help_file_seek(hp, &save);
+}
+
+/*
+ * Function to print help. The help argument is in argv[0] here.
+ */
+static void
+help_func(int argc, char *argv[])
+{
+ struct help_file hfile;
+ struct help_pos match, last_match;
+ const char *line;
+ char key[100];
+ int level;
+ int i, has_sub_topics;
+
+ memset(&hfile, 0, sizeof(hfile));
+ memset(&match, 0, sizeof(match));
+ memset(&last_match, 0, sizeof(last_match));
+
+ if (argc == 0) {
+ /* only 'help' - show intro */
+ if ((argv[0] = strdup("intro")) == NULL)
+ err(1, NULL);
+ argc = 1;
+ }
+
+ optind = 0;
+ match.pos = -1;
+ last_match.pos = -1;
+ for (;;) {
+ /* read next line */
+ if ((line = help_next_line(&hfile)) == NULL) {
+ /* EOF */
+ level = 999;
+ goto stop;
+ }
+ if (line[0] != '^' || line[1] == '^')
+ continue;
+
+ if (sscanf(line + 1, "%d%99s", &level, key) != 2)
+ errx(1, "error in help file '%s'", line);
+
+ if (level < optind) {
+ stop:
+ /* next higher level entry - stop this level */
+ if (match.pos == -1) {
+ /* not found */
+ goto not_found;
+ }
+ /* go back to the match */
+ help_file_seek(&hfile, &match);
+ last_match = match;
+ memset(&match, 0, sizeof(match));
+ match.pos = -1;
+
+ /* go to next key */
+ if (++optind >= argc)
+ break;
+ }
+ if (level == optind) {
+ if (substr(argv[optind], key)) {
+ if (match.pos != -1) {
+ printf("Ambiguous topic.");
+ goto list_topics;
+ }
+ help_file_tell(&hfile, &match);
+ }
+ }
+ }
+
+ /* before breaking above we have seeked back to the matching point */
+ for (;;) {
+ if ((line = help_next_line(&hfile)) == NULL)
+ break;
+
+ if (line[0] == '#')
+ continue;
+ if (line[0] == '^') {
+ if (line[1] == '^')
+ continue;
+ break;
+ }
+ if (strncmp(line, "$MAIN", 5) == 0) {
+ help_get_0topics(&hfile);
+ continue;
+ }
+ printf("%s", line);
+ }
+
+ exit(0);
+
+ not_found:
+ printf("Topic not found.");
+
+ list_topics:
+ printf(" Use one of:\natmconfig help");
+ for (i = 0; i < optind; i++)
+ printf(" %s", argv[i]);
+
+ printf(" [");
+
+ /* list all the keys at this level */
+ if (last_match.pos == -1)
+ /* go back to start of help */
+ help_file_rewind(&hfile);
+ else
+ help_file_seek(&hfile, &last_match);
+
+ has_sub_topics = 0;
+ while ((line = help_next_line(&hfile)) != NULL) {
+ if (line[0] == '#' || line[0] != '^' || line[1] == '^')
+ continue;
+
+ if (sscanf(line + 1, "%d%99s", &level, key) != 2)
+ errx(1, "error in help file '%s'", line);
+
+ if (level < optind)
+ break;
+ if (level == optind) {
+ has_sub_topics = 1;
+ printf(" %s", key);
+ }
+ }
+ printf(" ].");
+ if (!has_sub_topics)
+ printf(" No sub-topics found.");
+ printf("\n");
+ exit(1);
+}
+
+#ifndef RESCUE
+/*
+ * Parse a server specification
+ *
+ * syntax is [trans::][community@][server][:port]
+ */
+static void
+parse_server(char *name)
+{
+ char *p, *s = name;
+
+ /* look for a double colon */
+ for (p = s; *p != '\0'; p++) {
+ if (*p == '\\' && p[1] != '\0') {
+ p++;
+ continue;
+ }
+ if (*p == ':' && p[1] == ':')
+ break;
+ }
+ if (*p != '\0') {
+ if (p > s) {
+ if (p - s == 3 && strncmp(s, "udp", 3) == 0)
+ snmp_client.trans = SNMP_TRANS_UDP;
+ else if (p - s == 6 && strncmp(s, "stream", 6) == 0)
+ snmp_client.trans = SNMP_TRANS_LOC_STREAM;
+ else if (p - s == 5 && strncmp(s, "dgram", 5) == 0)
+ snmp_client.trans = SNMP_TRANS_LOC_DGRAM;
+ else
+ errx(1, "unknown SNMP transport '%.*s'",
+ (int)(p - s), s);
+ }
+ s = p + 2;
+ }
+
+ /* look for a @ */
+ for (p = s; *p != '\0'; p++) {
+ if (*p == '\\' && p[1] != '\0') {
+ p++;
+ continue;
+ }
+ if (*p == '@')
+ break;
+ }
+
+ if (*p != '\0') {
+ if (p - s > SNMP_COMMUNITY_MAXLEN)
+ err(1, "community string too long");
+ strncpy(snmp_client.read_community, s, p - s);
+ snmp_client.read_community[p - s] = '\0';
+ strncpy(snmp_client.write_community, s, p - s);
+ snmp_client.write_community[p - s] = '\0';
+ s = p + 1;
+ }
+
+ /* look for a colon */
+ for (p = s; *p != '\0'; p++) {
+ if (*p == '\\' && p[1] != '\0') {
+ p++;
+ continue;
+ }
+ if (*p == ':')
+ break;
+ }
+
+ if (*p == ':') {
+ if (p > s) {
+ *p = '\0';
+ snmp_client_set_host(&snmp_client, s);
+ *p = ':';
+ }
+ snmp_client_set_port(&snmp_client, p + 1);
+ } else if (p > s)
+ snmp_client_set_host(&snmp_client, s);
+}
+#endif
+
+int
+main(int argc, char *argv[])
+{
+ int opt, i;
+ const struct cmdtab *match, *cc, *tab;
+
+#ifndef RESCUE
+ snmp_client_init(&snmp_client);
+ snmp_client.trans = SNMP_TRANS_LOC_STREAM;
+ snmp_client_set_host(&snmp_client, PATH_ILMI_SOCK);
+#endif
+
+#ifdef RESCUE
+#define OPTSTR "htv"
+#else
+#define OPTSTR "htvs:"
+#endif
+
+ while ((opt = getopt(argc, argv, OPTSTR)) != -1)
+ switch (opt) {
+
+ case 'h':
+ help_func(0, argv);
+
+#ifndef RESCUE
+ case 's':
+ parse_server(optarg);
+ break;
+#endif
+
+ case 'v':
+ verbose++;
+ break;
+
+ case 't':
+ notitle = 1;
+ break;
+ }
+
+ if (argv[optind] == NULL)
+ help_func(0, argv);
+
+ argc -= optind;
+ argv += optind;
+
+ if ((main_tab = malloc(sizeof(static_main_tab))) == NULL)
+ err(1, NULL);
+ memcpy(main_tab, static_main_tab, sizeof(static_main_tab));
+
+#ifndef RESCUE
+ /* XXX while this is compiled in */
+ device_register();
+#endif
+
+ cc = main_tab;
+ i = 0;
+ for (;;) {
+ /*
+ * Scan the table for a match
+ */
+ tab = cc;
+ match = NULL;
+ while (cc->string != NULL) {
+ if (substr(argv[i], cc->string)) {
+ if (match != NULL) {
+ printf("Ambiguous option '%s'",
+ argv[i]);
+ cc = tab;
+ goto subopts;
+ }
+ match = cc;
+ }
+ cc++;
+ }
+ if ((cc = match) == NULL) {
+ printf("Unknown option '%s'", argv[i]);
+ cc = tab;
+ goto subopts;
+ }
+
+ /*
+ * Have a match. If there is no subtable, there must
+ * be either a handler or the command is only a help entry.
+ */
+ if (cc->sub == NULL) {
+ if (cc->func != NULL)
+ break;
+ printf("Unknown option '%s'", argv[i]);
+ cc = tab;
+ goto subopts;
+ }
+
+ /*
+ * Look at the next argument. If it doesn't exist or it
+ * looks like a switch, terminate the scan here.
+ */
+ if (argv[i + 1] == NULL || argv[i + 1][0] == '-') {
+ if (cc->func != NULL)
+ break;
+ printf("Need sub-option for '%s'", argv[i]);
+ cc = cc->sub;
+ goto subopts;
+ }
+
+ cc = cc->sub;
+ i++;
+ }
+
+ argc -= i + 1;
+ argv += i + 1;
+
+ (*cc->func)(argc, argv);
+
+ return (0);
+
+ subopts:
+ printf(". Select one of:\n");
+ while (cc->string != NULL) {
+ if (cc->func != NULL || cc->sub != NULL)
+ printf("%s ", cc->string);
+ cc++;
+ }
+ printf("\n");
+
+ return (1);
+}
+
+void
+verb(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (verbose) {
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ }
+}
+
+void
+heading(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (need_heading) {
+ need_heading = 0;
+ if (!notitle) {
+ va_start(ap, fmt);
+ fprintf(stdout, fmt, ap);
+ va_end(ap);
+ }
+ }
+}
+
+void
+heading_init(void)
+{
+ need_heading = 1;
+}
+
+/*
+ * stringify an enumerated value
+ */
+const char *
+penum(int32_t value, const struct penum *strtab, char *buf)
+{
+ while (strtab->str != NULL) {
+ if (strtab->value == value) {
+ strcpy(buf, strtab->str);
+ return (buf);
+ }
+ strtab++;
+ }
+ warnx("illegal value for enumerated variable '%d'", value);
+ strcpy(buf, "?");
+ return (buf);
+}
+
+/*
+ * And the other way 'round
+ */
+int
+pparse(int32_t *val, const struct penum *tab, const char *str)
+{
+
+ while (tab->str != NULL) {
+ if (strcmp(tab->str, str) == 0) {
+ *val = tab->value;
+ return (0);
+ }
+ tab++;
+ }
+ return (-1);
+}
+
+/*
+ * Parse command line options
+ */
+int
+parse_options(int *pargc, char ***pargv, const struct option *opts)
+{
+ const struct option *o, *m;
+ char *arg;
+ u_long ularg, ularg1;
+ long larg;
+ char *end;
+
+ if (*pargc == 0)
+ return (-1);
+ arg = (*pargv)[0];
+ if (arg[0] != '-' || arg[1] == '\0')
+ return (-1);
+ if (arg[1] == '-' && arg[2] == '\0') {
+ (*pargv)++;
+ (*pargc)--;
+ return (-1);
+ }
+
+ m = NULL;
+ for (o = opts; o->optstr != NULL; o++) {
+ if (strlen(arg + 1) <= strlen(o->optstr) &&
+ strncmp(arg + 1, o->optstr, strlen(arg + 1)) == 0) {
+ if (m != NULL)
+ errx(1, "ambiguous option '%s'", arg);
+ m = o;
+ }
+ }
+ if (m == NULL)
+ errx(1, "unknown option '%s'", arg);
+
+ (*pargv)++;
+ (*pargc)--;
+
+ if (m->opttype == OPT_NONE)
+ return (m - opts);
+
+ if (m->opttype == OPT_SIMPLE) {
+ *(int *)m->optarg = 1;
+ return (m - opts);
+ }
+
+ if (*pargc == 0)
+ errx(1, "option requires argument '%s'", arg);
+ optarg = *(*pargv)++;
+ (*pargc)--;
+
+ switch (m->opttype) {
+
+ case OPT_UINT:
+ ularg = strtoul(optarg, &end, 0);
+ if (*end != '\0')
+ errx(1, "bad unsigned integer argument for '%s'", arg);
+ if (ularg > UINT_MAX)
+ errx(1, "argument to large for option '%s'", arg);
+ *(u_int *)m->optarg = (u_int)ularg;
+ break;
+
+ case OPT_INT:
+ larg = strtol(optarg, &end, 0);
+ if (*end != '\0')
+ errx(1, "bad integer argument for '%s'", arg);
+ if (larg > INT_MAX || larg < INT_MIN)
+ errx(1, "argument out of range for option '%s'", arg);
+ *(int *)m->optarg = (int)larg;
+ break;
+
+ case OPT_UINT32:
+ ularg = strtoul(optarg, &end, 0);
+ if (*end != '\0')
+ errx(1, "bad unsigned integer argument for '%s'", arg);
+ if (ularg > UINT32_MAX)
+ errx(1, "argument to large for option '%s'", arg);
+ *(uint32_t *)m->optarg = (uint32_t)ularg;
+ break;
+
+ case OPT_INT32:
+ larg = strtol(optarg, &end, 0);
+ if (*end != '\0')
+ errx(1, "bad integer argument for '%s'", arg);
+ if (larg > INT32_MAX || larg < INT32_MIN)
+ errx(1, "argument out of range for option '%s'", arg);
+ *(int32_t *)m->optarg = (int32_t)larg;
+ break;
+
+ case OPT_UINT64:
+ *(uint64_t *)m->optarg = strtoull(optarg, &end, 0);
+ if (*end != '\0')
+ errx(1, "bad unsigned integer argument for '%s'", arg);
+ break;
+
+ case OPT_INT64:
+ *(int64_t *)m->optarg = strtoll(optarg, &end, 0);
+ if (*end != '\0')
+ errx(1, "bad integer argument for '%s'", arg);
+ break;
+
+ case OPT_FLAG:
+ if (strcasecmp(optarg, "enable") == 0 ||
+ strcasecmp(optarg, "yes") == 0 ||
+ strcasecmp(optarg, "true") == 0 ||
+ strcasecmp(optarg, "on") == 0 ||
+ strcmp(optarg, "1") == 0)
+ *(int *)m->optarg = 1;
+ else if (strcasecmp(optarg, "disable") == 0 ||
+ strcasecmp(optarg, "no") == 0 ||
+ strcasecmp(optarg, "false") == 0 ||
+ strcasecmp(optarg, "off") == 0 ||
+ strcmp(optarg, "0") == 0)
+ *(int *)m->optarg = 0;
+ else
+ errx(1, "bad boolean argument to '%s'", arg);
+ break;
+
+ case OPT_VCI:
+ ularg = strtoul(optarg, &end, 0);
+ if (*end == '.') {
+ ularg1 = strtoul(end + 1, &end, 0);
+ } else {
+ ularg1 = ularg;
+ ularg = 0;
+ }
+ if (*end != '\0')
+ errx(1, "bad VCI value for option '%s'", arg);
+ if (ularg > 0xff)
+ errx(1, "VPI value too large for option '%s'", arg);
+ if (ularg1 > 0xffff)
+ errx(1, "VCI value too large for option '%s'", arg);
+ ((u_int *)m->optarg)[0] = ularg;
+ ((u_int *)m->optarg)[1] = ularg1;
+ break;
+
+ case OPT_STRING:
+ if (m->optarg != NULL)
+ *(const char **)m->optarg = optarg;
+ break;
+
+ default:
+ errx(1, "(internal) bad option type %u for '%s'",
+ m->opttype, arg);
+ }
+ return (m - opts);
+}
+
+/*
+ * for compiled-in modules
+ */
+void
+register_module(const struct amodule *mod)
+{
+ main_tab_size++;
+ if ((main_tab = realloc(main_tab, main_tab_size * sizeof(main_tab[0])))
+ == NULL)
+ err(1, NULL);
+ main_tab[main_tab_size - 2] = *mod->cmd;
+ memset(&main_tab[main_tab_size - 1], 0, sizeof(main_tab[0]));
+}
diff --git a/sbin/atm/atmconfig/natm.c b/sbin/atm/atmconfig/natm.c
new file mode 100644
index 0000000..29f5ce2
--- /dev/null
+++ b/sbin/atm/atmconfig/natm.c
@@ -0,0 +1,680 @@
+/*
+ * Copyright (c) 2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * 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.
+ *
+ * Author: Hartmut Brandt <harti@freebsd.org>
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <net/if_atm.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include "atmconfig.h"
+#include "private.h"
+#include "diag.h"
+
+static void natm_add(int, char *[]);
+static void natm_delete(int, char *[]);
+static void natm_show(int, char *[]);
+
+const struct cmdtab natm_tab[] = {
+ { "add", NULL, natm_add },
+ { "delete", NULL, natm_delete },
+ { "show", NULL, natm_show },
+ { NULL, NULL, NULL }
+};
+
+/*
+ * Structure to hold a route
+ */
+struct natm_route {
+ TAILQ_ENTRY(natm_route) link;
+ struct in_addr host;
+ struct diagif *aif;
+ u_int flags;
+ int llcsnap;
+ u_int vpi, vci;
+ u_int traffic;
+ u_int pcr, scr, mbs, icr, mcr;
+ u_int tbe, nrm, trm, adtf, rif, rdf, cdf;
+};
+static TAILQ_HEAD(, natm_route) natm_route_list =
+ TAILQ_HEAD_INITIALIZER(natm_route_list);
+
+static void
+store_route(struct rt_msghdr *rtm)
+{
+ u_int i;
+ struct natm_route *r;
+ char *cp;
+ struct sockaddr *sa;
+ struct sockaddr_in *sain;
+ struct sockaddr_dl *sdl;
+ struct diagif *aif;
+ u_int n;
+
+ r = malloc(sizeof(*r));
+ if (r == NULL)
+ err(1, "allocate route");
+
+ r->flags = rtm->rtm_flags;
+ cp = (char *)(rtm + 1);
+ for (i = 1; i != 0; i <<= 1) {
+ if (rtm->rtm_addrs & i) {
+ sa = (struct sockaddr *)cp;
+ cp += roundup(sa->sa_len, sizeof(long));
+ switch (i) {
+
+ case RTA_DST:
+ if (sa->sa_family != AF_INET) {
+ warnx("RTA_DST not AF_INET %u", sa->sa_family);
+ goto fail;
+ }
+ sain = (struct sockaddr_in *)(void *)sa;
+ if (sain->sin_len < 4)
+ r->host.s_addr = INADDR_ANY;
+ else
+ r->host = sain->sin_addr;
+ break;
+
+ case RTA_GATEWAY:
+ if (sa->sa_family != AF_LINK) {
+ warnx("RTA_GATEWAY not AF_LINK");
+ goto fail;
+ }
+ sdl = (struct sockaddr_dl *)(void *)sa;
+ TAILQ_FOREACH(aif, &diagif_list, link)
+ if (strlen(aif->ifname) ==
+ sdl->sdl_nlen &&
+ strncmp(aif->ifname, sdl->sdl_data,
+ sdl->sdl_nlen) == 0)
+ break;
+ if (aif == NULL) {
+ warnx("interface '%.*s' not found",
+ sdl->sdl_nlen, sdl->sdl_data);
+ goto fail;
+ }
+ r->aif = aif;
+
+ /* parse ATM stuff */
+
+#define GET3() (((sdl->sdl_data[n] & 0xff) << 16) | \
+ ((sdl->sdl_data[n + 1] & 0xff) << 8) | \
+ ((sdl->sdl_data[n + 2] & 0xff) << 0))
+#define GET2() (((sdl->sdl_data[n] & 0xff) << 8) | \
+ ((sdl->sdl_data[n + 1] & 0xff) << 0))
+#define GET1() (((sdl->sdl_data[n] & 0xff) << 0))
+
+ n = sdl->sdl_nlen;
+ if (sdl->sdl_alen < 4) {
+ warnx("RTA_GATEWAY alen too short");
+ goto fail;
+ }
+ r->llcsnap = GET1() & ATM_PH_LLCSNAP;
+ n++;
+ r->vpi = GET1();
+ n++;
+ r->vci = GET2();
+ n += 2;
+ if (sdl->sdl_alen == 4) {
+ /* old address */
+ r->traffic = ATMIO_TRAFFIC_UBR;
+ r->pcr = 0;
+ break;
+ }
+ /* new address */
+ r->traffic = GET1();
+ n++;
+ switch (r->traffic) {
+
+ case ATMIO_TRAFFIC_UBR:
+ if (sdl->sdl_alen >= 5 + 3) {
+ r->pcr = GET3();
+ n += 3;
+ } else
+ r->pcr = 0;
+ break;
+
+ case ATMIO_TRAFFIC_CBR:
+ if (sdl->sdl_alen < 5 + 3) {
+ warnx("CBR address too short");
+ goto fail;
+ }
+ r->pcr = GET3();
+ n += 3;
+ break;
+
+ case ATMIO_TRAFFIC_VBR:
+ if (sdl->sdl_alen < 5 + 3 * 3) {
+ warnx("VBR address too short");
+ goto fail;
+ }
+ r->pcr = GET3();
+ n += 3;
+ r->scr = GET3();
+ n += 3;
+ r->mbs = GET3();
+ n += 3;
+ break;
+
+ case ATMIO_TRAFFIC_ABR:
+ if (sdl->sdl_alen < 5 + 4 * 3 + 2 +
+ 1 * 2 + 3) {
+ warnx("ABR address too short");
+ goto fail;
+ }
+ r->pcr = GET3();
+ n += 3;
+ r->mcr = GET3();
+ n += 3;
+ r->icr = GET3();
+ n += 3;
+ r->tbe = GET3();
+ n += 3;
+ r->nrm = GET1();
+ n++;
+ r->trm = GET1();
+ n++;
+ r->adtf = GET2();
+ n += 2;
+ r->rif = GET1();
+ n++;
+ r->rdf = GET1();
+ n++;
+ r->cdf = GET1();
+ n++;
+ break;
+
+ default:
+ goto fail;
+ }
+ break;
+ }
+ }
+ }
+
+ TAILQ_INSERT_TAIL(&natm_route_list, r, link);
+
+ return;
+ fail:
+ free(r);
+}
+
+/*
+ * Fetch the INET routes that a ours
+ */
+static void
+natm_route_fetch(void)
+{
+ int name[6];
+ size_t needed;
+ u_char *buf, *next;
+ struct rt_msghdr *rtm;
+
+ name[0] = CTL_NET;
+ name[1] = PF_ROUTE;
+ name[2] = 0;
+ name[3] = AF_INET;
+ name[4] = NET_RT_DUMP;
+ name[5] = 0;
+
+ if (sysctl(name, 6, NULL, &needed, NULL, 0) == -1)
+ err(1, "rtable estimate");
+ needed *= 2;
+ if ((buf = malloc(needed)) == NULL)
+ err(1, "rtable buffer (%zu)", needed);
+ if (sysctl(name, 6, buf, &needed, NULL, 0) == -1)
+ err(1, "rtable get");
+
+ next = buf;
+ while (next < buf + needed) {
+ rtm = (struct rt_msghdr *)(void *)next;
+ next += rtm->rtm_msglen;
+
+ if (rtm->rtm_type == RTM_GET) {
+ if ((rtm->rtm_flags & (RTF_UP | RTF_HOST |
+ RTF_STATIC)) == (RTF_UP | RTF_HOST | RTF_STATIC) &&
+ (rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY |
+ RTA_IFP)) == (RTA_DST | RTA_GATEWAY | RTA_IFP))
+ store_route(rtm);
+ }
+ }
+}
+
+static u_long
+parse_num(const char *arg, const char *name, u_long limit)
+{
+ u_long res;
+ char *end;
+
+ errno = 0;
+ res = strtoul(arg, &end, 10);
+ if (*end != '\0' || end == arg || errno != 0)
+ errx(1, "cannot parse %s '%s'", name, arg);
+ if (res > limit)
+ errx(1, "%s out of range (0...%lu)", name, limit);
+ return (res);
+}
+
+static void
+do_route(u_int type, u_int flags, const struct sockaddr_in *sain,
+ const struct sockaddr_dl *sdl)
+{
+ struct {
+ struct rt_msghdr h;
+ char space[512];
+ } msg;
+ char *ptr;
+ int s;
+ ssize_t rlen;
+
+ /* create routing message */
+ bzero(&msg, sizeof(msg));
+ msg.h.rtm_msglen = sizeof(msg.h);
+ msg.h.rtm_version = RTM_VERSION;
+ msg.h.rtm_type = type;
+ msg.h.rtm_index = 0;
+ msg.h.rtm_flags = flags;
+ msg.h.rtm_addrs = RTA_DST | (sdl != NULL ? RTA_GATEWAY : 0);
+ msg.h.rtm_pid = getpid();
+
+ ptr = (char *)&msg + sizeof(msg.h);
+ memcpy(ptr, sain, sain->sin_len);
+ ptr += roundup(sain->sin_len, sizeof(long));
+ msg.h.rtm_msglen += roundup(sain->sin_len, sizeof(long));
+
+ if (sdl != NULL) {
+ memcpy(ptr, sdl, sdl->sdl_len);
+ ptr += roundup(sdl->sdl_len, sizeof(long));
+ msg.h.rtm_msglen += roundup(sdl->sdl_len, sizeof(long));
+ }
+
+ /* open socket */
+ s = socket(PF_ROUTE, SOCK_RAW, AF_INET);
+ if (s == -1)
+ err(1, "cannot open routing socket");
+
+ rlen = write(s, &msg, msg.h.rtm_msglen);
+ if (rlen == -1)
+ err(1, "writing to routing socket");
+ if ((size_t)rlen != msg.h.rtm_msglen)
+ errx(1, "short write to routing socket: %zu %u",
+ (size_t)rlen, msg.h.rtm_msglen);
+ close(s);
+}
+
+/*
+ * Add a new NATM route
+ */
+static void
+natm_add(int argc, char *argv[])
+{
+ int opt;
+ struct hostent *hp;
+ struct sockaddr_in sain;
+ struct sockaddr_dl sdl;
+ struct diagif *aif;
+ u_long num, num1;
+ u_int idx;
+
+ static int printonly;
+
+ static const struct option opts[] = {
+ { "printonly", OPT_SIMPLE, &printonly },
+ { NULL, 0, NULL }
+ };
+
+ while ((opt = parse_options(&argc, &argv, opts)) != -1)
+ switch (opt) {
+ }
+
+ if (argc < 5)
+ errx(1, "missing arguments for 'natm add'");
+
+ memset(&sdl, 0, sizeof(sdl));
+ sdl.sdl_len = sizeof(sdl);
+ sdl.sdl_family = AF_LINK;
+
+ /* get the IP address for <dest> */
+ memset(&sain, 0, sizeof(sain));
+ hp = gethostbyname(argv[0]);
+ if (hp == NULL)
+ errx(1, "bad hostname %s: %s", argv[0], hstrerror(h_errno));
+ if (hp->h_addrtype != AF_INET)
+ errx(1, "bad address type for %s", argv[0]);
+ sain.sin_len = sizeof(sain);
+ sain.sin_family = AF_INET;
+ memcpy(&sain.sin_addr, hp->h_addr, sizeof(sain.sin_addr));
+
+ /* find interface */
+ diagif_fetch();
+ TAILQ_FOREACH(aif, &diagif_list, link)
+ if (strcmp(aif->ifname, argv[1]) == 0)
+ break;
+ if (aif == NULL)
+ errx(1, "unknown ATM interface '%s'", argv[1]);
+ sdl.sdl_index = aif->index;
+ strcpy(sdl.sdl_data, aif->ifname);
+ idx = sdl.sdl_nlen = strlen(aif->ifname);
+ idx++;
+
+ /* verify VPI/VCI */
+ num = parse_num(argv[2], "VPI", (1U << aif->mib.vpi_bits));
+ sdl.sdl_data[idx++] = num & 0xff;
+ num = parse_num(argv[3], "VCI", (1U << aif->mib.vci_bits));
+ if (num == 0)
+ errx(1, "VCI may not be 0");
+ sdl.sdl_data[idx++] = (num >> 8) & 0xff;
+ sdl.sdl_data[idx++] = num & 0xff;
+
+ /* encapsulation */
+ if (strcasecmp(argv[4], "llc/snap") == 0) {
+ sdl.sdl_data[sdl.sdl_nlen] = ATM_PH_LLCSNAP;
+ } else if (strcasecmp(argv[4], "aal5") == 0) {
+ sdl.sdl_data[sdl.sdl_nlen] = 0;
+ } else
+ errx(1, "bad encapsulation type '%s'", argv[4]);
+
+ /* look at the traffic */
+ argc -= 5;
+ argv += 5;
+
+ if (argc != 0) {
+ if (strcasecmp(argv[0], "ubr") == 0) {
+ sdl.sdl_data[idx++] = ATMIO_TRAFFIC_UBR;
+ if (argc == 1)
+ /* ok */;
+ else if (argc == 2) {
+ num = parse_num(argv[1], "PCR", aif->mib.pcr);
+ sdl.sdl_data[idx++] = (num >> 16) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 8) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 0) & 0xff;
+ } else
+ errx(1, "too many parameters for UBR");
+
+ } else if (strcasecmp(argv[0], "cbr") == 0) {
+ sdl.sdl_data[idx++] = ATMIO_TRAFFIC_CBR;
+ if (argc == 1)
+ errx(1, "missing PCR for CBR");
+ if (argc > 2)
+ errx(1, "too many parameters for CBR");
+ num = parse_num(argv[1], "PCR", aif->mib.pcr);
+ sdl.sdl_data[idx++] = (num >> 16) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 8) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 0) & 0xff;
+
+ } else if (strcasecmp(argv[0], "vbr") == 0) {
+ sdl.sdl_data[idx++] = ATMIO_TRAFFIC_VBR;
+
+ if (argc < 4)
+ errx(1, "missing arg(s) for VBR");
+ if (argc > 4)
+ errx(1, "too many parameters for VBR");
+
+ num = parse_num(argv[1], "PCR", aif->mib.pcr);
+ sdl.sdl_data[idx++] = (num >> 16) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 8) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 0) & 0xff;
+ num = parse_num(argv[2], "SCR", num);
+ sdl.sdl_data[idx++] = (num >> 16) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 8) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 0) & 0xff;
+ num = parse_num(argv[3], "MBS", 0xffffffLU);
+ sdl.sdl_data[idx++] = (num >> 16) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 8) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 0) & 0xff;
+
+ } else if (strcasecmp(argv[0], "abr") == 0) {
+ sdl.sdl_data[idx++] = ATMIO_TRAFFIC_ABR;
+ if (argc < 11)
+ errx(1, "missing arg(s) for ABR");
+ if (argc > 11)
+ errx(1, "too many parameters for ABR");
+
+ num = parse_num(argv[1], "PCR", aif->mib.pcr);
+ sdl.sdl_data[idx++] = (num >> 16) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 8) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 0) & 0xff;
+
+ num1 = parse_num(argv[2], "MCR", num);
+ sdl.sdl_data[idx++] = (num1 >> 16) & 0xff;
+ sdl.sdl_data[idx++] = (num1 >> 8) & 0xff;
+ sdl.sdl_data[idx++] = (num1 >> 0) & 0xff;
+
+ num = parse_num(argv[3], "ICR", num);
+ sdl.sdl_data[idx++] = (num >> 16) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 8) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 0) & 0xff;
+
+ if (num < num1)
+ errx(1, "ICR must be >= MCR");
+
+ num = parse_num(argv[4], "TBE", 0xffffffUL);
+ sdl.sdl_data[idx++] = (num >> 16) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 8) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 0) & 0xff;
+
+ num = parse_num(argv[5], "NRM", 0x7UL);
+ sdl.sdl_data[idx++] = (num >> 0) & 0xff;
+
+ num = parse_num(argv[6], "TRM", 0x7UL);
+ sdl.sdl_data[idx++] = (num >> 0) & 0xff;
+
+ num = parse_num(argv[7], "ADTF", 0x3ffUL);
+ sdl.sdl_data[idx++] = (num >> 8) & 0xff;
+ sdl.sdl_data[idx++] = (num >> 0) & 0xff;
+
+ num = parse_num(argv[8], "RIF", 0xfUL);
+ sdl.sdl_data[idx++] = (num >> 0) & 0xff;
+
+ num = parse_num(argv[9], "RDF", 0xfUL);
+ sdl.sdl_data[idx++] = (num >> 0) & 0xff;
+
+ num = parse_num(argv[10], "CDF", 0x7UL);
+ sdl.sdl_data[idx++] = (num >> 0) & 0xff;
+
+ } else
+ errx(1, "bad traffic type '%s'", argv[0]);
+ } else
+ sdl.sdl_data[idx++] = ATMIO_TRAFFIC_UBR;
+
+ sdl.sdl_alen = idx - sdl.sdl_nlen;
+ sdl.sdl_len += sdl.sdl_nlen + sdl.sdl_alen;
+
+ if (printonly) {
+ printf("route add -iface %s -link %.*s",
+ inet_ntoa(sain.sin_addr), sdl.sdl_nlen, sdl.sdl_data);
+ for (idx = 0; idx < sdl.sdl_alen; idx++)
+ printf("%c%x", ".:"[idx == 0],
+ (u_int)sdl.sdl_data[sdl.sdl_nlen + idx] & 0xffU);
+ printf("\n");
+ exit(0);
+ }
+
+ do_route(RTM_ADD, RTF_HOST | RTF_STATIC | RTF_UP, &sain, &sdl);
+}
+
+/*
+ * Delete an NATM route
+ */
+static void
+natm_delete(int argc, char *argv[])
+{
+ int opt;
+ struct hostent *hp;
+ struct sockaddr_in sain;
+ u_int vpi, vci;
+ struct diagif *aif;
+ struct natm_route *r;
+
+ static int printonly;
+
+ static const struct option opts[] = {
+ { "printonly", OPT_SIMPLE, &printonly },
+ { NULL, 0, NULL }
+ };
+
+ while ((opt = parse_options(&argc, &argv, opts)) != -1)
+ switch (opt) {
+ }
+
+ diagif_fetch();
+ natm_route_fetch();
+
+ memset(&sain, 0, sizeof(sain));
+ sain.sin_len = sizeof(sain);
+ sain.sin_family = AF_INET;
+
+ if (argc == 1) {
+ /* get the IP address for <dest> */
+ hp = gethostbyname(argv[0]);
+ if (hp == NULL)
+ errx(1, "bad hostname %s: %s", argv[0],
+ hstrerror(h_errno));
+ if (hp->h_addrtype != AF_INET)
+ errx(1, "bad address type for %s", argv[0]);
+ memcpy(&sain.sin_addr, hp->h_addr, sizeof(sain.sin_addr));
+
+ TAILQ_FOREACH(r, &natm_route_list, link)
+ if (r->host.s_addr == sain.sin_addr.s_addr)
+ break;
+ if (r == NULL)
+ errx(1, "no NATM route to host '%s' (%s)", argv[0],
+ inet_ntoa(sain.sin_addr));
+
+ } else if (argc == 3) {
+ TAILQ_FOREACH(aif, &diagif_list, link)
+ if (strcmp(aif->ifname, argv[0]) == 0)
+ break;
+ if (aif == 0)
+ errx(1, "no such interface '%s'", argv[0]);
+
+ vpi = parse_num(argv[1], "VPI", 0xff);
+ vci = parse_num(argv[2], "VCI", 0xffff);
+
+ TAILQ_FOREACH(r, &natm_route_list, link)
+ if (r->aif == aif && r->vpi == vpi && r->vci == vci)
+ break;
+ if (r == NULL)
+ errx(1, "no such NATM route %s %u %u", argv[0],
+ vpi, vci);
+ sain.sin_addr = r->host;
+
+ } else
+ errx(1, "bad number of arguments for 'natm delete'");
+
+ if (printonly) {
+ printf("route delete %s\n", inet_ntoa(r->host));
+ exit(0);
+ }
+
+ do_route(RTM_DELETE, r->flags, &sain, NULL);
+}
+
+/*
+ * Show NATM routes
+ */
+static void
+natm_show(int argc, char *argv[])
+{
+ int opt;
+ struct natm_route *r;
+ struct hostent *hp;
+
+ static const char *const traffics[] = {
+ [ATMIO_TRAFFIC_UBR] = "UBR",
+ [ATMIO_TRAFFIC_CBR] = "CBR",
+ [ATMIO_TRAFFIC_VBR] = "VBR",
+ [ATMIO_TRAFFIC_ABR] = "ABR"
+ };
+
+ static int numeric, abr;
+
+ static const struct option opts[] = {
+ { "abr", OPT_SIMPLE, &abr },
+ { "numeric", OPT_SIMPLE, &numeric },
+ { NULL, 0, NULL }
+ };
+
+ static const char head[] =
+ "Destination Iface VPI VCI Encaps Trf PCR "
+ "SCR/MCR MBS/ICR\n";
+ static const char head_abr[] =
+ "Destination Iface VPI VCI Encaps Trf PCR "
+ "SCR/MCR MBS/ICR TBE NRM TRM ADTF RIF RDF CDF\n";
+
+ while ((opt = parse_options(&argc, &argv, opts)) != -1)
+ switch (opt) {
+ }
+
+ diagif_fetch();
+ natm_route_fetch();
+
+ heading_init();
+ TAILQ_FOREACH(r, &natm_route_list, link) {
+ heading(abr ? head_abr : head);
+ if (numeric)
+ printf("%-20s", inet_ntoa(r->host));
+ else if (r->host.s_addr == INADDR_ANY)
+ printf("%-20s", "default");
+ else {
+ hp = gethostbyaddr((char *)&r->host, sizeof(r->host),
+ AF_INET);
+ if (hp != NULL)
+ printf("%-20s", hp->h_name);
+ else
+ printf("%-20s", inet_ntoa(r->host));
+ }
+ printf("%-12s%-4u%-6u%-9s%-4s", r->aif->ifname, r->vpi, r->vci,
+ r->llcsnap ? "LLC/SNAP" : "AAL5", traffics[r->traffic]);
+ switch (r->traffic) {
+
+ case ATMIO_TRAFFIC_UBR:
+ case ATMIO_TRAFFIC_CBR:
+ printf("%-8u", r->pcr);
+ break;
+
+ case ATMIO_TRAFFIC_VBR:
+ printf("%-8u%-8u%-8u", r->pcr, r->scr, r->mbs);
+ break;
+
+ case ATMIO_TRAFFIC_ABR:
+ printf("%-8u%-8u%-8u", r->pcr, r->mcr, r->icr);
+ if (abr)
+ printf("%-8u%-4u%-4u%-5u%-4u%-4u%-4u",
+ r->tbe, r->nrm, r->trm, r->adtf,
+ r->rif, r->rdf, r->cdf);
+ break;
+ }
+ printf("\n");
+ }
+}
diff --git a/sbin/atm/atmconfig/private.h b/sbin/atm/atmconfig/private.h
new file mode 100644
index 0000000..9dcf539
--- /dev/null
+++ b/sbin/atm/atmconfig/private.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * 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.
+ *
+ * Author: Hartmut Brandt <harti@freebsd.org>
+ *
+ * $FreeBSD$
+ */
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+#include <netgraph.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#ifndef PATH_HELP
+#define PATH_HELP "/usr/share/doc/atm:/usr/local/share/doc/atm"
+#endif
+#ifndef FILE_HELP
+#define FILE_HELP "atmconfig.help"
+#endif
+#ifndef FILE_HELP_OTHERS
+#define FILE_HELP_OTHERS "atmconfig_*.help"
+#endif
+#ifndef PATH_ILMI_SOCK
+#define PATH_ILMI_SOCK "/var/run/ilmid.sock"
+#endif
+
+/*
+ * Builtin commands
+ */
+extern const struct cmdtab diag_tab[];
+extern const struct cmdtab natm_tab[];
diff --git a/sbin/atm/fore_dnld/COPYRIGHT b/sbin/atm/fore_dnld/COPYRIGHT
new file mode 100644
index 0000000..f934d42
--- /dev/null
+++ b/sbin/atm/fore_dnld/COPYRIGHT
@@ -0,0 +1,38 @@
+$FreeBSD$
+
+This copyright applies to the microcode image in the file pca200e.c.
+
+(Copyright Notice)
+
+Copyright (c) 1995-2000 FORE Systems, Inc., as an unpublished work.
+
+This notice does not imply unrestricted or public access to these
+materials which are a trade secret of FORE Systems, Inc. or its
+subsidiaries or affiliates (together referred to as "FORE"), and
+which may not be reproduced, used, sold or transferred to any third
+party without FORE's prior written consent. All rights reserved.
+
+U.S. Government Restricted Rights.
+
+If you are licensing the Software on behalf of the U.S. Government
+("Government"), the following provisions apply to you. If the
+software is supplied to the Department of Defense ("DoD"), it is
+classified as "Commercial Computer Software" under paragraph
+252.227-7014 of the DoD Supplement to the Federal Acquisition
+Regulations ("DFARS") (or any successor regulations) and the
+Government is acquiring only the license rights granted herein (the
+license rights customarily provided to non-Government users). If
+the Software is supplied to any unit or agency of the Government
+other than the DoD, it is classified as "Restricted Computer
+Software" and the Government's rights in the Software are defined
+in paragraph 52.227-19 of the Federal Acquisition Regulations
+("FAR") (or any successor regulations) or, in the cases of NASA,
+in paragraph 18.52.227-86 of the NASA Supplement to the FAR (or
+any successor regulations).
+
+FORE Systems is a registered trademark, and ForeRunner, ForeRunnerLE,
+and ForeThought are trademarks of FORE Systems, Inc. All other
+brands or product names are trademarks or registered trademarks of
+their respective holders.
+
+(End Copyright Notice)
diff --git a/sbin/atm/fore_dnld/Makefile b/sbin/atm/fore_dnld/Makefile
new file mode 100644
index 0000000..abcec31
--- /dev/null
+++ b/sbin/atm/fore_dnld/Makefile
@@ -0,0 +1,36 @@
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $Id: Makefile,v 1.5 1998/07/10 16:01:58 jpt Exp $
+# $FreeBSD$
+
+PROG= fore_dnld
+MAN= fore_dnld.8
+SRCS= fore_dnld.c pca200e.c
+
+WARNS?= 6
+CFLAGS+= -I${.CURDIR}/../../../sys
+
+DPADD= ${LIBATM}
+LDADD= -latm
+
+.include <bsd.prog.mk>
diff --git a/sbin/atm/fore_dnld/fore_dnld.8 b/sbin/atm/fore_dnld/fore_dnld.8
new file mode 100644
index 0000000..c7afb78
--- /dev/null
+++ b/sbin/atm/fore_dnld/fore_dnld.8
@@ -0,0 +1,114 @@
+.\"
+.\" ===================================
+.\" HARP | Host ATM Research Platform
+.\" ===================================
+.\"
+.\"
+.\" This Host ATM Research Platform ("HARP") file (the "Software") is
+.\" made available by Network Computing Services, Inc. ("NetworkCS")
+.\" "AS IS". NetworkCS does not provide maintenance, improvements or
+.\" support of any kind.
+.\"
+.\" NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+.\" INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+.\" SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+.\" In no event shall NetworkCS be responsible for any damages, including
+.\" but not limited to consequential damages, arising from or relating to
+.\" any use of the Software or related support.
+.\"
+.\" Copyright 1994-1998 Network Computing Services, Inc.
+.\"
+.\" Copies of this Software may be made, however, the above copyright
+.\" notice must be reproduced on all copies.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 3, 1996
+.Dt FORE_DNLD 8
+.Os
+.Sh NAME
+.Nm fore_dnld
+.Nd "download FORE Systems' microcode into host ATM adapter"
+.Sh SYNOPSIS
+.Nm
+.Op Fl 3
+.Op Fl i Ar intf
+.Op Fl d Ar path
+.Op Fl f Ar objfile
+.Op Fl r
+.Sh DESCRIPTION
+The
+.Nm
+utility downloads FORE Systems' microcode into the host ATM adapter(s).
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl i Ar intf
+Specify which ATM interface to download microcode to.
+Default is to load microcode into all FORE Systems host adapters.
+.It Fl d Ar path
+Specify the path to prepend to the
+.Ar objfile
+name.
+Default is to use current directory.
+.It Fl f Ar objfile
+Specify the microcode binary file.
+Defaults are:
+.Pa sba200.obj
+for SBA-200 adapters,
+.Pa sba200e.obj
+for SBA-200E adapters, and
+.Pa pca200e.bin
+for PCA-200E adapters.
+.El
+.Sh NOTES
+For the PCA200E adapter, if no file is specified on the command
+line a built-in copy of version 4.1.12 microcode is used.
+When the
+option
+.Fl 3
+is specified version 3.0.1 microcode is used instead.
+.Pp
+Microcode as distributed by FORE Systems is not ready for downloading
+directly into SBA host ATM adapters.
+Instead, the supplied microcode needs
+to be processed with the
+.Xr objcopy 1
+command to create an image suitable
+for downloading.
+Arguments to
+.Xr objcopy 1
+are
+.Dq Li "-S -l -Fcoff" .
+.Pp
+Microcode as
+distributed by FORE Systems for the PCA host ATM adapter does not need
+to be processed.
+.Sh FILES
+.Bl -tag -width indent
+.It Pa ~fore/etc/objcopy
+command to process FORE Systems supplied microcode.
+.It Pa ~fore/etc/sba200*.ucode*
+microcode as supplied by FORE Systems for SBA
+adapters.
+.It Pa ~fore/i386/pca200e.bin
+microcode as supplied by FORE Systems for PCA
+adapters.
+.It Pa ~harp/doc/Install
+HARP installation instructions.
+.El
+.Sh COPYRIGHT
+Copyright (c) 1994-1998, Network Computing Services, Inc.
+.Sh AUTHORS
+.An John Cavanaugh ,
+Minnesota Supercomputer Center, Inc.
+.An Mike Spengler ,
+Minnesota Supercomputer Center, Inc.
+.An Joe Thomas ,
+Minnesota Supercomputer Center, Inc.
+.Sh ACKNOWLEDGMENTS
+This software was developed under the sponsorship of the
+Defense Advanced Research Projects Agency (DARPA) under
+contract numbers F19628-92-C-0072 and F19628-95-C-0215.
+.Sh BUGS
+None known.
diff --git a/sbin/atm/fore_dnld/fore_dnld.c b/sbin/atm/fore_dnld/fore_dnld.c
new file mode 100644
index 0000000..dc52eff
--- /dev/null
+++ b/sbin/atm/fore_dnld/fore_dnld.c
@@ -0,0 +1,1346 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ */
+
+/*
+ * User utilities
+ * --------------
+ *
+ * Download (pre)processed microcode into Fore Series-200 host adapter
+ * Interact with i960 uart on Fore Series-200 host adapter
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <net/if.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+#include <netinet/in.h>
+#include <dev/hfa/fore.h>
+#include <dev/hfa/fore_aali.h>
+#include <dev/hfa/fore_slave.h>
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if (defined(BSD) && (BSD >= 199103))
+#include <termios.h>
+#else
+#include <termio.h>
+#endif /* !BSD */
+#include <unistd.h>
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+extern u_char pca200e_microcode_3[];
+extern int pca200e_microcode_size_3;
+extern u_char pca200e_microcode_4[];
+extern int pca200e_microcode_size_4;
+
+#ifdef sun
+#define DEV_NAME "/dev/sbus%d"
+#endif /* sun */
+#if (defined(BSD) && (BSD >= 199103))
+#define DEV_NAME _PATH_KMEM
+#endif /* BSD */
+
+#define MAX_CHECK 60
+
+static int comm_mode = 0;
+static const char *progname;
+
+static int tty;
+static cc_t vmin, vtime;
+#if (defined(BSD) && (BSD >= 199103))
+static struct termios sgtty;
+#define TCSETA TIOCSETA
+#define TCGETA TIOCGETA
+#else
+static struct termio sgtty;
+#endif /* !BSD */
+
+static int endian = 0;
+static int verbose = 0;
+static int reset = 0;
+
+static char line[132];
+static u_int lineptr;
+
+static Mon960 *Uart;
+
+static void
+delay(int cnt)
+{
+ usleep(cnt);
+}
+
+static uint32_t
+CP_READ(uint32_t val)
+{
+ if ( endian )
+ return ( ntohl ( val ) );
+ else
+ return ( val );
+}
+
+static uint32_t
+CP_WRITE(uint32_t val)
+{
+ if ( endian )
+ return ( htonl ( val ) );
+ else
+ return ( val );
+}
+
+/*
+ * Print an error message and exit.
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ */
+static void
+error(const char *msg)
+{
+ printf ( "%s\n", msg );
+ exit (1);
+}
+
+/*
+ * Get a byte for the uart and if printing, display it.
+ *
+ * Returns:
+ * c Character from uart
+ */
+static char
+getbyte(void)
+{
+ char c;
+
+ while ( ! ( CP_READ(Uart->mon_xmithost) & UART_VALID ) )
+ delay(10);
+
+ c = CP_READ(Uart->mon_xmithost) & UART_DATAMASK;
+ Uart->mon_xmithost = CP_WRITE(UART_READY);
+
+ /*
+ * We need to introduce a delay in here or things tend to hang...
+ */
+ delay(10000);
+
+ if ( lineptr >= sizeof(line) )
+ lineptr = 0;
+
+ /*
+ * Save character into line
+ */
+ line[lineptr++] = c;
+
+ if (verbose) {
+ if (isprint(c) || (c == '\n') || (c == '\r'))
+ putc(c, stdout);
+ }
+ return (c);
+}
+
+/*
+ * Loop getting characters from uart into static string until eol. If printing,
+ * display the line retrieved.
+ *
+ * Arguments:
+ * prn Are we displaying characters
+ *
+ * Returns:
+ * none Line in global string 'line[]'
+ */
+static void
+getline(int prn)
+{
+ char c = '\0';
+ u_int i = 0;
+
+ while ( c != '>' && c != '\n' && c != '\r' )
+ {
+ c = getbyte();
+ if ( ++i >= sizeof(line) )
+ {
+ if ( prn )
+ printf ( "%s", line );
+ i = 0;
+ }
+ }
+
+ /*
+ * Terminate line
+ */
+ line[lineptr] = 0;
+ lineptr = 0;
+
+}
+
+/*
+ * Send a byte to the i960
+ *
+ * Arguments:
+ * c Character to send
+ *
+ * Returns:
+ * none
+ */
+static void
+xmit_byte(u_char c, int dn)
+{
+ int val;
+
+ while ( CP_READ(Uart->mon_xmitmon) != UART_READY )
+ {
+ if ( CP_READ(Uart->mon_xmithost) & UART_VALID )
+ getbyte();
+ if ( !dn ) delay ( 10000 );
+ }
+ val = (int)c | UART_VALID;
+ Uart->mon_xmitmon = CP_WRITE( val );
+ if ( !dn ) delay ( 10000 );
+ if ( CP_READ(Uart->mon_xmithost) & UART_VALID )
+ getbyte();
+
+}
+
+/*
+ * Transmit a line to the i960. Eol must be included as part of text to transmit.
+ *
+ * Arguments:
+ * msg Character string to transmit
+ * len len of string. This allows us to include NULL's
+ * in the string/block to be transmitted.
+ *
+ * Returns:
+ * none
+ */
+static void
+xmit_to_i960(const char *msg, int len, int dn)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ xmit_byte(msg[i], dn);
+}
+
+/*
+ * Send autobaud sequence to i960 monitor
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ */
+static void
+autobaud(void)
+{
+ if ( strncmp ( line, "Mon960", 6 ) == 0 )
+ xmit_to_i960 ( "\r\n\r\n\r\n\r\n", 8, 0 );
+}
+
+/*
+ * Reset tty to initial state
+ *
+ * Arguments:
+ * ret error code for exit()
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+finish(int ret)
+{
+ sgtty.c_lflag |= ( ICANON | ECHO );
+ sgtty.c_cc[VMIN] = vmin;
+ sgtty.c_cc[VTIME] = vtime;
+ ioctl ( tty, TCSETA, &sgtty );
+ exit ( ret );
+}
+
+/*
+ * Utility to strip off any leading path information from a filename
+ *
+ * Arguments:
+ * path pathname to strip
+ *
+ * Returns:
+ * fname striped filename
+ *
+ */
+static const char *
+basename(const char *path)
+{
+ const char *fname;
+
+ if ( ( fname = strrchr ( path, '/' ) ) != NULL )
+ fname++;
+ else
+ fname = path;
+
+ return ( fname );
+}
+
+/*
+ * ASCII constants
+ */
+#define SOH 001
+#define STX 002
+#define ETX 003
+#define EOT 004
+#define ENQ 005
+#define ACK 006
+#define LF 012
+#define CR 015
+#define NAK 025
+#define SYN 026
+#define CAN 030
+#define ESC 033
+
+#define NAKMAX 2
+#define ERRORMAX 10
+#define RETRYMAX 5
+
+#define CRCCHR 'C'
+#define CTRLZ 032
+
+#define BUFSIZE 128
+
+#define W 16
+#define B 8
+
+/*
+ * crctab - CRC-16 constant array...
+ * from Usenet contribution by Mark G. Mendel, Network Systems Corp.
+ * (ihnp4!umn-cs!hyper!mark)
+ */
+static unsigned short crctab[1<<B] = {
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
+ 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
+ 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
+ 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
+ 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
+ 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
+ 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+ 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
+ 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+ 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
+ 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
+ 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
+ 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
+ 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+ 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
+ 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
+ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
+ 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
+ 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
+ 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
+ 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
+ };
+
+/*
+ * Hacked up xmodem protocol. Transmits the file 'filename' down to the i960
+ * using the xmodem protocol.
+ *
+ * Arguments:
+ * filename name of file to transmit
+ *
+ * Returns:
+ * 0 file transmitted
+ * -1 unable to send file
+ */
+static int
+xmitfile(const char *filename)
+{
+ int fd;
+ int numsect;
+ int sectnum;
+ struct stat stb;
+ char c;
+ char sendresp;
+ int crcmode = 0;
+ int attempts = 0;
+ int errors;
+ int sendfin;
+ int extrachr;
+ char buf[BUFSIZE + 6];
+ char blockbuf[BUFSIZE + 6];
+ int bufcntr;
+ int bbufcntr;
+ int bufsize = BUFSIZE;
+ int checksum;
+
+ /*
+ * Try opening file
+ */
+ if ( ( fd = open ( filename, O_RDONLY ) ) < 0 )
+ {
+ return -1;
+ }
+ stat ( filename, &stb );
+
+ /*
+ * Determine number of 128 bytes sectors to transmit
+ */
+ numsect = ( stb.st_size / 128 ) + 1;
+
+ if ( verbose )
+ fprintf ( stderr, "Downloading %d sectors from %s\n",
+ numsect, filename );
+
+ /*
+ * Send DO'wnload' command to i960
+ */
+ xmit_to_i960 ( "do\r\n", 4, 0 );
+ /*
+ * Wait for response from i960 indicating download in progress
+ */
+ while ( strncmp ( line, "Downloading", 11 ) != 0 )
+ getline ( verbose );
+
+
+ /*
+ * Get startup character from i960
+ */
+ do {
+ while ((c = getbyte()) != NAK && c != CRCCHR)
+ if ( ++attempts > NAKMAX )
+ error ( "Remote system not responding" );
+
+ if ( c == CRCCHR )
+ crcmode = 1;
+
+ } while ( c != NAK && c != CRCCHR );
+
+ sectnum = 1;
+ attempts = errors = sendfin = extrachr = 0;
+
+ /*
+ * Loop over each sector to be sent
+ */
+ do {
+ if ( extrachr >= 128 )
+ {
+ extrachr = 0;
+ numsect++;
+ }
+
+ if ( sectnum > 0 )
+ {
+ /*
+ * Read a sectors worth of data from the file into
+ * an internal buffer.
+ */
+ for ( bufcntr = 0; bufcntr < bufsize; )
+ {
+ int n;
+ /*
+ * Check for EOF
+ */
+ if ( ( n = read ( fd, &c, 1 ) ) == 0 )
+ {
+ sendfin = 1;
+ if ( !bufcntr )
+ break;
+ buf[bufcntr++] = CTRLZ;
+ continue;
+ }
+ buf[bufcntr++] = c;
+ }
+ if ( !bufcntr )
+ break;
+ }
+
+ /*
+ * Fill in xmodem protocol values. Block size and sector number
+ */
+ bbufcntr = 0;
+ blockbuf[bbufcntr++] = (bufsize == 1024) ? STX : SOH;
+ blockbuf[bbufcntr++] = sectnum;
+ blockbuf[bbufcntr++] = ~sectnum;
+
+ checksum = 0;
+
+ /*
+ * Loop over the internal buffer computing the checksum of the
+ * sector
+ */
+ for ( bufcntr = 0; bufcntr < bufsize; bufcntr++ )
+ {
+ blockbuf[bbufcntr++] = buf[bufcntr];
+
+ if ( crcmode )
+ checksum = (checksum<<B) ^ crctab[(checksum>>(W-B)) ^ buf[bufcntr]];
+ else
+ checksum = ((checksum + buf[bufcntr]) & 0xff);
+
+ }
+
+ /*
+ * Place the checksum at the end of the transmit buffer
+ */
+ if ( crcmode )
+ {
+ checksum &= 0xffff;
+ blockbuf[bbufcntr++] = ((checksum >> 8) & 0xff);
+ blockbuf[bbufcntr++] = (checksum & 0xff);
+ } else
+ blockbuf[bbufcntr++] = checksum;
+
+ attempts = 0;
+
+ /*
+ * Make several attempts to send the data to the i960
+ */
+ do
+ {
+ /*
+ * Transmit the sector + protocol to the i960
+ */
+ xmit_to_i960 ( blockbuf, bbufcntr, 1 );
+
+ /*
+ * Inform user where we're at
+ */
+ if ( verbose )
+ printf ( "Sector %3d %3dk\r",
+ sectnum, (sectnum * bufsize) / 1024 );
+
+ attempts++;
+ /*
+ * Get response from i960
+ */
+ sendresp = getbyte();
+
+ /*
+ * If i960 didn't like the sector
+ */
+ if ( sendresp != ACK )
+ {
+ errors++;
+
+ /*
+ * Are we supposed to cancel the transfer?
+ */
+ if ( ( sendresp & 0x7f ) == CAN )
+ if (getbyte() == CAN)
+ error ( "Send canceled at user's request" );
+ }
+
+ } while ( ( sendresp != ACK ) && ( attempts < RETRYMAX ) && ( errors < ERRORMAX ) );
+
+ /*
+ * Next sector
+ */
+ sectnum++;
+
+ } while ( !sendfin && ( attempts < RETRYMAX ) && ( errors < ERRORMAX ) );
+
+ /*
+ * Did we expire all our allows attempts?
+ */
+ if ( attempts >= RETRYMAX )
+ {
+ xmit_byte ( CAN, 1 ), xmit_byte ( CAN, 1 ), xmit_byte ( CAN, 1 );
+ error ( "Remote system not responding" );
+ }
+
+ /*
+ * Check for too many transmission errors
+ */
+ if ( errors >= ERRORMAX )
+ {
+ xmit_byte ( CAN, 1 ), xmit_byte ( CAN, 1 ), xmit_byte ( CAN, 1 );
+ error ( "Too many errors in transmission" );
+ }
+
+ attempts = 0;
+
+ /*
+ * Indicate the transfer is complete
+ */
+ xmit_byte ( EOT, 1 );
+
+ /*
+ * Wait until i960 acknowledges us
+ */
+ while ((c = getbyte()) != ACK && (++attempts < RETRYMAX))
+ xmit_byte ( EOT, 1 );
+
+ if ( attempts >= RETRYMAX )
+ error ( "Remote system not responding on completion" );
+
+ /*
+ * After download, we'll see a few more command
+ * prompts as the CP does its stuff. Ignore them.
+ */
+ while ( strncmp ( line, "=>", 2 ) != 0 )
+ getline ( verbose );
+
+ while ( strncmp ( line, "=>", 2 ) != 0 )
+ getline ( verbose );
+
+ while ( strncmp ( line, "=>", 2 ) != 0 )
+ getline ( verbose );
+
+ /*
+ * Tell the i960 to start executing the downloaded code
+ */
+ xmit_to_i960 ( "go\r\n", 4, 0 );
+
+ /*
+ * Get the messages the CP will spit out
+ * after the GO command.
+ */
+ getline ( verbose );
+ getline ( verbose );
+
+ close ( fd );
+
+ return ( 0 );
+}
+
+
+static int
+loadmicrocode(u_char *ucode, int size, u_char *ram)
+{
+ struct {
+ uint32_t Id;
+ uint32_t fver;
+ uint32_t start;
+ uint32_t entry;
+ } binhdr;
+#ifdef sun
+ union {
+ uint32_t w;
+ char c[4];
+ } w1, w2;
+ int n;
+#endif
+ u_char *bufp;
+ uint32_t *lp;
+
+
+ /*
+ * Check that we understand this header
+ */
+ memcpy(&binhdr, ucode, sizeof(binhdr));
+ if ( strncmp ( (caddr_t)&binhdr.Id, "fore", 4 ) != 0 ) {
+ fprintf ( stderr, "Unrecognized format in micorcode file." );
+ return ( -1 );
+ }
+
+#ifdef sun
+ /*
+ * We always swap the SunOS microcode file...
+ */
+ endian = 1;
+
+ /*
+ * We need to swap the header start/entry words...
+ */
+ w1.w = binhdr.start;
+ for ( n = 0; n < sizeof(uint32_t); n++ )
+ w2.c[3-n] = w1.c[n];
+ binhdr.start = w2.w;
+ w1.w = binhdr.entry;
+ for ( n = 0; n < sizeof(uint32_t); n++ )
+ w2.c[3-n] = w1.c[n];
+ binhdr.entry = w2.w;
+#endif /* sun */
+
+ /*
+ * Set pointer to RAM load location
+ */
+ bufp = (ram + binhdr.start);
+
+ /*
+ * Load file
+ */
+ if ( endian ) {
+ u_int i;
+
+ lp = (uint32_t *)(void *)ucode;
+ /* Swap buffer */
+ for ( i = 0; i < size / sizeof(uint32_t); i++ )
+#ifndef sun
+ lp[i] = CP_WRITE(lp[i]);
+#else
+ {
+ int j;
+
+ w1.w = lp[i];
+ for ( j = 0; j < 4; j++ )
+ w2.c[3-j] = w1.c[j];
+ lp[i] = w2.w;
+ }
+#endif
+ }
+ bcopy ( (caddr_t)ucode, bufp, size );
+
+ /*
+ * With .bin extension, we need to specify start address on 'go'
+ * command.
+ */
+ {
+ char cmd[80];
+
+ sprintf ( cmd, "go %x\r\n", binhdr.entry );
+
+ xmit_to_i960 ( cmd, strlen ( cmd ), 0 );
+
+ while ( strncmp ( line, cmd, strlen(cmd) - 3 ) != 0 )
+ getline ( verbose );
+
+ if ( verbose )
+ printf("\n");
+ }
+ return ( 0 );
+}
+
+static int
+sendbinfile(const char *fname, u_char *ram)
+{
+ struct {
+ uint32_t Id;
+ uint32_t fver;
+ uint32_t start;
+ uint32_t entry;
+ } binhdr;
+#ifdef sun
+ union {
+ uint32_t w;
+ char c[4];
+ } w1, w2;
+#endif
+ int fd;
+ int n;
+ int cnt = 0;
+ u_char *bufp;
+ uint32_t buffer[1024];
+
+ /*
+ * Try opening file
+ */
+ if ( ( fd = open ( fname, O_RDONLY ) ) < 0 )
+ return ( -1 );
+
+ /*
+ * Read the .bin header from the file
+ */
+ if ( ( read ( fd, &binhdr, sizeof(binhdr) ) ) != sizeof(binhdr) )
+ {
+ close ( fd );
+ return ( -1 );
+ }
+
+ /*
+ * Check that we understand this header
+ */
+ if ( strncmp ( (caddr_t)&binhdr.Id, "fore", 4 ) != 0 ) {
+ fprintf ( stderr, "Unrecognized format in micorcode file." );
+ close ( fd );
+ return ( -1 );
+ }
+
+#ifdef sun
+ /*
+ * We always swap the SunOS microcode file...
+ */
+ endian = 1;
+
+ /*
+ * We need to swap the header start/entry words...
+ */
+ w1.w = binhdr.start;
+ for ( n = 0; n < sizeof(uint32_t); n++ )
+ w2.c[3-n] = w1.c[n];
+ binhdr.start = w2.w;
+ w1.w = binhdr.entry;
+ for ( n = 0; n < sizeof(uint32_t); n++ )
+ w2.c[3-n] = w1.c[n];
+ binhdr.entry = w2.w;
+#endif /* sun */
+
+ /*
+ * Rewind the file
+ */
+ lseek ( fd, 0, 0 );
+
+ /*
+ * Set pointer to RAM load location
+ */
+ bufp = (ram + binhdr.start);
+
+ /*
+ * Load file
+ */
+ if ( endian ) {
+ /*
+ * Need to swap longs - copy file into temp buffer
+ */
+ while ( ( n = read ( fd, (char *)buffer, sizeof(buffer))) > 0 )
+ {
+ u_int i;
+
+ /* Swap buffer */
+ for (i = 0; i < sizeof(buffer) / sizeof(uint32_t); i++)
+#ifndef sun
+ buffer[i] = CP_WRITE(buffer[i]);
+#else
+ {
+ int j;
+
+ w1.w = buffer[i];
+ for ( j = 0; j < 4; j++ )
+ w2.c[3-j] = w1.c[j];
+ buffer[i] = w2.w;
+ }
+#endif
+
+ /*
+ * Copy swapped buffer into CP RAM
+ */
+ cnt++;
+ bcopy ( (caddr_t)buffer, bufp, n );
+ if ( verbose )
+ printf ( "%d\r", cnt );
+ bufp += n;
+ }
+ } else {
+ while ( ( n = read ( fd, bufp, 128 ) ) > 0 )
+ {
+ cnt++;
+ if ( verbose )
+ printf ( "%d\r", cnt );
+ bufp += n;
+ }
+ }
+
+ /*
+ * With .bin extension, we need to specify start address on 'go'
+ * command.
+ */
+ {
+ char cmd[80];
+
+ sprintf ( cmd, "go %x\r\n", binhdr.entry );
+
+ xmit_to_i960 ( cmd, strlen ( cmd ), 0 );
+
+ while ( strncmp ( line, cmd, strlen(cmd) - 3 ) != 0 )
+ getline ( verbose );
+
+ if ( verbose )
+ printf("\n");
+ }
+
+ close ( fd );
+ return ( 0 );
+}
+
+
+/*
+ * Program to download previously processed microcode to series-200 host adapter
+ */
+int
+main(int argc, char *argv[])
+{
+ int fd; /* mmap for Uart */
+ u_char *ram; /* pointer to RAM */
+ Mon960 *Mon; /* Uart */
+ Aali *aap;
+ int c, i, err;
+ int binary = 0; /* Send binary file */
+ caddr_t buf; /* Ioctl buffer */
+ char bus_dev[80]; /* Bus device to mmap on */
+ struct atminfreq req;
+ struct air_cfg_rsp *air; /* Config info response structure */
+ int buf_len; /* Size of ioctl buffer */
+ const char *dev = "\0"; /* Device to download */
+ char *dirname = NULL; /* Directory path to objd files */
+ char *objfile = NULL; /* Command line object filename */
+ u_char *ucode = NULL; /* Pointer to microcode */
+ int ucode_size = 0; /* Length of microcode */
+ char *sndfile = NULL; /* Object filename to download */
+ char filename[64]; /* Constructed object filename */
+ char base[64]; /* sba200/sba200e/pca200e basename */
+ int ext = 0; /* 0 == bin 1 == objd */
+ struct stat sbuf; /* Used to find if .bin or .objd */
+ int pca_vers = 4;
+
+ progname = basename(argv[0]);
+ comm_mode = strcmp ( progname, "fore_comm" ) == 0;
+
+ while ( ( c = getopt ( argc, argv, "3i:d:f:berv" ) ) != -1 )
+ switch ( c ) {
+ case '3':
+ pca_vers = 3;
+ break;
+ case 'b':
+ binary++;
+ break;
+ case 'd':
+ dirname = (char *)strdup ( optarg );
+ break;
+ case 'e':
+ endian++;
+ break;
+ case 'i':
+ dev = (char *)strdup(optarg);
+ break;
+ case 'f':
+ objfile = (char *)strdup ( optarg );
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'r':
+ reset++;
+ break;
+ case '?':
+ printf ( "usage: %s [-v] [-i intf] [-d dirname] [-f objfile]\n", argv[0] );
+ exit ( 2 );
+ }
+
+ /*
+ * Unbuffer stdout
+ */
+ setbuf ( stdout, NULL );
+
+ if ( ( fd = socket ( AF_ATM, SOCK_DGRAM, 0 ) ) < 0 )
+ {
+ perror ( "Cannot create ATM socket" );
+ exit ( 1 );
+ }
+ /*
+ * Over allocate memory for returned data. This allows
+ * space for IOCTL reply info as well as config info.
+ */
+ buf_len = 4 * sizeof(struct air_cfg_rsp);
+ if ( ( buf = (caddr_t)malloc(buf_len) ) == NULL )
+ {
+ perror ( "Cannot allocate memory for reply" );
+ exit ( 1 );
+ }
+ /*
+ * Fill in request paramaters
+ */
+ req.air_opcode = AIOCS_INF_CFG;
+ req.air_buf_addr = buf;
+ req.air_buf_len = buf_len;
+
+ /*
+ * Copy interface name into ioctl request
+ */
+ strcpy(req.air_cfg_intf, dev);
+
+ /*
+ * Issue ioctl
+ */
+ if ( ( ioctl ( fd, AIOCINFO, (caddr_t)&req ) ) ) {
+ perror ( "ioctl (AIOCSINFO)" );
+ exit ( 1 );
+ }
+ /*
+ * Reset buffer pointer
+ */
+ req.air_buf_addr = buf;
+
+ /*
+ * Close socket
+ */
+ close ( fd );
+
+ /*
+ * Loop through all attached adapters
+ */
+ for (; req.air_buf_len >= sizeof(struct air_cfg_rsp);
+ buf += sizeof(struct air_cfg_rsp),
+ req.air_buf_len -= sizeof(struct air_cfg_rsp)) {
+
+ /*
+ * Point to vendor info
+ */
+ air = (struct air_cfg_rsp *)(void *)buf;
+
+ if (air->acp_vendapi == VENDAPI_FORE_1 && air->acp_ram != 0)
+ {
+ /*
+ * Create /dev name
+ */
+#ifdef sun
+ sprintf ( bus_dev, DEV_NAME, air->acp_busslot );
+#else
+ sprintf ( bus_dev, DEV_NAME );
+#endif
+
+ /*
+ * Setup signal handlers
+ */
+ signal ( SIGINT, SIG_IGN );
+ signal ( SIGQUIT, SIG_IGN );
+
+ /*
+ * If comm_mode, setup terminal for single char I/O
+ */
+ if ( comm_mode ) {
+ tty = open ( _PATH_TTY, O_RDWR );
+ ioctl ( tty, TCGETA, &sgtty );
+ sgtty.c_lflag &= ~( ICANON | ECHO );
+ vmin = sgtty.c_cc[VMIN];
+ vtime = sgtty.c_cc[VTIME];
+ sgtty.c_cc[VMIN] = 0;
+ sgtty.c_cc[VTIME] = 0;
+ ioctl ( tty, TCSETA, &sgtty );
+ }
+
+ /*
+ * Open bus for memory access
+ */
+ if ( ( fd = open ( bus_dev, O_RDWR ) ) < 0 )
+ {
+ perror ( "open bus_dev" );
+ fprintf(stderr, "%s download failed (%s)\n",
+ air->acp_intf, bus_dev);
+ continue;
+ }
+
+ /*
+ * Map in the RAM memory to get access to the Uart
+ */
+#ifdef __FreeBSD__ /*XXX*/
+ ram = (u_char *) mmap(0, PCA200E_MMAP_SIZE,
+#else
+ ram = (u_char *) mmap(0, air->acp_ramsize,
+#endif
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_HASSEMAPHORE,
+ fd, air->acp_ram);
+ if (ram == (u_char *)-1) {
+ perror ( "mmap ram" );
+ fprintf(stderr, "%s download failed\n",
+ air->acp_intf);
+ (void) close(fd);
+ continue;
+ }
+ Mon = (Mon960 *)(volatile void *)(ram + MON960_BASE);
+ Uart = (Mon960 *)(volatile void *)&(Mon->mon_xmitmon);
+
+ /*
+ * Determine endianess
+ */
+ switch ( Mon->mon_bstat ) {
+ case BOOT_COLDSTART:
+ case BOOT_MONREADY:
+ case BOOT_FAILTEST:
+ case BOOT_RUNNING:
+ break;
+
+ default:
+ switch (ntohl(Mon->mon_bstat)) {
+ case BOOT_COLDSTART:
+ case BOOT_MONREADY:
+ case BOOT_FAILTEST:
+ case BOOT_RUNNING:
+ endian++;
+ break;
+
+ default:
+ fprintf(stderr, "%s unknown status\n",
+ air->acp_intf);
+ (void) close(fd);
+ continue;
+ }
+ break;
+ }
+
+#ifdef __FreeBSD__
+ if (reset) {
+ u_int *hcr = (u_int *)(void *)(ram + PCA200E_HCR_OFFSET);
+ PCA200E_HCR_INIT(*hcr, PCA200E_RESET_BD);
+ delay(10000);
+ PCA200E_HCR_CLR(*hcr, PCA200E_RESET_BD);
+ delay(10000);
+ }
+#endif
+
+ if ( comm_mode ) {
+ static struct timeval timeout = { 0, 0 };
+ int esc_seen = 0;
+
+ /*
+ * We want to talk with the i960 monitor
+ */
+
+ /*
+ * Loop forever accepting characters
+ */
+ for ( ; ; ) {
+ fd_set fdr;
+ int ns;
+
+ /*
+ * Check for data from the terminal
+ */
+ FD_ZERO ( &fdr );
+ FD_SET ( fileno(stdin), &fdr );
+
+ if ( ( ns = select ( FD_SETSIZE, &fdr, NULL, NULL,
+ &timeout ) ) < 0 ) {
+ perror ( "select" );
+ finish( -1 );
+ }
+
+ if ( ns ) {
+ char c1;
+ int nr;
+
+ nr = read ( fileno(stdin), &c1, 1 );
+ c1 &= 0xff;
+ if ( !esc_seen ) {
+ if ( c1 == 27 )
+ esc_seen++;
+ else
+ xmit_byte ( c1, 0 );
+ } else {
+ if ( c1 == 27 )
+ finish( -1 );
+ else {
+ xmit_byte ( 27, 0 );
+ esc_seen = 0;
+ }
+ xmit_byte ( c1, 0 );
+ }
+ }
+
+ /*
+ * Check for data from the i960
+ */
+ if ( CP_READ(Uart->mon_xmithost) & UART_VALID ) {
+ c = getbyte();
+ putchar ( c );
+ }
+ if ( strcmp ( line, "Mon960" ) == 0 )
+ autobaud();
+
+ }
+ } else {
+ /*
+ * Make sure the driver is loaded and that the CP
+ * is ready for commands
+ */
+ if ( CP_READ(Mon->mon_bstat) == BOOT_RUNNING )
+ {
+ fprintf ( stderr,
+ "%s is up and running - no download allowed.\n",
+ air->acp_intf );
+ (void) close(fd);
+ continue;
+ }
+
+ if ( CP_READ(Mon->mon_bstat) != BOOT_MONREADY )
+ {
+ fprintf ( stderr,
+ "%s is not ready for downloading.\n",
+ air->acp_intf );
+ (void) close(fd);
+ continue;
+ }
+
+ /*
+ * Indicate who we're downloading
+ */
+ if ( verbose )
+ printf ( "Downloading code for %s\n",
+ air->acp_intf );
+
+ /*
+ * Look for the i960 monitor message.
+ * We should see this after a board reset.
+ */
+ while ( strncmp ( line, "Mon960", 6 ) != 0 &&
+ strncmp ( line, "=>", 2 ) != 0 )
+ getline( verbose ); /* Verbose */
+
+ /*
+ * Autobaud fakery
+ */
+ if ( strncmp ( line, "Mon960", 6 ) == 0 ) {
+ xmit_to_i960 ( "\r\n\r\n\r\n\r\n", 8, 0 );
+ delay ( 10000 );
+ }
+
+ /*
+ * Keep reading until we get a command prompt
+ */
+ while ( strncmp ( line, "=>", 2 ) != 0 )
+ getline( verbose ); /* Verbose */
+
+ /*
+ * Choose the correct microcode file based on the
+ * adapter type the card claims to be.
+ */
+ switch ( air->acp_device )
+ {
+ case DEV_FORE_SBA200:
+ sprintf ( base, "sba200" );
+ break;
+
+ case DEV_FORE_SBA200E:
+ sprintf ( base, "sba200e" );
+ break;
+
+ case DEV_FORE_PCA200E:
+ sprintf ( base, "pca200e" );
+ break;
+
+ default:
+ err = 1;
+ fprintf(stderr, "Unknown adapter type: %d\n",
+ air->acp_device );
+ }
+
+ sndfile = NULL;
+
+ if ( objfile == NULL ) {
+ switch ( air->acp_device ) {
+ case DEV_FORE_SBA200:
+ case DEV_FORE_SBA200E:
+ sprintf ( filename, "%s.bin%d", base,
+ air->acp_bustype );
+ if ( stat ( filename, &sbuf ) == -1 ) {
+ sprintf ( filename, "%s/%s.bin%d",
+ dirname, base,
+ air->acp_bustype );
+ if ( stat ( filename, &sbuf ) == -1 ) {
+ ext = 1;
+ sprintf ( filename, "%s.objd%d",
+ base, air->acp_bustype );
+ if ( stat(filename, &sbuf) == -1 ) {
+ sprintf ( filename,
+ "%s/%s.objd%d", dirname,
+ base,
+ air->acp_bustype );
+ if ( stat ( filename, &sbuf ) != -1 )
+ sndfile = filename;
+ } else
+ sndfile = filename;
+ } else
+ sndfile = filename;
+ } else
+ sndfile = filename;
+ break;
+ case DEV_FORE_PCA200E:
+ /* Use compiled in microcode */
+ if (pca_vers == 3) {
+ ucode = pca200e_microcode_3;
+ ucode_size = pca200e_microcode_size_3;
+ } else {
+ ucode = pca200e_microcode_4;
+ ucode_size = pca200e_microcode_size_4;
+ }
+ break;
+ default:
+ break;
+ }
+ } else
+ sndfile = objfile;
+
+ if ( ext && !binary )
+ err = xmitfile ( sndfile );
+ else if (sndfile != NULL)
+ err = sendbinfile ( sndfile, ram );
+ else
+ err = loadmicrocode( ucode, ucode_size, ram );
+
+ if ( err ) {
+ fprintf(stderr, "%s download failed\n",
+ air->acp_intf);
+ (void) close(fd);
+ continue;
+ }
+
+ /*
+ * Download completed - wait around a while for
+ * the driver to initialize the adapter
+ */
+ aap = (Aali *)(void *)(ram + CP_READ(Mon->mon_appl));
+ for (i = 0; i < MAX_CHECK; i++, sleep(1)) {
+ uint32_t hb1, hb2, hb3;
+
+ hb3 = CP_READ(Mon->mon_bstat);
+ if (hb3 != BOOT_RUNNING) {
+ if (verbose)
+ printf("bstat %x\n", hb3);
+ continue;
+ }
+
+ hb1 = CP_READ(aap->aali_heartbeat);
+ delay(1);
+ hb2 = CP_READ(aap->aali_heartbeat);
+ if (verbose)
+ printf("hb %x %x\n", hb1, hb2);
+ if (hb1 < hb2)
+ break;
+ }
+ }
+
+ close ( fd );
+ }
+ }
+
+ /*
+ * Exit
+ */
+ exit (0);
+}
diff --git a/sbin/atm/fore_dnld/pca200e.c b/sbin/atm/fore_dnld/pca200e.c
new file mode 100644
index 0000000..c208a4d
--- /dev/null
+++ b/sbin/atm/fore_dnld/pca200e.c
@@ -0,0 +1,3796 @@
+/*
+ * (Copyright Notice)
+ *
+ * Copyright (c) 1995-2000 FORE Systems, Inc., as an unpublished work.
+ *
+ * This notice does not imply unrestricted or public access to these
+ * materials which are a trade secret of FORE Systems, Inc. or its
+ * subsidiaries or affiliates (together referred to as "FORE"), and
+ * which may not be reproduced, used, sold or transferred to any third
+ * party without FORE's prior written consent. All rights reserved.
+ *
+ * U.S. Government Restricted Rights.
+ * If you are licensing the Software on behalf of the U.S. Government
+ * ("Government"), the following provisions apply to you. If the
+ * software is supplied to the Department of Defense ("DoD"), it is
+ * classified as "Commercial Computer Software" under paragraph
+ * 252.227-7014 of the DoD Supplement to the Federal Acquisition
+ * Regulations ("DFARS") (or any successor regulations) and the
+ * Government is acquiring only the license rights granted herein (the
+ * license rights customarily provided to non-Government users). If
+ * the Software is supplied to any unit or agency of the Government
+ * other than the DoD, it is classified as "Restricted Computer
+ * Software" and the Government's rights in the Software are defined
+ * in paragraph 52.227-19 of the Federal Acquisition Regulations
+ * ("FAR") (or any successor regulations) or, in the cases of NASA,
+ * in paragraph 18.52.227-86 of the NASA Supplement to the FAR (or
+ * any successor regulations).
+ *
+ * FORE Systems is a registered trademark, and ForeRunner, ForeRunnerLE,
+ * and ForeThought are trademarks of FORE Systems, Inc. All other
+ * brands or product names are trademarks or registered trademarks of
+ * their respective holders.
+ *
+ * (End Copyright Notice)
+ *
+ * This are binary copies of the PCA200E firmware versions 3.0.1 and
+ * 4.1.12
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+u_char pca200e_microcode_3[] = {
+102,111,114,101,0,1,0,0,0,82,0,0,208,92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,77,0,0,0,4,0,0,0,0,0,0,0,0,0,0,3,
+0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,96,244,0,0,0,0,0,0,1,255,128,255,
+0,0,0,0,1,255,255,255,113,0,0,0,1,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0,
+0,0,0,0,0,1,231,167,167,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,223,111,223,0,0,0,0,1,
+127,0,0,0,0,0,0,1,255,0,0,0,0,0,0,1,255,0,0,0,0,0,0,1,127,65,127,0,0,0,
+0,1,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,251,0,251,0,0,0,0,1,
+255,240,240,0,0,0,0,1,255,0,0,0,0,0,0,1,255,0,0,0,0,0,0,1,15,0,0,0,0,0,
+0,1,255,0,0,0,0,0,0,1,255,0,0,0,0,0,0,1,15,0,0,0,0,0,0,1,127,1,127,0,0,
+0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,173,0,129,0,0,0,0,1,175,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,1,255,175,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,255,0,0,0,0,0,0,1,255,255,0,0,0,0,0,1,255,0,0,0,0,0,0,1,255,0,
+0,0,0,0,0,1,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,1,135,3,135,0,0,0,0,1,63,62,63,0,0,0,0,1,127,127,127,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,255,255,255,0,0,0,0,1,255,255,
+255,144,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,255,255,255,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,1,191,63,63,4,0,0,0,1,255,224,224,0,0,0,0,1,255,255,
+255,0,0,0,0,1,255,255,255,0,0,0,0,1,255,0,0,0,0,0,0,1,255,255,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,255,159,159,4,0,0,0,1,255,255,255,0,0,0,0,1,255,255,255,
+106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,31,5,0,0,0,0,0,0,0,0,1,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,129,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,130,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,144,86,0,0,176,
+86,0,0,208,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,0,0,0,
+0,0,0,0,160,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,250,0,0,0,0,0,0,64,156,0,0,
+0,0,0,0,80,195,0,0,0,0,0,0,36,244,0,0,0,0,0,128,150,152,0,0,0,0,0,32,188,
+190,0,0,0,0,0,40,107,238,0,0,0,0,0,249,2,149,0,0,0,0,64,183,67,186,0,0,
+0,0,16,165,212,232,0,0,0,0,42,231,132,145,0,0,0,128,244,32,230,181,0,0,
+0,160,49,169,95,227,0,0,0,0,4,0,0,0,7,0,0,0,10,0,0,0,14,0,0,0,17,0,0,0,
+20,0,0,0,24,0,0,0,27,0,0,0,30,0,0,0,34,0,0,0,37,0,0,0,40,0,0,0,44,0,0,0,
+47,0,0,0,50,0,0,0,255,255,255,255,255,255,255,255,0,0,0,4,191,201,27,142,
+157,181,112,43,168,173,197,157,214,149,67,14,5,141,41,175,213,166,207,255,
+73,31,120,194,251,247,218,135,143,122,231,215,162,20,155,197,22,171,179,
+239,158,50,35,153,192,173,15,133,223,140,233,128,201,71,186,147,231,166,
+211,168,197,185,2,164,170,23,230,127,43,161,22,182,147,191,155,133,145,
+162,40,202,106,85,39,57,141,247,112,224,69,97,130,55,53,12,46,249,47,201,
+60,227,255,150,82,138,101,23,191,214,243,166,145,153,141,222,249,157,251,
+235,126,170,130,188,157,167,74,209,73,189,140,47,106,92,25,252,38,210,253,
+131,124,36,32,223,80,233,0,0,0,0,54,0,0,0,107,0,0,0,160,0,0,0,213,0,0,0,
+10,1,0,0,63,1,0,0,117,1,0,0,170,1,0,0,223,1,0,0,20,2,0,0,73,2,0,0,126,2,
+0,0,179,2,0,0,233,2,0,0,30,3,0,0,83,3,0,0,136,3,0,0,189,3,0,0,242,3,0,0,
+255,255,255,255,255,255,255,255,204,204,204,204,204,204,204,204,10,215,
+163,112,61,10,215,163,59,223,79,141,151,110,18,131,43,101,25,226,88,23,
+183,209,35,132,71,27,71,172,197,167,181,105,108,175,5,189,55,134,188,66,
+122,229,213,148,191,214,252,206,97,132,17,119,204,171,151,165,180,54,65,
+95,112,137,190,213,237,189,206,254,230,219,254,170,36,203,11,255,235,175,
+203,136,80,111,9,204,188,140,19,14,180,75,66,19,46,225,15,216,92,9,53,220,
+36,180,217,172,176,58,247,124,29,144,0,0,0,0,253,255,255,255,250,255,255,
+255,247,255,255,255,243,255,255,255,240,255,255,255,237,255,255,255,233,
+255,255,255,230,255,255,255,227,255,255,255,223,255,255,255,220,255,255,
+255,217,255,255,255,213,255,255,255,210,255,255,255,207,255,255,255,255,
+255,255,255,255,255,255,255,91,225,77,196,190,148,149,230,186,148,57,69,
+173,30,177,207,89,193,126,177,83,124,18,187,165,233,57,165,39,234,127,168,
+165,25,9,107,186,96,197,151,155,83,117,253,247,2,180,136,125,211,101,240,
+188,53,67,246,160,228,188,100,124,70,208,221,200,130,83,124,110,186,202,
+199,147,160,115,219,147,224,244,179,47,0,203,56,219,39,23,162,6,204,35,
+84,119,131,255,145,96,188,164,61,169,222,128,131,189,78,49,74,236,60,229,
+236,214,225,50,207,205,95,96,213,57,25,122,99,37,67,49,192,102,59,228,94,
+171,142,28,173,119,197,106,131,98,206,236,155,37,73,11,186,217,220,113,
+140,0,0,0,0,203,255,255,255,150,255,255,255,97,255,255,255,44,255,255,255,
+247,254,255,255,194,254,255,255,140,254,255,255,87,254,255,255,34,254,255,
+255,237,253,255,255,184,253,255,255,131,253,255,255,78,253,255,255,24,253,
+255,255,227,252,255,255,174,252,255,255,121,252,255,255,68,252,255,255,
+15,252,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,10,0,0,
+0,0,0,0,0,100,0,0,0,0,0,0,0,232,3,0,0,0,0,0,0,16,39,0,0,0,0,0,0,160,134,
+1,0,0,0,0,0,64,66,15,0,0,0,0,0,128,150,152,0,0,0,0,0,0,225,245,5,0,0,0,
+0,0,202,154,59,0,0,0,0,0,228,11,84,2,0,0,0,0,232,118,72,23,0,0,0,0,16,165,
+212,232,0,0,0,0,160,114,78,24,9,0,0,0,64,122,16,243,90,0,0,0,128,198,164,
+126,141,3,0,0,0,193,111,242,134,35,0,0,0,138,93,120,69,99,1,0,0,100,167,
+179,182,224,13,0,0,232,137,4,35,199,138,1,0,0,0,10,0,0,0,100,0,0,0,232,
+3,0,0,16,39,0,0,160,134,1,0,64,66,15,0,128,150,152,0,0,225,245,5,0,202,
+154,59,0,0,0,0,0,0,0,0,0,48,128,140,112,218,3,0,12,192,135,50,16,22,8,92,
+4,0,0,9,128,62,0,102,0,0,0,140,0,30,240,92,0,48,128,140,32,242,0,0,0,48,
+136,140,112,202,3,0,0,16,244,146,4,8,132,89,248,95,132,52,0,48,128,140,
+0,16,0,59,204,129,0,9,252,148,0,9,20,128,0,9,112,98,0,9,140,119,0,9,0,48,
+48,144,32,242,0,0,40,160,1,50,16,22,32,93,0,20,128,140,0,0,0,0,0,48,136,
+140,64,93,0,0,17,1,132,89,16,128,49,89,0,144,1,134,4,22,128,93,72,0,0,9,
+228,97,0,8,84,65,88,73,45,49,48,48,0,0,0,0,0,0,0,0,84,65,88,73,45,49,52,
+48,0,0,0,0,0,0,0,0,83,79,78,69,84,95,79,67,51,0,0,0,0,0,0,0,85,78,75,78,
+79,87,78,0,0,0,0,0,0,0,0,0,32,96,8,140,188,46,0,9,104,43,0,9,0,48,160,144,
+68,20,0,80,63,0,40,140,64,224,47,130,8,222,40,89,148,64,33,88,65,224,247,
+130,4,22,128,92,4,42,0,9,136,25,40,88,78,64,33,58,18,64,33,49,30,32,1,58,
+16,1,0,8,0,0,0,0,137,25,40,88,102,64,33,58,0,1,0,8,0,0,0,0,0,48,160,144,
+84,82,0,0,13,30,40,92,64,224,135,140,0,48,40,130,80,243,0,0,0,48,136,140,
+96,93,0,0,5,30,40,92,40,0,0,8,0,48,160,144,84,82,0,0,9,30,40,92,64,224,
+135,140,0,48,40,130,80,243,0,0,0,48,136,140,112,93,0,0,4,30,40,92,176,32,
+45,146,16,106,0,11,180,0,0,8,0,48,160,136,2,4,0,136,8,30,40,92,0,48,40,
+130,80,243,0,0,148,208,169,88,26,96,13,55,0,48,160,144,84,82,0,0,6,30,40,
+92,92,0,0,8,0,0,0,0,149,16,161,88,46,32,21,48,26,96,5,48,0,48,160,144,84,
+82,0,0,22,30,40,92,60,0,0,8,0,0,0,0,0,48,160,144,84,82,0,0,31,216,41,89,
+40,0,0,8,26,96,5,48,0,48,160,144,84,82,0,0,31,216,45,89,20,0,0,8,0,0,0,
+0,0,48,160,144,84,82,0,0,70,0,40,140,176,32,45,146,64,224,135,140,0,48,
+136,140,128,93,0,0,116,105,0,11,24,0,0,8,0,0,0,0,64,224,135,140,0,48,136,
+140,144,93,0,0,92,105,0,11,32,44,0,9,44,50,0,9,168,39,0,9,108,41,0,11,137,
+25,40,88,54,64,33,61,0,30,128,92,80,224,143,140,136,89,0,11,38,32,4,58,
+80,224,167,144,0,48,168,144,84,82,0,0,127,0,40,140,148,64,161,88,180,96,
+165,146,24,0,0,8,0,0,0,0,0,48,160,144,84,82,0,0,1,25,40,89,180,32,45,146,
+0,48,40,140,80,242,0,0,0,48,40,146,48,242,0,0,152,42,0,9,0,48,48,140,255,
+255,0,0,31,88,62,89,0,48,64,140,96,0,0,32,0,48,72,140,112,243,0,0,0,48,
+80,140,128,243,0,0,0,48,88,140,144,243,0,0,0,48,96,140,160,243,0,0,0,48,
+104,140,176,243,0,0,0,48,112,140,192,243,0,0,0,48,120,140,208,243,0,0,0,
+48,24,140,224,243,0,0,0,48,168,144,64,244,0,0,0,80,165,144,20,80,160,89,
+0,80,165,146,0,48,160,144,48,242,0,0,0,16,133,144,0,13,0,9,0,48,160,144,
+48,242,0,0,0,48,168,144,32,20,0,80,4,32,165,144,149,48,0,90,0,48,160,146,
+48,242,0,0,42,0,0,21,0,48,160,144,36,20,0,80,30,32,5,61,0,48,160,144,40,
+20,0,80,18,32,5,61,0,48,160,144,44,20,0,80,8,32,5,58,148,56,0,9,0,48,128,
+144,16,82,0,0,18,32,4,58,36,13,0,9,0,48,240,146,16,82,0,0,0,48,136,144,
+128,180,3,0,0,80,164,144,102,33,5,58,0,80,132,144,0,80,140,144,0,48,144,
+144,52,242,0,0,31,29,40,88,144,208,131,88,145,64,137,88,220,28,0,9,0,48,
+168,144,52,242,0,0,4,96,165,144,24,222,43,89,6,96,141,128,148,128,145,88,
+148,64,161,88,24,12,165,89,74,32,37,58,14,32,37,60,18,32,5,58,140,0,0,8,
+98,32,45,58,132,0,0,8,0,48,176,144,52,242,0,0,0,30,152,92,0,144,41,140,
+8,160,165,144,0,144,133,144,22,16,180,89,148,64,169,88,16,12,165,89,28,
+78,0,9,88,0,0,8,0,0,0,0,0,48,176,144,52,242,0,0,0,30,152,92,8,160,165,144,
+0,144,133,144,22,16,180,89,148,128,169,88,16,12,165,89,192,79,0,9,44,0,
+0,8,0,48,176,144,52,242,0,0,0,30,152,92,0,144,41,140,8,160,165,144,0,144,
+133,144,22,16,180,89,148,64,169,88,16,12,165,89,196,72,0,9,0,48,160,144,
+128,180,3,0,4,32,181,144,0,16,245,146,0,48,168,144,132,180,3,0,20,16,162,
+89,0,48,160,146,128,180,3,0,22,64,165,54,0,48,160,144,136,180,3,0,0,48,
+160,146,128,180,3,0,0,48,160,144,52,242,0,0,0,48,176,146,56,0,0,32,2,30,
+40,92,0,48,40,146,96,0,0,48,4,32,165,144,28,12,165,89,46,32,5,58,0,48,168,
+144,40,82,0,0,255,0,40,140,21,80,168,89,149,64,161,88,136,9,165,88,0,48,
+168,146,40,82,0,0,0,48,160,146,4,0,0,32,0,48,160,144,56,242,0,0,0,16,165,
+144,127,0,40,140,148,64,161,88,246,61,5,58,2,9,165,89,102,34,61,52,20,57,
+160,144,232,97,0,0,0,16,5,132,0,0,0,0,8,98,0,0,108,100,0,0,32,98,0,0,108,
+100,0,0,56,98,0,0,144,99,0,0,176,99,0,0,216,99,0,0,0,48,128,144,56,242,
+0,0,80,47,0,9,16,22,232,92,88,2,0,8,0,0,0,0,0,48,128,144,56,242,0,0,168,
+49,0,9,16,22,232,92,64,2,0,8,0,0,0,0,0,48,160,144,68,20,0,80,2,30,232,92,
+30,32,157,48,147,9,165,88,0,48,240,146,52,244,0,0,0,48,160,146,68,20,0,
+80,16,0,0,8,1,30,40,92,0,48,40,146,52,244,0,0,0,48,160,144,56,242,0,0,4,
+32,165,144,0,48,184,140,96,243,0,0,0,48,160,146,84,0,0,32,0,48,56,146,96,
+0,0,32,0,208,133,176,0,16,130,178,0,80,130,176,0,16,130,178,0,144,130,176,
+0,16,130,178,0,208,130,176,0,16,130,178,0,16,131,176,0,16,130,178,0,80,
+131,176,0,16,130,178,0,144,131,176,0,16,130,178,0,208,131,176,0,16,130,
+178,0,208,128,176,0,16,130,178,0,48,160,140,240,243,0,0,0,16,133,176,0,
+16,130,178,0,48,160,140,0,244,0,0,0,16,133,176,0,16,130,178,0,48,160,140,
+16,244,0,0,0,16,133,176,0,48,184,140,32,244,0,0,0,16,130,178,0,208,165,
+144,0,48,160,146,96,0,0,32,23,16,169,89,0,80,165,144,0,48,160,146,96,0,
+0,32,23,16,170,89,0,80,165,144,23,16,171,89,0,48,160,146,96,0,0,32,0,80,
+165,144,0,48,160,146,96,0,0,32,23,16,172,89,0,80,165,144,0,48,160,146,96,
+0,0,32,23,16,173,89,0,80,165,144,0,48,160,146,96,0,0,32,23,16,174,89,0,
+80,165,144,23,16,175,89,0,48,160,146,96,0,0,32,0,80,165,144,0,48,160,146,
+96,0,0,32,228,0,0,8,0,48,128,144,56,242,0,0,80,89,0,11,2,30,232,92,210,
+32,4,61,10,30,232,92,200,0,0,8,0,0,0,0,128,90,0,9,0,48,160,144,56,242,0,
+0,4,32,141,144,16,30,128,92,0,48,144,140,240,193,3,0,228,25,0,9,2,30,232,
+92,156,0,0,8,0,48,160,144,56,242,0,0,4,32,165,144,0,48,160,146,64,0,0,32,
+0,48,160,144,0,225,1,240,0,48,160,146,96,0,0,48,0,48,160,144,4,225,1,240,
+0,48,160,146,96,0,0,48,0,48,160,144,100,224,1,240,0,48,160,146,96,0,0,48,
+0,48,160,144,104,224,1,240,2,30,232,92,0,48,160,146,96,0,0,48,64,0,0,8,
+0,0,0,0,0,48,160,144,56,242,0,0,0,16,165,144,0,48,40,140,94,208,94,208,
+34,64,161,61,0,48,160,144,64,244,0,0,0,30,128,92,0,48,40,140,13,0,173,222,
+0,16,45,146,216,90,0,9,8,30,232,92,0,48,176,144,56,242,0,0,0,144,165,144,
+118,32,61,48,16,160,189,144,0,144,245,146,0,48,168,144,60,242,0,0,32,160,
+165,140,0,48,160,146,56,242,0,0,22,64,165,54,0,48,160,144,64,242,0,0,0,
+48,160,146,56,242,0,0,0,48,168,144,40,82,0,0,0,48,184,146,56,0,0,32,255,
+0,40,140,0,48,232,146,96,0,0,48,21,80,168,89,149,64,161,88,136,9,165,88,
+0,48,168,146,40,82,0,0,0,48,160,146,4,0,0,32,80,0,0,8,0,0,0,0,0,48,160,
+144,56,242,0,0,16,32,181,144,0,16,245,146,0,48,168,144,60,242,0,0,32,32,
+165,140,0,48,160,146,56,242,0,0,22,64,165,54,0,48,160,144,64,242,0,0,0,
+48,160,146,56,242,0,0,0,48,176,146,56,0,0,32,0,48,232,146,96,0,0,48,0,48,
+160,144,56,242,0,0,0,16,165,144,127,0,40,140,148,64,161,88,132,60,5,61,
+112,250,255,8,0,0,0,0,0,0,0,0,0,0,0,0,16,22,32,93,44,27,0,9,132,208,163,
+88,255,0,144,140,148,128,164,88,62,32,5,61,133,48,0,90,0,48,232,140,0,0,
+0,48,42,0,0,22,0,80,167,176,0,16,161,178,0,80,167,176,4,73,41,89,16,32,
+33,140,133,48,0,90,0,16,161,178,4,16,36,89,224,255,255,17,0,0,0,10,0,48,
+160,140,0,0,0,48,0,16,165,152,4,73,41,89,133,48,0,90,0,16,161,154,4,16,
+34,89,0,48,152,140,0,0,0,48,42,0,0,22,0,208,164,176,0,16,161,178,0,208,
+164,176,4,73,41,89,16,32,33,140,133,48,0,90,0,16,161,178,4,16,36,89,224,
+255,255,17,0,48,160,140,0,0,0,48,0,16,165,176,0,16,161,178,0,48,160,140,
+0,0,0,48,0,16,165,152,4,16,36,89,0,16,161,154,0,0,0,10,0,0,0,0,0,0,0,0,
+0,0,0,0,28,32,180,144,12,32,172,144,0,48,160,144,40,82,0,0,0,144,181,144,
+20,80,160,89,0,48,160,146,40,82,0,0,145,48,0,90,255,0,184,140,149,56,168,
+140,0,0,0,0,0,48,176,146,48,0,0,32,0,48,168,146,96,0,0,32,148,192,165,88,
+0,48,160,146,4,0,0,32,10,0,0,18,224,6,0,8,0,48,128,146,16,82,0,0,0,0,0,
+10,0,48,160,144,16,82,0,0,16,22,40,92,16,32,36,144,22,32,5,58,20,22,128,
+92,184,6,0,9,0,48,240,146,16,82,0,0,12,96,161,144,0,48,168,144,112,180,
+3,0,20,1,161,89,14,64,165,52,1,30,128,92,0,0,0,10,0,48,160,144,112,242,
+0,0,154,32,5,58,0,48,128,176,112,242,0,0,0,48,48,152,136,242,0,0,1,9,164,
+89,0,16,133,140,0,48,160,146,112,242,0,0,148,160,4,90,148,157,185,140,6,
+1,0,19,0,208,161,144,254,32,5,58,0,208,177,144,0,48,168,144,124,242,0,0,
+0,48,160,144,40,82,0,0,0,48,176,146,48,0,0,32,0,48,232,140,112,242,0,0,
+0,48,232,146,16,82,0,0,255,0,232,140,149,56,168,140,0,0,0,0,20,80,160,89,
+0,48,168,146,96,0,0,32,148,64,183,88,0,48,160,146,40,82,0,0,0,48,176,146,
+4,0,0,32,160,0,0,8,0,0,0,0,0,48,160,144,32,244,0,0,0,48,176,144,140,242,
+0,0,20,80,160,89,0,48,160,146,32,244,0,0,0,144,165,144,146,32,5,58,0,144,
+181,144,0,48,168,144,40,82,0,0,0,48,160,144,124,242,0,0,0,48,176,146,48,
+0,0,32,148,56,160,140,0,0,0,0,0,48,160,146,96,0,0,32,21,80,168,89,255,0,
+232,140,0,48,168,146,40,82,0,0,149,64,175,88,0,48,128,140,112,242,0,0,0,
+48,168,146,4,0,0,32,124,5,0,9,0,48,160,144,112,242,0,0,0,48,168,144,136,
+242,0,0,1,9,165,89,0,48,160,146,112,242,0,0,148,93,189,140,0,208,165,144,
+0,16,161,146,4,16,34,89,16,96,33,146,4,224,133,144,0,0,0,10,168,254,255,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,48,160,144,16,82,0,0,16,22,40,92,16,32,36,144,
+22,32,5,58,20,22,128,92,24,5,0,9,0,48,240,146,16,82,0,0,12,96,161,144,0,
+48,168,144,112,180,3,0,20,1,161,89,14,64,165,52,1,30,128,92,0,0,0,10,0,
+48,160,144,160,242,0,0,146,32,5,58,0,48,128,176,160,242,0,0,0,48,48,152,
+184,242,0,0,1,9,164,89,0,16,133,140,0,48,160,146,160,242,0,0,148,160,4,
+90,148,157,185,140,250,0,0,19,0,208,161,144,242,32,5,58,0,208,177,144,0,
+48,168,144,172,242,0,0,0,48,160,144,40,82,0,0,0,48,176,146,48,0,0,32,0,
+48,232,140,160,242,0,0,0,48,232,146,16,82,0,0,255,0,232,140,1,78,173,89,
+1,32,165,140,0,48,168,146,96,0,0,32,148,64,183,88,0,48,160,146,40,82,0,
+0,0,48,176,146,4,0,0,32,152,0,0,8,0,48,160,144,36,244,0,0,0,48,176,144,
+188,242,0,0,20,80,160,89,0,48,160,146,36,244,0,0,0,144,165,144,146,32,5,
+58,0,144,181,144,0,48,168,144,40,82,0,0,0,48,160,144,172,242,0,0,0,48,176,
+146,48,0,0,32,1,14,165,89,0,48,160,146,96,0,0,32,21,80,168,89,255,0,232,
+140,0,48,168,146,40,82,0,0,149,64,175,88,0,48,128,140,160,242,0,0,0,48,
+168,146,4,0,0,32,232,3,0,9,0,48,160,144,160,242,0,0,0,48,168,144,184,242,
+0,0,1,9,165,89,0,48,160,146,160,242,0,0,148,93,189,140,0,208,165,144,0,
+16,161,146,4,16,34,89,16,96,33,146,4,224,133,144,0,0,0,10,0,0,0,0,176,254,
+255,8,0,0,0,0,0,48,160,144,16,82,0,0,16,22,40,92,16,32,36,144,22,32,5,58,
+20,22,128,92,136,3,0,9,0,48,240,146,16,82,0,0,12,96,161,144,0,48,168,144,
+112,180,3,0,20,1,161,89,14,64,165,52,1,30,128,92,0,0,0,10,0,48,160,144,
+208,242,0,0,146,32,5,58,0,48,128,176,208,242,0,0,0,48,48,152,232,242,0,
+0,1,9,164,89,0,16,133,140,0,48,160,146,208,242,0,0,148,160,4,90,148,157,
+185,140,250,0,0,19,0,208,161,144,242,32,5,58,0,208,177,144,0,48,168,144,
+220,242,0,0,0,48,160,144,40,82,0,0,0,48,176,146,48,0,0,32,0,48,232,140,
+208,242,0,0,0,48,232,146,16,82,0,0,255,0,232,140,1,78,173,89,1,32,165,140,
+0,48,168,146,96,0,0,32,148,64,183,88,0,48,160,146,40,82,0,0,0,48,176,146,
+4,0,0,32,152,0,0,8,0,48,160,144,40,244,0,0,0,48,176,144,236,242,0,0,20,
+80,160,89,0,48,160,146,40,244,0,0,0,144,165,144,146,32,5,58,0,144,181,144,
+0,48,168,144,40,82,0,0,0,48,160,144,220,242,0,0,0,48,176,146,48,0,0,32,
+1,14,165,89,0,48,160,146,96,0,0,32,21,80,168,89,255,0,232,140,0,48,168,
+146,40,82,0,0,149,64,175,88,0,48,128,140,208,242,0,0,0,48,168,146,4,0,0,
+32,88,2,0,9,0,48,160,144,208,242,0,0,0,48,168,144,232,242,0,0,1,9,165,89,
+0,48,160,146,208,242,0,0,148,93,189,140,0,208,165,144,0,16,161,146,4,16,
+34,89,16,96,33,146,4,224,133,144,0,0,0,10,0,0,0,0,176,254,255,8,0,0,0,0,
+0,48,160,144,16,82,0,0,16,22,40,92,16,32,36,144,22,32,5,58,20,22,128,92,
+248,1,0,9,0,48,240,146,16,82,0,0,12,96,161,144,0,48,168,144,112,180,3,0,
+20,1,161,89,14,64,165,52,1,30,128,92,0,0,0,10,0,48,160,144,0,243,0,0,146,
+32,5,58,0,48,128,176,0,243,0,0,0,48,48,152,24,243,0,0,1,9,164,89,0,16,133,
+140,0,48,160,146,0,243,0,0,148,160,4,90,148,157,185,140,250,0,0,19,0,208,
+161,144,242,32,5,58,0,208,177,144,0,48,168,144,12,243,0,0,0,48,160,144,
+40,82,0,0,0,48,176,146,48,0,0,32,0,48,232,140,0,243,0,0,0,48,232,146,16,
+82,0,0,255,0,232,140,1,78,173,89,1,32,165,140,0,48,168,146,96,0,0,32,148,
+64,183,88,0,48,160,146,40,82,0,0,0,48,176,146,4,0,0,32,152,0,0,8,0,48,160,
+144,44,244,0,0,0,48,176,144,28,243,0,0,20,80,160,89,0,48,160,146,44,244,
+0,0,0,144,165,144,146,32,5,58,0,144,181,144,0,48,168,144,40,82,0,0,0,48,
+160,144,12,243,0,0,0,48,176,146,48,0,0,32,1,14,165,89,0,48,160,146,96,0,
+0,32,21,80,168,89,255,0,232,140,0,48,168,146,40,82,0,0,149,64,175,88,0,
+48,128,140,0,243,0,0,0,48,168,146,4,0,0,32,200,0,0,9,0,48,160,144,0,243,
+0,0,0,48,168,144,24,243,0,0,1,9,165,89,0,48,160,146,0,243,0,0,148,93,189,
+140,0,208,165,144,0,16,161,146,4,16,34,89,16,96,33,146,4,224,133,144,0,
+0,0,10,0,0,0,0,176,254,255,8,0,0,0,0,16,22,32,92,0,48,128,144,16,82,0,0,
+18,32,4,58,112,0,0,9,0,48,240,146,16,82,0,0,28,32,177,144,0,144,165,144,
+86,32,5,58,0,16,161,152,78,64,165,57,0,144,181,144,12,32,169,144,0,48,160,
+144,40,82,0,0,0,48,32,146,16,82,0,0,0,48,176,146,48,0,0,32,255,0,184,140,
+1,78,173,89,1,32,165,140,0,48,168,146,96,0,0,32,148,192,181,88,0,48,160,
+146,40,82,0,0,0,48,176,146,4,0,0,32,0,0,0,10,0,0,0,0,0,16,68,176,24,32,
+164,144,16,22,40,92,136,29,37,140,32,19,0,9,255,0,136,140,132,208,163,88,
+148,64,164,88,148,48,0,90,0,208,130,140,66,0,0,21,139,48,0,90,0,48,152,
+140,0,0,0,48,162,0,0,22,0,208,164,176,0,16,161,178,0,208,164,176,4,9,132,
+89,16,32,33,140,144,48,0,90,0,16,161,178,4,16,36,89,224,255,255,17,120,
+0,0,8,0,0,0,0,0,48,160,140,0,0,0,48,0,16,165,152,4,201,130,89,144,48,0,
+90,0,16,161,154,4,16,34,89,0,48,144,140,0,0,0,48,42,0,0,22,0,144,164,176,
+0,16,161,178,0,144,164,176,4,9,132,89,16,32,33,140,144,48,0,90,0,16,161,
+178,4,16,36,89,224,255,255,17,0,48,160,140,0,0,0,48,0,16,165,176,0,16,161,
+178,0,48,160,140,0,0,0,48,0,16,165,152,4,16,36,89,0,16,161,154,5,16,175,
+89,0,80,165,144,4,32,133,144,0,16,245,146,0,80,165,144,32,96,177,152,20,
+16,162,89,10,192,165,54,22,22,160,92,0,80,165,146,0,48,128,146,56,0,0,32,
+2,30,136,92,0,48,136,146,96,0,0,48,0,80,161,176,20,192,165,89,0,80,161,
+146,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,64,96,8,140,0,116,192,178,240,255,
+255,255,120,32,164,144,0,48,160,146,128,242,0,0,136,32,172,144,0,48,168,
+146,176,242,0,0,152,32,180,144,0,48,176,146,224,242,0,0,16,22,104,92,168,
+32,188,144,64,32,132,140,148,48,0,90,0,48,184,146,16,243,0,0,34,0,0,21,
+30,96,5,61,26,160,5,61,22,224,5,61,8,30,128,92,0,116,192,176,240,255,255,
+255,0,0,0,10,52,32,180,144,0,48,176,146,132,242,0,0,68,32,164,144,0,48,
+160,146,180,242,0,0,84,32,164,144,0,48,160,146,228,242,0,0,0,48,160,144,
+128,242,0,0,100,32,172,144,148,48,0,90,0,48,168,146,20,243,0,0,14,0,0,18,
+63,0,24,140,174,223,176,62,0,48,160,144,176,242,0,0,22,32,5,58,0,48,160,
+144,180,242,0,0,63,0,24,140,146,223,160,62,0,48,160,144,224,242,0,0,22,
+32,5,58,0,48,160,144,228,242,0,0,63,0,24,140,118,223,160,62,0,48,160,144,
+16,243,0,0,22,32,5,58,0,48,160,144,20,243,0,0,63,0,24,140,90,223,160,62,
+0,48,160,144,128,242,0,0,38,32,5,58,0,48,160,144,176,242,0,0,26,32,5,58,
+0,48,168,144,132,242,0,0,0,48,160,144,180,242,0,0,46,31,173,57,0,48,160,
+144,224,242,0,0,38,32,5,58,0,48,160,144,16,243,0,0,26,32,5,58,0,48,168,
+144,228,242,0,0,0,48,160,144,20,243,0,0,2,31,173,57,0,48,160,144,128,242,
+0,0,46,32,5,61,0,48,160,144,180,242,0,0,0,48,24,140,48,104,0,0,0,48,24,
+146,48,243,0,0,0,48,160,146,132,242,0,0,24,0,0,8,0,0,0,0,0,48,24,140,144,
+102,0,0,0,48,24,146,48,243,0,0,0,48,160,144,224,242,0,0,42,32,5,61,0,48,
+160,144,20,243,0,0,0,48,24,140,80,107,0,0,0,48,24,146,52,243,0,0,0,48,160,
+146,228,242,0,0,20,0,0,8,0,48,24,140,192,105,0,0,0,48,24,146,52,243,0,0,
+60,32,180,144,0,48,176,146,124,242,0,0,76,32,164,144,0,48,160,146,172,242,
+0,0,92,32,164,144,0,48,160,146,220,242,0,0,0,48,160,144,128,242,0,0,108,
+32,172,144,148,48,0,90,0,48,168,146,12,243,0,0,10,0,0,18,58,190,5,59,0,
+48,160,144,124,242,0,0,31,216,31,89,42,222,160,49,0,48,160,128,124,242,
+0,0,255,0,24,140,148,208,160,88,148,192,160,88,18,62,5,61,0,48,160,144,
+176,242,0,0,18,32,5,58,0,48,160,144,172,242,0,0,250,61,5,59,0,48,160,144,
+172,242,0,0,31,216,31,89,234,221,160,49,0,48,160,128,172,242,0,0,255,0,
+24,140,148,208,160,88,148,192,160,88,210,61,5,61,0,48,160,144,224,242,0,
+0,18,32,5,58,0,48,160,144,220,242,0,0,186,61,5,59,0,48,160,144,220,242,
+0,0,31,216,31,89,170,221,160,49,0,48,160,128,220,242,0,0,255,0,24,140,148,
+208,160,88,148,192,160,88,146,61,5,61,0,48,160,144,16,243,0,0,18,32,5,58,
+0,48,160,144,12,243,0,0,122,61,5,59,0,48,160,144,12,243,0,0,31,216,31,89,
+106,221,160,49,0,48,160,128,12,243,0,0,255,0,24,140,148,208,160,88,148,
+192,160,88,82,61,5,61,0,48,168,144,128,242,0,0,18,96,5,58,0,48,160,144,
+124,242,0,0,58,29,173,60,0,48,168,144,176,242,0,0,18,96,5,58,0,48,160,144,
+172,242,0,0,34,29,173,60,0,48,168,144,224,242,0,0,18,96,5,58,0,48,160,144,
+220,242,0,0,10,29,173,60,0,48,168,144,16,243,0,0,18,96,5,58,0,48,160,144,
+12,243,0,0,242,28,173,60,0,48,176,144,176,242,0,0,0,48,32,176,160,242,0,
+0,0,48,96,144,128,242,0,0,0,48,128,176,112,242,0,0,0,48,232,144,16,243,
+0,0,0,48,192,176,0,243,0,0,0,48,184,144,224,242,0,0,0,48,64,176,208,242,
+0,0,19,1,163,89,1,13,171,89,0,16,141,140,21,22,144,92,0,48,128,146,112,
+242,0,0,0,48,168,146,120,242,0,0,0,48,160,146,116,242,0,0,7,129,165,89,
+0,48,152,146,124,242,0,0,1,141,181,89,0,16,45,140,22,22,48,92,0,48,32,146,
+160,242,0,0,0,48,160,146,164,242,0,0,11,193,165,89,0,48,176,146,168,242,
+0,0,1,205,189,89,0,16,77,140,23,22,80,92,0,48,64,146,208,242,0,0,0,48,184,
+146,216,242,0,0,0,48,160,146,212,242,0,0,27,65,167,89,0,48,88,146,220,242,
+0,0,1,77,239,89,0,16,205,140,29,22,208,92,0,48,192,146,0,243,0,0,0,48,56,
+146,172,242,0,0,0,30,32,92,0,48,160,146,4,243,0,0,0,48,232,146,8,243,0,
+0,0,48,216,146,12,243,0,0,98,32,3,58,112,96,139,144,0,48,112,140,112,242,
+0,0,0,48,120,140,88,242,0,0,0,48,112,154,80,242,0,0,0,48,128,140,112,242,
+0,0,8,37,0,9,1,30,32,92,184,59,68,58,0,48,160,144,140,242,0,0,12,96,163,
+146,0,48,128,144,128,242,0,0,144,57,128,140,0,0,0,0,48,67,0,9,0,48,128,
+146,136,242,0,0,140,59,4,58,0,48,160,144,176,242,0,0,126,32,5,58,0,48,24,
+140,160,242,0,0,132,57,160,140,0,0,0,0,96,224,31,146,0,52,173,140,80,242,
+0,0,0,52,165,140,88,242,0,0,128,96,139,144,100,224,167,146,96,224,31,144,
+4,96,165,146,0,48,128,140,160,242,0,0,0,80,29,146,132,36,0,9,4,80,32,89,
+52,59,68,58,0,48,160,144,188,242,0,0,16,96,163,146,0,48,128,144,176,242,
+0,0,144,57,128,140,0,0,0,0,172,66,0,9,0,48,128,146,184,242,0,0,8,59,4,58,
+0,48,160,144,224,242,0,0,126,32,5,58,0,48,24,140,208,242,0,0,132,57,160,
+140,0,0,0,0,80,224,31,146,0,52,173,140,80,242,0,0,0,52,165,140,88,242,0,
+0,144,96,139,144,84,224,167,146,80,224,31,144,4,96,165,146,0,48,128,140,
+208,242,0,0,0,80,29,146,0,36,0,9,4,80,32,89,176,58,68,58,0,48,160,144,236,
+242,0,0,20,96,163,146,0,48,128,144,224,242,0,0,144,57,128,140,0,0,0,0,40,
+66,0,9,0,48,128,146,232,242,0,0,132,58,4,58,0,48,160,144,16,243,0,0,126,
+32,5,58,0,48,24,140,0,243,0,0,132,57,160,140,0,0,0,0,64,224,31,146,0,52,
+173,140,80,242,0,0,0,52,165,140,88,242,0,0,160,96,139,144,68,224,167,146,
+64,224,31,144,4,96,165,146,0,48,128,140,0,243,0,0,0,80,29,146,124,35,0,
+9,4,80,32,89,44,58,68,58,0,48,160,144,28,243,0,0,24,96,163,146,0,48,128,
+144,16,243,0,0,144,57,128,140,0,0,0,0,164,65,0,9,0,48,128,146,24,243,0,
+0,0,58,4,58,0,48,24,140,80,242,0,0,2,30,128,92,132,57,24,146,76,242,0,0,
+0,116,192,176,240,255,255,255,0,0,0,10,0,0,0,0,0,48,128,146,76,243,0,0,
+16,22,32,92,144,64,132,112,0,80,44,140,92,65,0,9,0,48,128,146,64,243,0,
+0,18,32,4,61,8,30,128,92,0,0,0,10,0,0,0,0,4,57,128,140,0,0,0,0,56,65,0,
+9,0,48,128,146,68,243,0,0,14,32,4,61,8,30,128,92,0,0,0,10,0,48,160,144,
+64,243,0,0,132,48,0,90,0,48,168,144,68,243,0,0,0,30,176,92,82,0,0,22,0,
+47,1,90,0,80,189,140,0,32,165,140,28,0,0,16,1,30,176,92,0,80,165,146,150,
+32,1,90,20,64,161,89,4,96,189,140,42,0,0,19,23,22,168,92,0,80,165,146,20,
+64,161,89,2,160,181,140,4,96,165,146,150,32,1,90,20,64,161,89,8,96,173,
+140,228,255,255,20,4,57,128,140,0,0,0,0,176,64,0,9,0,48,128,146,72,243,
+0,0,14,32,4,61,8,30,128,92,0,0,0,10,132,48,0,90,0,48,160,144,72,243,0,0,
+0,30,176,92,66,0,0,22,0,47,1,90,0,16,173,140,24,0,0,16,1,30,176,92,150,
+32,1,90,0,16,245,146,20,16,169,89,34,0,0,19,21,22,160,92,0,16,245,146,22,
+144,176,89,4,32,245,146,150,32,1,90,8,32,165,140,236,255,255,20,2,30,128,
+92,0,0,0,10,0,48,184,144,24,82,0,0,0,48,160,144,68,243,0,0,23,29,173,140,
+0,80,181,144,16,22,32,93,62,160,5,58,0,48,160,144,76,243,0,0,0,144,141,
+146,0,80,245,146,22,16,170,89,16,32,172,146,23,80,168,89,12,32,180,146,
+1,9,165,89,148,64,165,88,1,0,128,140,0,48,160,146,24,82,0,0,0,0,0,10,0,
+48,160,144,20,82,0,0,98,32,5,59,188,1,0,9,0,48,168,144,24,82,0,0,0,48,160,
+144,68,243,0,0,21,29,189,140,0,208,181,144,66,160,5,58,0,48,160,144,76,
+243,0,0,0,208,245,146,21,80,168,89,1,9,165,89,148,64,165,88,0,48,160,146,
+24,82,0,0,0,144,45,146,22,16,162,89,16,32,161,146,1,30,128,92,12,32,177,
+146,0,0,0,10,0,0,0,0,0,48,160,144,48,244,0,0,20,80,160,89,0,0,128,140,0,
+48,160,146,48,244,0,0,0,0,0,10,0,0,0,0,0,48,136,144,112,180,3,0,16,22,32,
+92,116,57,0,9,0,48,160,144,96,180,3,0,0,48,184,144,28,82,0,0,20,58,160,
+140,16,0,0,0,0,16,161,146,0,48,168,144,68,243,0,0,0,48,160,144,76,243,0,
+0,23,80,176,89,23,93,37,146,1,9,165,89,148,128,165,88,0,48,160,146,28,82,
+0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,48,240,140,216,119,0,0,30,22,136,92,0,0,
+240,140,0,48,184,144,32,82,0,0,0,48,128,144,72,243,0,0,0,48,160,144,76,
+243,0,0,0,48,168,144,20,82,0,0,23,80,176,89,1,9,165,89,148,128,165,88,23,
+29,132,144,1,73,173,89,0,48,160,146,32,82,0,0,0,48,168,146,20,82,0,0,0,
+80,4,132,0,0,0,10,0,0,0,0,0,48,240,140,80,120,0,0,30,22,144,92,0,0,240,
+140,0,48,160,144,48,244,0,0,0,48,184,144,36,82,0,0,0,48,136,144,72,243,
+0,0,0,48,168,144,76,243,0,0,0,48,176,144,20,82,0,0,20,80,160,89,0,48,160,
+146,48,244,0,0,23,80,160,89,1,73,173,89,149,0,173,88,23,93,132,146,22,80,
+176,89,0,48,168,146,36,82,0,0,0,48,176,146,20,82,0,0,0,144,4,132,0,0,0,
+0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,255,0,48,140,0,48,56,140,255,255,0,0,
+0,48,160,144,140,180,3,0,0,16,165,144,74,33,5,58,0,48,168,144,32,82,0,0,
+0,48,184,144,72,243,0,0,0,48,160,144,76,243,0,0,0,48,176,144,20,82,0,0,
+21,221,37,144,1,137,181,89,0,48,176,146,20,82,0,0,21,80,168,89,1,9,165,
+89,148,64,165,88,0,48,160,146,32,82,0,0,4,16,161,89,0,16,181,144,0,48,168,
+144,140,180,3,0,150,192,129,88,0,16,133,146,0,80,141,144,4,22,144,92,144,
+141,45,89,144,56,128,140,9,0,0,0,3,12,132,89,192,4,0,9,0,48,168,144,140,
+180,3,0,0,48,160,144,144,180,3,0,4,96,181,144,0,80,245,146,21,16,170,89,
+14,0,173,54,0,48,168,144,148,180,3,0,0,48,168,146,140,180,3,0,0,48,168,
+144,40,82,0,0,0,48,136,144,112,180,3,0,0,48,176,146,56,0,0,32,129,73,161,
+88,0,48,160,146,96,0,0,48,21,80,168,89,0,48,168,146,40,82,0,0,149,128,169,
+88,136,73,173,88,0,16,129,140,0,48,168,146,4,0,0,32,56,55,0,9,0,48,160,
+144,96,180,3,0,0,48,128,144,68,243,0,0,20,58,160,140,16,0,0,0,0,16,161,
+146,0,48,176,144,28,82,0,0,0,48,160,144,76,243,0,0,0,48,184,144,20,82,0,
+0,22,80,168,89,1,9,165,89,148,64,165,88,22,29,36,146,0,48,160,146,28,82,
+0,0,180,254,5,60,0,0,0,10,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,16,36,176,
+18,22,232,92,0,208,68,140,30,32,33,58,18,32,33,60,214,32,1,58,4,1,0,8,0,
+0,0,0,106,32,41,58,248,0,0,8,50,224,20,61,0,48,160,144,196,243,0,0,0,48,
+168,144,216,243,0,0,17,0,141,89,1,96,173,140,0,48,136,146,196,243,0,0,0,
+48,168,146,216,243,0,0,200,0,0,8,0,48,160,144,208,243,0,0,0,48,168,144,
+224,243,0,0,17,0,141,89,1,96,173,140,0,48,136,146,208,243,0,0,0,48,168,
+146,224,243,0,0,156,0,0,8,0,0,0,0,50,224,20,61,0,48,160,144,244,243,0,0,
+0,48,168,144,4,244,0,0,17,0,141,89,1,96,173,140,0,48,136,146,244,243,0,
+0,0,48,168,146,4,244,0,0,104,0,0,8,0,48,160,144,248,243,0,0,0,48,168,144,
+16,244,0,0,17,0,141,89,1,96,173,140,0,48,136,146,248,243,0,0,0,48,168,146,
+16,244,0,0,60,0,0,8,0,0,0,0,34,224,20,61,0,48,160,144,180,243,0,0,17,0,
+141,89,0,48,136,146,180,243,0,0,28,0,0,8,0,0,0,0,0,48,160,144,184,243,0,
+0,17,0,141,89,0,48,136,146,184,243,0,0,16,32,164,144,7,1,165,89,8,9,165,
+89,3,12,165,89,4,224,161,146,40,32,180,152,7,16,162,89,18,128,149,62,7,
+16,164,89,22,129,236,89,12,224,177,146,26,192,237,60,23,65,239,89,4,32,
+189,146,157,224,5,90,8,32,165,140,240,255,255,19,10,96,7,59,4,32,237,146,
+0,48,160,144,20,82,0,0,10,32,5,59,28,253,255,9,0,48,136,144,140,180,3,0,
+0,80,164,144,118,32,5,61,4,224,169,144,0,48,184,144,36,82,0,0,0,48,176,
+144,72,243,0,0,16,14,162,89,148,67,165,88,4,224,161,146,23,157,61,146,0,
+48,168,144,48,244,0,0,0,48,160,144,76,243,0,0,0,48,176,144,20,82,0,0,23,
+80,184,89,1,96,173,140,1,9,165,89,148,192,165,88,0,48,168,146,48,244,0,
+0,22,80,176,89,0,48,160,146,36,82,0,0,0,48,176,146,20,82,0,0,0,0,0,10,0,
+0,0,0,4,224,129,144,0,80,140,144,7,22,144,92,144,56,128,140,9,0,0,0,131,
+13,132,89,208,1,0,9,0,48,168,144,140,180,3,0,0,48,160,144,144,180,3,0,4,
+96,181,144,0,80,245,146,21,16,170,89,14,0,173,54,0,48,168,144,148,180,3,
+0,0,48,160,144,40,82,0,0,0,48,136,144,112,180,3,0,0,48,176,146,56,0,0,32,
+0,48,168,146,140,180,3,0,129,9,170,88,0,48,168,146,96,0,0,48,20,80,160,
+89,255,0,72,140,0,48,160,146,40,82,0,0,148,64,162,88,136,9,165,88,0,208,
+129,140,0,48,160,146,4,0,0,32,68,52,0,9,0,48,160,144,96,180,3,0,0,48,184,
+144,28,82,0,0,20,58,160,140,16,0,0,0,0,208,161,146,0,48,168,144,68,243,
+0,0,0,48,160,144,76,243,0,0,23,80,176,89,23,93,61,146,1,9,165,89,148,128,
+165,88,0,48,160,146,28,82,0,0,0,0,0,10,144,68,148,101,0,0,0,10,5,56,0,102,
+0,0,0,10,33,22,128,92,0,0,0,10,16,54,8,92,16,22,128,92,16,22,128,92,16,
+22,128,92,16,22,128,92,16,22,128,92,16,22,128,92,16,22,128,92,16,22,128,
+92,0,0,0,10,16,54,0,92,0,0,0,10,32,22,128,92,0,0,0,10,0,48,32,140,0,0,31,
+0,16,14,44,89,132,2,41,101,5,22,128,92,0,0,0,10,144,18,128,101,0,0,0,10,
+0,48,32,140,255,255,255,255,16,64,36,97,4,22,128,92,0,0,0,10,16,64,148,
+97,18,22,128,92,0,0,0,10,16,65,36,97,4,22,128,92,0,0,0,10,0,0,0,0,0,0,0,
+0,0,0,0,0,0,48,136,146,48,0,0,32,144,57,160,140,0,0,0,0,0,30,136,92,144,
+48,0,90,0,48,160,146,96,0,0,32,0,48,152,140,0,0,0,48,54,0,0,22,0,48,160,
+144,8,0,0,16,248,63,61,51,0,208,164,176,0,144,164,178,0,208,164,176,17,
+80,136,89,16,160,148,140,145,32,4,90,0,144,164,178,18,16,148,89,212,255,
+255,20,0,0,0,10,18,22,184,92,144,57,160,140,0,0,0,0,0,48,136,146,84,0,0,
+32,144,48,0,90,0,48,160,146,96,0,0,32,0,30,136,92,146,0,0,22,56,32,4,48,
+0,144,36,176,0,48,160,140,96,0,0,48,1,30,136,92,0,16,37,178,18,16,188,89,
+0,48,168,140,96,0,0,48,0,208,37,176,23,16,188,89,145,32,4,90,0,80,37,178,
+90,0,0,19,0,48,176,140,96,0,0,48,0,48,168,140,96,0,0,48,0,48,152,140,96,
+0,0,48,0,208,37,176,0,208,36,178,23,16,164,89,0,16,37,176,0,208,36,178,
+32,224,165,140,0,16,37,176,0,144,37,178,48,224,165,140,0,16,37,176,17,144,
+136,89,64,224,189,140,145,32,4,90,0,80,37,178,200,255,255,20,0,0,0,10,0,
+0,0,0,0,0,0,0,0,48,240,140,144,127,0,0,30,22,184,92,0,0,240,140,16,22,176,
+92,0,48,136,146,84,0,0,32,144,176,0,90,0,48,128,146,96,0,0,32,18,22,168,
+92,42,0,0,18,18,32,20,60,50,32,12,58,64,0,0,8,0,0,0,0,56,32,28,61,0,144,
+164,144,18,16,169,89,0,48,160,146,96,0,0,32,0,80,165,144,21,16,169,89,0,
+48,160,146,96,0,0,32,0,80,165,144,0,48,160,146,96,0,0,32,0,208,5,132,0,
+0,0,0,146,208,163,88,50,32,69,58,18,32,69,52,22,32,37,58,76,0,0,8,0,0,0,
+0,50,32,101,58,64,0,0,8,0,144,164,144,18,16,169,89,1,9,180,89,0,48,160,
+146,96,0,0,32,0,80,165,144,21,16,169,89,1,137,181,89,0,48,160,146,96,0,
+0,32,0,80,165,144,21,16,169,89,1,137,181,89,0,48,160,146,96,0,0,32,150,
+240,0,90,0,48,160,140,96,0,0,32,30,0,0,22,0,80,133,176,4,137,181,89,16,
+96,173,140,150,240,0,90,0,16,133,178,236,255,255,17,34,160,5,59,0,80,165,
+144,1,137,181,89,4,96,173,140,150,48,0,90,0,48,160,146,96,0,0,32,232,255,
+255,17,0,208,5,132,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,255,0,176,140,144,128,
+133,88,0,0,168,140,0,48,128,146,4,0,0,32,0,48,160,144,4,0,0,16,255,0,176,
+140,148,128,165,88,188,0,164,58,0,48,160,144,4,0,0,16,148,128,165,88,172,
+0,164,58,0,48,160,144,4,0,0,16,148,128,165,88,156,0,164,58,0,48,160,144,
+4,0,0,16,148,128,165,88,140,0,164,58,0,48,160,144,4,0,0,16,148,128,165,
+88,124,0,164,58,0,48,160,144,4,0,0,16,148,128,165,88,108,0,164,58,0,48,
+160,144,4,0,0,16,148,128,165,88,92,0,164,58,0,48,160,144,4,0,0,16,148,128,
+165,88,76,0,164,58,0,48,160,144,4,0,0,16,148,128,165,88,60,0,164,58,0,48,
+160,144,4,0,0,16,148,128,165,88,44,0,164,58,21,144,170,89,0,48,176,140,
+255,179,196,4,82,159,173,62,0,48,160,144,64,244,0,0,0,48,176,140,6,0,173,
+222,0,16,181,146,148,62,0,8,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,30,176,92,
+0,48,168,128,40,82,0,0,0,48,160,144,4,0,0,16,255,0,184,140,148,192,165,
+88,188,64,165,58,0,48,160,144,4,0,0,16,148,192,165,88,172,64,165,58,0,48,
+160,144,4,0,0,16,148,192,165,88,156,64,165,58,0,48,160,144,4,0,0,16,148,
+192,165,88,140,64,165,58,0,48,160,144,4,0,0,16,148,192,165,88,124,64,165,
+58,0,48,160,144,4,0,0,16,148,192,165,88,108,64,165,58,0,48,160,144,4,0,
+0,16,148,192,165,88,92,64,165,58,0,48,160,144,4,0,0,16,148,192,165,88,76,
+64,165,58,0,48,160,144,4,0,0,16,148,192,165,88,60,64,165,58,0,48,160,144,
+4,0,0,16,148,192,165,88,44,64,165,58,22,144,178,89,0,48,184,140,255,179,
+196,4,82,223,181,62,0,48,160,144,64,244,0,0,0,48,184,140,7,0,173,222,0,
+16,189,146,172,61,0,8,0,0,0,10,0,0,0,0,0,48,240,140,176,129,0,0,30,22,128,
+92,0,0,240,140,0,48,168,144,40,82,0,0,255,0,176,140,21,80,168,89,149,128,
+165,88,136,9,165,88,0,48,168,146,40,82,0,0,0,48,160,146,4,0,0,32,0,16,4,
+132,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,16,22,40,92,1,9,52,89,0,0,
+32,140,0,48,56,140,16,39,0,0,28,251,255,9,5,1,132,88,12,251,255,9,4,22,
+160,92,148,224,1,90,1,32,33,140,10,0,0,22,32,61,0,9,252,250,255,9,144,64,
+129,88,216,63,4,61,1,30,128,92,0,0,0,10,0,0,0,0,0,0,0,0,64,96,8,140,0,116,
+128,178,192,255,255,255,0,116,160,178,208,255,255,255,0,116,192,178,224,
+255,255,255,0,116,224,178,240,255,255,255,0,48,160,144,52,82,0,0,34,32,
+5,61,0,48,160,144,64,244,0,0,0,48,176,140,1,0,173,222,0,16,181,146,188,
+60,0,9,232,60,0,9,0,48,160,144,0,0,0,16,210,0,176,140,148,128,165,88,40,
+0,176,140,0,48,40,140,16,39,0,0,148,131,165,88,0,0,32,140,0,48,160,146,
+0,0,0,16,104,250,255,9,0,14,132,88,88,250,255,9,4,22,160,92,148,96,1,90,
+1,32,33,140,10,0,0,22,108,60,0,9,72,250,255,9,220,63,4,55,0,48,160,144,
+60,82,0,0,0,48,168,144,64,82,0,0,20,80,160,89,0,48,160,146,60,82,0,0,54,
+96,5,58,0,48,168,144,40,82,0,0,255,0,176,140,21,80,168,89,149,128,165,88,
+136,9,165,88,0,48,168,146,40,82,0,0,0,48,160,146,4,0,0,32,16,0,0,8,0,0,
+0,0,0,48,240,146,52,82,0,0,0,116,128,176,192,255,255,255,0,116,160,176,
+208,255,255,255,0,116,192,176,224,255,255,255,0,116,224,176,240,255,255,
+255,3,54,8,92,0,0,0,10,64,96,8,140,0,116,128,178,192,255,255,255,0,116,
+160,178,208,255,255,255,0,116,192,178,224,255,255,255,0,116,224,178,240,
+255,255,255,0,48,160,128,68,20,0,80,22,32,37,48,0,48,160,144,68,20,0,80,
+10,32,133,48,180,3,0,9,0,48,160,128,68,20,0,80,22,32,45,48,0,48,160,144,
+68,20,0,80,10,32,141,48,184,3,0,9,0,48,160,128,68,20,0,80,22,32,53,48,0,
+48,160,144,68,20,0,80,10,32,149,48,188,3,0,9,0,48,160,128,68,12,0,80,14,
+32,21,48,10,32,29,48,200,3,0,9,0,30,32,92,0,48,40,140,16,39,0,0,40,249,
+255,9,4,14,132,88,24,249,255,9,4,22,160,92,148,96,1,90,1,32,33,140,10,0,
+0,22,44,59,0,9,8,249,255,9,220,63,36,55,0,116,128,176,192,255,255,255,0,
+116,160,176,208,255,255,255,0,116,192,176,224,255,255,255,0,116,224,176,
+240,255,255,255,3,54,8,92,0,0,0,10,0,0,0,0,0,0,0,0,64,96,8,140,0,116,128,
+178,192,255,255,255,0,116,160,178,208,255,255,255,0,116,192,178,224,255,
+255,255,0,116,224,178,240,255,255,255,0,48,160,144,76,82,0,0,34,32,5,61,
+0,48,160,144,64,244,0,0,0,48,168,140,2,0,173,222,0,16,173,146,172,58,0,
+9,216,58,0,9,68,4,0,9,0,30,32,92,0,48,40,140,16,39,0,0,116,248,255,9,5,
+14,132,88,100,248,255,9,4,22,160,92,148,96,1,90,1,32,33,140,10,0,0,22,120,
+58,0,9,84,248,255,9,220,63,44,55,0,116,128,176,192,255,255,255,0,116,160,
+176,208,255,255,255,0,116,192,176,224,255,255,255,0,116,224,176,240,255,
+255,255,3,54,8,92,0,0,0,10,0,0,0,0,64,96,8,140,0,116,128,178,192,255,255,
+255,0,116,160,178,208,255,255,255,0,116,192,178,224,255,255,255,0,116,224,
+178,240,255,255,255,0,48,160,144,48,82,0,0,34,32,5,61,0,48,160,144,64,244,
+0,0,0,48,168,140,3,0,173,222,0,16,173,146,252,57,0,9,40,58,0,9,0,48,160,
+144,0,0,0,16,210,0,168,140,0,48,40,140,16,39,0,0,148,64,165,88,128,9,165,
+88,0,0,32,140,0,48,160,146,0,0,0,16,172,247,255,9,2,14,132,88,156,247,255,
+9,4,22,160,92,148,96,1,90,1,32,33,140,10,0,0,22,176,57,0,9,140,247,255,
+9,220,63,20,55,0,116,128,176,192,255,255,255,0,116,160,176,208,255,255,
+255,0,116,192,176,224,255,255,255,0,116,224,176,240,255,255,255,3,54,8,
+92,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,64,96,8,140,0,116,128,178,192,255,255,
+255,0,116,160,178,208,255,255,255,0,116,192,178,224,255,255,255,0,116,224,
+178,240,255,255,255,0,48,160,144,44,82,0,0,34,32,5,61,0,48,160,144,64,244,
+0,0,0,48,168,140,4,0,173,222,0,16,173,146,44,57,0,9,88,57,0,9,0,116,128,
+176,192,255,255,255,0,116,160,176,208,255,255,255,0,116,192,176,224,255,
+255,255,0,116,224,176,240,255,255,255,3,54,8,92,0,0,0,10,0,0,0,0,0,0,0,
+0,0,0,0,0,144,246,255,9,4,32,44,144,16,32,36,144,0,30,128,92,144,246,255,
+9,0,30,128,92,0,48,64,140,1,50,0,0,168,246,255,9,4,30,72,92,0,48,88,140,
+32,88,0,0,28,96,89,146,16,96,65,154,0,48,160,140,16,130,0,0,1,4,128,140,
+76,32,161,146,0,48,160,140,208,132,0,0,0,30,136,92,140,32,161,146,0,48,
+160,140,160,133,0,0,0,30,144,92,204,32,161,146,0,48,160,140,48,131,0,0,
+12,33,161,146,20,246,255,9,0,30,128,92,76,246,255,9,255,15,128,140,28,246,
+255,9,1,30,128,92,76,246,255,8,0,246,255,9,4,32,36,144,16,32,44,144,0,30,
+128,92,0,246,255,9,0,30,128,92,32,246,255,9,84,0,160,140,1,4,128,140,20,
+32,161,146,0,48,160,140,0,88,0,0,0,30,136,92,28,32,161,146,0,48,160,140,
+48,131,0,0,0,30,144,92,12,97,161,146,0,48,160,140,32,132,0,0,76,97,161,
+146,164,245,255,9,0,30,128,92,220,245,255,9,255,15,128,140,172,245,255,
+9,1,30,128,92,220,245,255,8,0,48,160,144,64,244,0,0,0,48,168,140,16,0,173,
+222,0,16,173,146,220,55,0,9,1,30,128,92,4,56,0,8,0,48,160,144,64,244,0,
+0,0,48,168,140,17,0,173,222,0,16,173,146,188,55,0,9,1,30,128,92,228,55,
+0,8,0,48,160,144,64,244,0,0,0,48,168,140,18,0,173,222,0,16,173,146,156,
+55,0,9,1,30,128,92,196,55,0,8,0,48,160,144,64,244,0,0,0,48,168,140,19,0,
+173,222,0,16,173,146,124,55,0,9,1,30,128,92,164,55,0,8,0,48,240,140,200,
+135,0,0,30,22,136,92,0,0,240,140,255,0,160,140,144,0,133,88,0,48,128,146,
+8,28,0,80,0,80,4,132,0,0,0,0,0,0,0,10,0,0,0,0,136,25,176,88,144,160,5,90,
+0,0,160,140,0,30,168,92,58,0,0,18,14,128,133,49,26,32,4,58,84,0,0,8,137,
+25,176,88,62,128,133,58,72,0,0,8,0,0,0,0,0,48,160,140,130,184,97,202,0,
+48,168,140,118,116,210,62,48,0,0,8,0,0,0,0,0,48,160,140,222,153,139,252,
+0,48,168,140,59,93,202,62,24,0,0,8,0,0,0,0,0,48,160,140,216,182,122,157,
+0,48,168,140,98,194,199,62,20,22,128,93,0,0,144,140,0,48,152,140,132,215,
+119,65,80,103,0,9,220,88,0,9,255,0,176,140,144,128,133,88,0,48,128,146,
+8,28,0,80,0,0,0,10,0,0,0,0,0,48,240,140,160,136,0,0,30,22,128,92,0,0,240,
+140,0,48,160,144,4,28,0,80,0,48,240,146,76,82,0,0,1,14,165,88,0,48,160,
+146,4,28,0,80,0,16,4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,48,160,144,
+76,82,0,0,10,32,5,61,132,54,0,9,0,48,168,144,4,28,0,80,137,73,165,88,0,
+48,160,146,4,28,0,80,0,48,160,136,4,28,0,80,22,32,85,48,9,222,176,89,149,
+131,165,88,0,48,160,146,4,28,0,80,0,48,160,144,68,82,0,0,20,80,160,89,0,
+48,160,146,68,82,0,0,0,0,0,10,0,0,0,0,0,0,0,0,30,22,32,92,0,48,128,144,
+84,82,0,0,6,222,136,89,0,54,0,9,0,48,168,144,0,0,0,16,210,0,184,140,149,
+192,165,88,128,9,173,88,0,48,168,146,0,0,0,16,0,48,160,144,84,82,0,0,20,
+16,175,89,0,48,168,146,68,244,0,0,0,48,160,144,84,82,0,0,32,32,173,140,
+0,48,168,146,72,244,0,0,0,48,160,144,84,82,0,0,48,32,173,140,0,48,168,146,
+64,244,0,0,0,48,160,144,84,82,0,0,44,32,173,140,0,48,168,146,76,244,0,0,
+2,30,160,92,0,48,160,146,80,244,0,0,0,48,160,144,84,82,0,0,0,48,168,140,
+0,184,2,0,40,32,173,146,0,48,160,144,84,82,0,0,0,48,168,140,96,244,0,0,
+36,32,173,146,0,48,160,144,84,82,0,0,0,48,168,140,1,0,3,0,52,32,173,146,
+0,48,160,144,84,82,0,0,0,48,168,144,88,82,0,0,16,96,181,144,16,142,173,
+89,56,32,173,146,0,0,0,10,0,0,0,0,0,0,0,0,16,72,8,89,30,22,32,92,0,48,160,
+144,88,82,0,0,0,48,168,140,237,254,17,206,8,32,173,146,0,48,160,144,64,
+244,0,0,0,16,173,144,1,73,181,89,0,16,181,146,0,48,160,144,84,82,0,0,64,
+32,173,144,127,0,184,140,149,192,165,88,148,48,0,90,186,0,0,18,148,112,
+0,90,10,0,0,18,180,0,0,8,0,48,128,144,84,82,0,0,0,10,0,9,64,224,135,146,
+0,48,160,144,84,82,0,0,64,224,175,144,129,73,181,88,68,32,181,146,0,48,
+160,144,84,82,0,0,64,32,173,144,21,22,160,92,0,48,184,140,128,255,255,255,
+148,192,173,88,255,0,184,140,149,192,165,88,148,48,0,90,50,0,0,18,0,48,
+160,144,40,82,0,0,20,80,168,89,0,48,168,146,40,82,0,0,0,48,168,144,40,82,
+0,0,149,192,165,88,136,9,173,88,0,48,168,146,4,0,0,32,0,48,160,144,84,82,
+0,0,0,30,168,92,64,32,173,146,64,224,167,144,148,176,0,90,14,0,0,21,0,0,
+0,10,0,0,0,0,16,0,0,8,0,0,0,0,8,0,0,8,0,0,0,0,20,255,255,8,0,0,0,0,0,0,
+0,10,0,0,0,0,0,0,0,0,0,0,0,0,16,72,8,89,30,22,160,92,0,48,168,144,68,12,
+0,80,128,73,181,88,0,48,176,146,68,12,0,80,0,48,168,144,68,12,0,80,0,78,
+181,88,0,48,176,146,68,12,0,80,0,48,168,144,68,12,0,80,2,78,181,88,0,48,
+176,146,68,12,0,80,0,30,168,92,0,48,168,146,72,16,0,80,1,30,168,92,0,48,
+168,146,68,16,0,80,0,30,168,92,64,224,175,146,64,224,175,144,21,240,0,90,
+78,0,0,17,64,224,175,144,21,57,176,140,0,0,0,0,0,160,173,140,15,30,176,
+92,0,116,181,146,16,16,0,80,64,224,175,144,21,57,176,140,0,0,0,0,0,160,
+173,140,0,30,176,92,0,116,181,146,0,16,0,80,64,224,175,144,21,80,176,89,
+64,224,183,146,176,255,255,8,0,48,168,144,4,28,0,80,2,78,181,88,0,48,176,
+146,4,28,0,80,0,48,168,144,68,20,0,80,1,78,181,88,0,48,176,146,68,20,0,
+80,244,1,168,140,0,48,168,146,48,20,0,80,0,48,168,144,68,20,0,80,135,73,
+181,88,0,48,176,146,68,20,0,80,0,48,168,144,68,20,0,80,134,73,181,88,0,
+48,176,146,68,20,0,80,0,48,168,144,68,20,0,80,4,78,181,88,0,48,176,146,
+68,20,0,80,0,48,168,144,68,20,0,80,5,78,181,88,0,48,176,146,68,20,0,80,
+0,0,0,10,16,72,8,89,30,22,32,92,0,0,160,140,0,48,160,146,0,0,0,16,0,48,
+168,144,0,0,0,16,210,0,184,140,149,192,165,88,129,9,173,88,0,48,168,146,
+0,0,0,16,0,48,160,144,0,0,0,16,208,0,184,140,148,192,173,88,0,48,168,146,
+0,0,0,16,0,48,168,144,0,0,0,16,210,0,184,140,149,192,165,88,128,9,173,88,
+0,48,168,146,0,0,0,16,0,48,168,144,0,0,0,16,149,192,165,88,40,0,184,140,
+148,195,173,88,0,48,168,146,0,0,0,16,10,30,128,92,4,50,0,11,0,48,160,144,
+4,28,0,80,131,9,173,88,0,48,168,146,4,28,0,80,31,216,132,89,232,49,0,11,
+0,48,168,144,4,28,0,80,11,222,184,89,149,195,165,88,0,48,184,140,56,24,
+0,0,148,224,5,90,14,0,0,18,1,30,128,92,24,50,0,9,0,48,160,144,68,12,0,80,
+148,48,0,90,14,0,0,18,1,30,128,92,0,50,0,9,0,48,168,144,68,20,0,80,8,222,
+184,89,149,195,165,88,148,224,5,90,14,0,0,18,1,30,128,92,224,49,0,9,0,48,
+160,144,68,12,0,80,129,9,173,88,0,48,168,146,68,12,0,80,0,48,160,144,68,
+12,0,80,128,9,173,88,0,48,168,146,68,12,0,80,0,48,160,144,68,20,0,80,130,
+9,173,88,0,48,168,146,68,20,0,80,0,48,160,144,68,20,0,80,128,9,173,88,0,
+48,168,146,68,20,0,80,0,48,160,144,68,20,0,80,10,14,173,88,0,48,168,146,
+68,20,0,80,0,48,160,144,4,28,0,80,133,9,173,88,0,48,168,146,4,28,0,80,0,
+48,160,144,4,28,0,80,132,9,173,88,0,48,168,146,4,28,0,80,0,48,160,144,4,
+28,0,80,3,14,173,88,0,48,168,146,4,28,0,80,10,30,128,92,224,48,0,11,0,30,
+160,92,64,224,167,146,64,224,167,144,20,240,0,90,110,0,0,17,64,224,167,
+144,20,57,168,140,0,0,0,0,0,96,165,140,64,224,175,144,10,78,181,89,0,48,
+168,144,80,82,0,0,21,128,181,89,0,52,181,146,0,12,0,144,64,224,167,144,
+20,57,168,140,0,0,0,0,0,96,165,140,64,224,175,144,10,78,181,89,0,48,168,
+144,80,82,0,0,21,128,181,89,0,52,181,146,16,12,0,144,64,224,167,144,20,
+80,168,89,64,224,175,146,144,255,255,8,0,48,160,144,80,244,0,0,148,112,
+0,90,110,0,0,21,0,48,160,144,80,82,0,0,0,52,173,140,0,32,0,0,0,48,168,146,
+0,20,0,144,0,48,160,144,80,82,0,0,0,52,173,140,0,32,0,0,0,48,168,146,16,
+20,0,144,0,48,160,144,80,82,0,0,0,52,173,140,0,16,0,0,0,48,168,146,0,24,
+0,144,0,48,160,144,80,82,0,0,0,52,173,140,0,16,0,0,0,48,168,146,16,24,0,
+144,24,1,0,8,0,0,0,0,0,30,160,92,64,224,167,146,64,224,167,144,20,240,0,
+90,2,1,0,17,64,224,167,144,20,57,168,140,0,0,0,0,0,96,165,140,64,224,183,
+144,150,56,168,140,0,0,0,0,21,128,173,89,11,78,173,89,0,116,181,140,0,32,
+0,0,0,48,168,144,80,82,0,0,21,128,181,89,0,52,181,146,0,20,0,144,64,224,
+167,144,20,57,168,140,0,0,0,0,0,96,165,140,64,224,183,144,150,56,168,140,
+0,0,0,0,21,128,173,89,11,78,173,89,0,116,181,140,0,32,0,0,0,48,168,144,
+80,82,0,0,21,128,181,89,0,52,181,146,16,20,0,144,64,224,167,144,20,57,168,
+140,0,0,0,0,0,96,165,140,64,224,175,144,10,78,181,89,0,180,173,140,0,16,
+0,0,0,48,176,144,80,82,0,0,22,64,173,89,0,52,173,146,0,24,0,144,64,224,
+167,144,20,57,168,140,0,0,0,0,0,96,165,140,64,224,175,144,10,78,181,89,
+0,180,173,140,0,16,0,0,0,48,176,144,80,82,0,0,22,64,173,89,0,52,173,146,
+16,24,0,144,64,224,167,144,20,80,168,89,64,224,175,146,0,255,255,8,0,0,
+0,0,1,30,160,92,0,48,160,146,68,16,0,80,0,30,160,92,64,224,167,146,64,224,
+167,144,20,240,0,90,78,0,0,17,64,224,167,144,20,57,168,140,0,0,0,0,0,96,
+165,140,15,30,168,92,0,52,173,146,16,16,0,80,64,224,167,144,20,57,168,140,
+0,0,0,0,0,96,165,140,0,30,168,92,0,52,173,146,0,16,0,80,64,224,167,144,
+20,80,168,89,64,224,175,146,176,255,255,8,0,48,160,144,68,12,0,80,1,14,
+173,88,0,48,168,146,68,12,0,80,0,48,160,144,68,12,0,80,0,14,173,88,0,48,
+168,146,68,12,0,80,0,48,160,144,68,20,0,80,2,14,173,88,0,48,168,146,68,
+20,0,80,0,48,160,144,68,20,0,80,0,14,173,88,0,48,168,146,68,20,0,80,0,48,
+160,144,68,20,0,80,138,9,173,88,0,48,168,146,68,20,0,80,0,48,160,144,4,
+28,0,80,5,14,173,88,0,48,168,146,4,28,0,80,0,48,160,144,4,28,0,80,4,14,
+173,88,0,48,168,146,4,28,0,80,0,48,160,144,68,20,0,80,147,9,173,88,0,48,
+168,146,68,20,0,80,0,30,128,92,0,0,0,10,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,
+0,30,22,160,92,0,48,168,128,80,243,0,0,0,48,168,146,68,16,0,80,0,30,168,
+92,0,48,168,146,0,16,0,80,1,30,168,92,0,48,168,146,16,16,0,80,0,0,0,10,
+24,22,40,92,16,72,8,89,0,116,200,146,240,255,255,255,0,116,208,154,248,
+255,255,255,0,16,164,144,0,48,232,140,0,255,0,0,0,16,172,144,148,64,167,
+88,8,78,239,89,4,32,180,144,136,13,141,89,149,64,175,88,144,77,149,89,42,
+96,36,58,38,96,44,58,10,30,128,92,5,22,192,92,0,116,200,144,240,255,255,
+255,0,116,208,152,248,255,255,255,0,0,0,10,0,0,0,0,10,160,4,58,220,191,
+12,61,0,48,160,144,100,180,3,0,210,31,181,52,0,48,160,144,96,180,3,0,198,
+31,181,49,0,48,160,144,224,193,3,0,22,157,173,140,21,30,37,140,4,32,161,
+144,172,63,69,58,138,96,36,58,14,96,36,60,18,96,4,58,156,255,255,8,138,
+96,44,58,148,255,255,8,8,32,164,144,31,88,236,89,157,5,165,112,72,32,161,
+146,14,32,5,61,1,30,232,92,72,32,233,146,4,32,161,144,98,32,69,61,8,32,
+137,144,72,32,161,144,38,0,141,60,145,92,148,140,18,58,144,140,0,0,0,0,
+4,22,128,92,2,0,152,140,116,231,255,9,8,32,241,146,24,32,241,146,2,30,128,
+92,5,22,192,92,0,116,200,144,240,255,255,255,0,116,208,152,248,255,255,
+255,0,0,0,10,0,0,0,0,31,88,187,89,4,0,128,140,16,0,0,8,0,0,0,0,31,88,188,
+89,0,0,128,140,18,160,4,58,134,160,12,58,0,255,255,8,0,0,0,0,0,48,160,144,
+176,242,0,0,240,62,5,58,0,48,160,144,48,243,0,0,48,32,169,140,20,22,48,
+92,0,48,56,140,48,104,0,0,0,80,165,146,4,96,61,146,0,48,160,144,132,242,
+0,0,151,5,165,116,32,32,161,146,0,48,160,144,180,242,0,0,151,5,165,116,
+32,32,177,140,0,144,69,144,20,22,72,92,151,0,170,112,151,0,189,112,4,160,
+165,146,16,65,173,89,0,80,85,140,23,22,88,92,0,144,69,146,120,0,0,8,0,0,
+0,0,0,48,160,144,16,243,0,0,120,62,5,58,0,48,160,144,52,243,0,0,48,32,169,
+140,20,22,192,92,0,48,200,140,80,107,0,0,0,80,165,146,4,96,205,146,0,48,
+160,144,228,242,0,0,151,5,165,116,32,32,161,146,0,48,160,144,20,243,0,0,
+151,5,165,116,32,32,177,140,0,144,101,144,20,22,104,92,151,0,171,112,151,
+0,189,112,4,160,165,146,16,65,173,89,0,80,117,140,23,22,120,92,0,144,101,
+146,8,160,173,146,12,160,189,146,17,22,208,92,20,32,145,146,8,30,216,92,
+0,16,137,146,2,30,128,92,4,32,217,146,5,22,192,92,0,116,200,144,240,255,
+255,255,0,116,208,152,248,255,255,255,0,0,0,10,4,32,180,144,0,48,160,144,
+100,180,3,0,42,0,181,52,0,48,160,144,96,180,3,0,30,0,181,49,0,48,160,144,
+224,193,3,0,22,157,173,140,21,62,165,144,4,0,0,0,18,32,77,61,10,30,128,
+92,0,0,0,10,0,0,0,0,0,48,168,144,224,193,3,0,22,157,165,140,20,94,37,140,
+8,32,137,144,22,96,4,59,4,22,128,92,0,0,144,140,8,30,152,92,156,229,255,
+9,4,22,128,92,80,0,136,140,224,42,0,9,9,30,184,92,4,32,185,146,159,25,184,
+88,2,0,128,140,56,32,185,146,0,0,0,10,0,0,0,0,96,32,164,144,0,48,160,146,
+116,180,3,0,96,32,164,144,148,57,160,140,32,0,0,0,0,48,160,146,120,180,
+3,0,0,48,160,128,120,180,3,0,16,22,96,92,64,32,92,140,255,0,104,140,148,
+208,167,88,148,64,163,88,14,32,5,58,8,30,128,92,0,0,0,10,0,48,128,144,120,
+180,3,0,208,33,0,9,0,48,128,146,52,242,0,0,14,32,4,61,8,30,128,92,0,0,0,
+10,28,224,162,144,148,57,160,140,32,0,0,0,0,48,160,146,112,180,3,0,0,48,
+160,128,112,180,3,0,148,208,167,88,148,64,163,88,14,32,5,58,8,30,128,92,
+0,0,0,10,24,224,162,144,148,208,160,88,148,64,163,88,14,32,5,58,8,30,128,
+92,0,0,0,10,16,224,170,144,20,224,74,144,24,224,82,144,24,224,146,144,24,
+224,154,144,48,224,234,144,56,224,34,144,64,224,42,144,72,224,50,144,80,
+224,58,144,88,224,66,144,0,48,176,144,120,180,3,0,96,224,138,144,0,48,160,
+144,112,180,3,0,104,224,130,144,12,224,186,144,22,16,178,89,150,64,178,
+112,8,32,165,140,148,128,162,112,21,93,173,140,23,221,189,140,149,157,173,
+140,21,0,173,89,18,93,149,140,19,157,156,140,157,221,236,140,132,93,39,
+140,133,29,41,140,134,93,49,140,135,157,57,140,136,221,65,140,145,29,138,
+140,144,93,132,140,0,48,104,140,0,192,2,0,23,30,188,140,14,64,187,62,8,
+30,128,92,0,0,0,10,12,22,128,92,164,0,0,9,18,32,68,61,8,30,128,92,0,0,0,
+10,0,0,0,0,12,22,128,92,28,1,0,9,18,32,68,61,8,30,128,92,0,0,0,10,0,0,0,
+0,12,22,128,92,164,1,0,9,18,32,68,61,8,30,128,92,0,0,0,10,0,0,0,0,12,22,
+128,92,140,216,255,9,18,32,68,61,8,30,128,92,0,0,0,10,0,0,0,0,24,224,130,
+144,0,48,136,144,112,180,3,0,236,222,255,9,18,32,68,61,8,30,128,92,0,0,
+0,10,0,0,0,0,64,32,131,140,196,15,0,9,18,32,68,61,8,30,128,92,0,0,0,10,
+0,0,0,0,0,30,128,92,76,233,255,9,2,30,128,92,0,0,0,10,16,22,32,92,80,32,
+132,144,0,48,128,146,152,180,3,0,5,14,132,89,12,32,0,9,0,48,128,146,56,
+242,0,0,18,32,4,61,8,30,128,92,0,0,0,10,0,0,0,0,0,48,184,144,56,242,0,0,
+0,16,185,146,0,48,168,144,152,180,3,0,0,30,176,92,0,48,184,146,64,242,0,
+0,5,78,165,89,23,60,165,140,224,255,255,255,0,48,160,146,60,242,0,0,34,
+96,5,59,23,22,168,92,0,80,245,146,0,48,160,144,152,180,3,0,32,96,173,140,
+22,80,176,89,236,31,181,60,2,30,128,92,0,0,0,10,0,0,0,0,88,32,36,144,16,
+22,40,92,132,57,128,140,0,0,0,0,128,31,0,9,0,48,128,146,140,180,3,0,14,
+32,4,61,8,30,128,92,0,0,0,10,0,48,176,144,140,180,3,0,0,30,168,92,132,48,
+0,90,8,96,177,146,0,48,176,146,148,180,3,0,132,189,165,140,248,255,255,
+255,0,48,160,146,144,180,3,0,62,0,0,22,0,47,1,90,0,144,165,140,24,0,0,16,
+1,30,168,92,149,32,1,90,0,144,245,146,22,16,162,89,30,0,0,19,0,16,245,146,
+21,144,168,89,8,32,245,146,149,32,1,90,16,32,165,140,236,255,255,20,2,30,
+128,92,0,0,0,10,0,0,0,0,0,0,0,0,84,32,36,144,16,22,40,92,132,57,128,140,
+0,0,0,0,224,30,0,9,0,48,128,146,128,180,3,0,14,32,4,61,8,30,128,92,0,0,
+0,10,0,48,176,144,128,180,3,0,0,30,168,92,132,48,0,90,4,96,177,146,0,48,
+176,146,136,180,3,0,132,189,165,140,248,255,255,255,0,48,160,146,132,180,
+3,0,62,0,0,22,0,47,1,90,0,144,165,140,24,0,0,16,1,30,168,92,149,32,1,90,
+0,144,245,146,22,16,162,89,30,0,0,19,0,16,245,146,21,144,168,89,8,32,245,
+146,149,32,1,90,16,32,165,140,236,255,255,20,2,30,128,92,0,0,0,10,0,0,0,
+0,0,0,0,0,16,22,32,92,145,57,128,140,0,0,0,0,17,22,40,92,64,30,0,9,28,32,
+129,146,18,32,4,61,8,30,128,92,0,0,0,10,0,0,0,0,28,32,185,144,32,32,169,
+140,0,30,176,92,133,48,0,90,133,253,165,140,248,255,255,255,0,80,189,146,
+4,96,165,146,62,0,0,22,0,111,1,90,0,208,165,140,24,0,0,16,1,30,176,92,150,
+96,1,90,0,208,245,146,23,16,162,89,30,0,0,19,0,16,245,146,22,144,176,89,
+8,32,245,146,150,96,1,90,16,32,165,140,236,255,255,20,2,30,128,92,0,0,0,
+10,0,0,0,0,80,96,8,140,0,116,192,178,240,255,255,255,0,30,88,92,0,48,192,
+140,32,20,0,80,0,48,24,140,252,4,0,96,0,48,200,140,132,8,0,64,0,48,208,
+140,255,63,0,0,1,25,216,89,0,16,166,144,222,44,5,58,8,206,114,89,0,180,
+163,140,128,8,0,64,0,16,101,144,0,80,166,144,142,137,238,88,148,64,79,88,
+0,48,232,140,255,255,255,0,4,12,171,89,149,64,167,88,16,12,165,89,148,44,
+5,61,0,48,160,144,96,180,3,0,8,76,239,89,149,64,175,88,128,12,173,49,0,
+48,160,144,224,193,3,0,21,93,173,140,21,30,45,140,4,96,161,144,0,80,169,
+144,46,32,77,61,11,22,128,92,0,80,138,140,64,16,0,11,0,48,160,144,164,243,
+0,0,20,80,160,89,0,48,160,146,164,243,0,0,76,12,0,8,0,0,0,0,140,144,163,
+88,2,12,165,89,10,32,21,61,44,108,5,61,28,96,81,144,24,96,65,144,8,96,49,
+144,78,100,37,58,18,96,37,60,142,105,5,58,16,12,0,8,0,0,0,0,8,108,45,61,
+246,32,2,61,106,160,1,61,5,22,128,92,0,16,139,140,116,220,255,9,172,43,
+4,58,48,96,161,144,5,22,128,92,0,16,5,134,144,112,0,90,0,16,84,140,50,0,
+0,21,0,48,160,144,248,243,0,0,11,22,128,92,9,0,165,89,0,80,138,140,0,48,
+160,146,248,243,0,0,160,15,0,11,12,96,129,144,16,221,255,9,116,11,0,8,32,
+96,161,144,68,96,217,146,140,0,0,8,0,0,0,0,86,5,232,140,62,64,55,54,0,48,
+160,144,252,243,0,0,11,22,128,92,1,32,165,140,9,22,136,92,0,48,160,146,
+252,243,0,0,92,15,0,11,5,22,128,92,0,144,137,140,0,30,144,92,8,0,152,140,
+112,223,255,9,36,11,0,8,52,96,161,144,5,22,128,92,0,16,5,134,144,112,0,
+90,0,16,84,140,46,0,0,21,11,22,128,92,0,80,138,140,32,15,0,11,5,22,128,
+92,0,144,137,140,0,30,144,92,8,0,152,140,52,223,255,9,232,10,0,8,0,0,0,
+0,36,96,161,144,24,96,161,146,86,5,232,140,126,64,55,54,0,48,160,144,252,
+243,0,0,11,22,128,92,1,32,165,140,9,22,136,92,0,48,160,146,252,243,0,0,
+212,14,0,11,5,22,128,92,0,144,137,140,0,30,144,92,8,0,152,140,232,222,255,
+9,156,10,0,8,11,22,128,92,0,80,138,140,176,14,0,11,24,0,0,8,11,22,128,92,
+0,80,138,140,160,14,0,11,12,96,129,144,16,220,255,9,0,48,160,144,248,243,
+0,0,9,0,165,89,0,48,160,146,248,243,0,0,36,1,0,8,0,0,0,0,42,33,11,48,26,
+97,2,59,0,180,35,140,112,4,0,96,0,180,67,140,120,4,0,96,0,180,59,140,64,
+4,0,64,68,96,161,144,0,48,80,146,84,0,0,32,10,30,232,92,0,48,232,146,96,
+0,0,32,0,48,160,146,64,20,0,80,0,16,161,176,0,16,161,176,0,16,162,152,0,
+208,161,152,64,224,167,154,0,48,160,144,64,20,0,80,6,80,48,89,0,48,232,
+140,123,221,4,199,64,224,151,136,102,64,167,58,0,48,168,144,8,244,0,0,134,
+156,161,140,20,58,160,140,0,0,0,0,18,1,165,89,8,9,165,89,47,0,232,140,21,
+80,168,89,20,96,7,90,0,48,168,146,8,244,0,0,26,0,0,22,0,48,160,144,252,
+243,0,0,20,80,160,89,0,48,160,146,252,243,0,0,5,22,128,92,0,144,137,140,
+0,30,144,92,8,0,152,140,216,221,255,9,20,0,0,8,5,22,128,92,0,144,137,140,
+2,30,152,92,196,221,255,9,129,75,74,90,54,0,0,18,5,22,128,92,68,96,217,
+146,12,22,136,92,28,218,255,9,0,30,48,92,198,62,4,58,48,96,161,144,5,22,
+128,92,0,16,5,134,144,112,0,90,0,16,84,140,190,254,255,18,8,127,2,60,8,
+96,241,146,24,96,241,146,120,9,0,8,24,96,137,144,0,180,35,140,112,4,0,96,
+86,64,76,62,11,22,128,92,0,144,146,140,68,96,153,140,92,10,0,9,24,96,161,
+144,8,96,169,144,52,96,177,144,21,0,173,89,0,80,129,140,8,96,169,146,20,
+65,74,89,0,144,5,134,144,112,0,90,0,16,84,140,202,8,0,18,36,96,161,144,
+137,32,5,90,24,96,161,146,20,22,136,92,180,255,255,17,68,96,161,144,0,48,
+160,146,64,20,0,80,8,96,161,144,24,96,169,144,137,48,0,90,9,0,165,89,9,
+65,173,89,8,96,161,146,24,96,169,146,250,0,0,22,137,176,2,90,0,80,130,140,
+10,0,0,22,10,30,128,92,144,28,164,140,144,28,172,140,20,57,160,140,0,0,
+0,0,0,48,80,146,84,0,0,32,1,9,180,89,0,48,160,146,96,0,0,32,22,112,2,90,
+21,158,82,140,178,0,0,17,22,57,160,144,48,157,0,0,0,16,5,132,196,157,0,
+0,184,157,0,0,172,157,0,0,160,157,0,0,148,157,0,0,136,157,0,0,124,157,0,
+0,112,157,0,0,100,157,0,0,88,157,0,0,0,16,161,176,0,16,161,176,0,16,161,
+176,0,16,161,176,0,16,161,176,0,16,161,176,0,16,161,176,0,16,161,176,0,
+16,161,176,0,16,161,176,0,16,161,176,0,16,161,176,0,16,161,176,0,16,161,
+176,0,16,161,176,0,16,161,176,0,16,161,176,0,16,161,176,0,16,161,176,0,
+16,161,176,0,16,161,176,0,16,161,176,0,16,161,176,0,16,161,176,0,16,161,
+176,0,16,161,176,0,16,161,176,0,16,161,176,0,16,161,176,0,16,161,176,16,
+65,74,89,16,127,2,60,0,48,160,144,64,20,0,80,28,96,81,146,68,96,161,146,
+228,7,0,8,0,0,0,0,0,180,123,140,192,4,0,64,137,48,0,90,56,96,33,144,0,30,
+104,92,34,5,0,18,0,208,163,144,0,48,232,140,255,255,0,0,148,64,63,88,146,
+0,57,61,74,32,2,61,210,5,232,140,22,64,55,54,11,22,128,92,140,16,0,11,44,
+2,0,8,0,0,0,0,52,96,161,144,5,22,128,92,0,16,5,134,144,112,0,90,0,16,84,
+140,22,0,0,21,11,22,128,92,100,16,0,11,4,2,0,8,0,0,0,0,36,96,65,144,0,48,
+80,146,68,0,0,32,0,180,163,140,240,4,0,96,0,16,133,176,0,16,165,176,1,9,
+66,89,44,160,82,140,6,80,48,89,138,48,0,90,0,208,160,144,0,208,160,144,
+0,208,160,144,196,1,0,18,0,36,161,140,148,128,38,88,120,4,0,8,14,204,161,
+89,102,32,13,58,18,32,13,49,254,33,21,58,234,34,29,58,68,4,0,8,11,22,128,
+92,244,15,0,11,34,160,1,58,5,22,128,92,0,144,137,140,0,30,144,92,8,0,152,
+140,244,218,255,9,28,0,0,8,0,0,0,0,0,48,160,144,208,243,0,0,20,80,160,89,
+0,48,160,146,208,243,0,0,159,25,32,88,0,0,64,140,0,30,48,92,20,4,0,8,0,
+0,0,0,42,160,1,61,11,22,128,92,160,15,0,11,0,48,160,144,208,243,0,0,159,
+25,32,88,1,32,165,140,0,30,64,92,228,3,0,8,0,0,0,0,142,9,161,88,135,32,
+5,90,1,160,49,140,46,0,0,18,5,22,128,92,0,144,137,140,0,30,144,92,8,0,152,
+140,120,218,255,9,11,22,128,92,0,0,48,140,84,15,0,11,20,2,0,8,0,0,0,0,11,
+22,128,92,80,224,143,140,128,14,0,11,0,48,128,144,76,20,0,80,28,12,164,
+89,20,253,151,144,76,0,0,0,60,96,169,144,0,48,232,140,0,0,255,0,26,12,36,
+89,146,64,167,88,54,0,173,58,0,48,160,144,204,243,0,0,5,22,128,92,0,144,
+137,140,0,30,144,92,8,0,152,140,20,80,160,89,0,0,48,140,0,48,160,146,204,
+243,0,0,0,218,255,9,168,1,0,8,146,208,160,88,44,0,232,140,20,57,160,144,
+96,82,0,0,134,64,175,112,0,48,232,140,255,255,0,0,146,64,63,88,4,9,33,89,
+7,0,165,89,4,124,173,140,208,255,255,255,58,64,165,58,0,48,160,144,204,
+243,0,0,5,22,128,92,0,144,137,140,0,30,144,92,8,0,152,140,20,80,160,89,
+0,0,48,140,0,48,160,146,204,243,0,0,156,217,255,9,68,1,0,8,0,0,0,0,66,32,
+2,61,48,96,161,144,5,22,128,92,0,16,5,134,144,112,0,90,0,16,84,140,42,0,
+0,21,5,22,128,92,0,144,137,140,0,30,144,92,8,0,152,140,100,217,255,9,0,
+30,48,92,159,25,32,88,164,2,0,8,0,0,0,0,26,32,1,58,4,208,128,89,2,12,132,
+89,0,144,138,140,80,224,151,140,228,221,255,11,5,22,128,92,0,144,137,140,
+7,22,144,92,2,0,152,140,204,0,0,8,0,0,0,0,26,160,1,58,11,22,128,92,0,14,
+0,11,5,22,128,92,0,144,137,140,168,0,0,8,5,22,128,92,0,16,139,140,112,213,
+255,9,38,32,4,61,11,22,128,92,220,13,0,11,0,48,160,144,208,243,0,0,20,80,
+160,89,159,25,32,88,36,2,0,8,0,0,0,0,48,96,161,144,5,22,128,92,0,16,5,134,
+144,112,0,90,0,16,84,140,46,0,0,21,12,96,129,144,12,214,255,9,11,22,128,
+92,156,13,0,11,0,48,160,144,208,243,0,0,20,80,160,89,159,25,32,88,228,1,
+0,8,0,0,0,0,0,208,163,144,11,22,128,92,0,48,232,140,0,0,255,0,148,64,167,
+88,0,144,138,140,60,96,161,146,60,12,0,9,144,48,0,90,0,16,84,140,42,0,0,
+21,5,22,128,92,0,0,136,140,0,30,144,92,8,0,152,140,0,30,48,92,88,216,255,
+9,0,30,64,92,159,25,32,88,152,1,0,8,32,96,169,144,0,228,161,140,148,128,
+38,88,1,0,48,140,1,73,69,89,128,1,0,8,38,160,1,58,5,22,128,92,0,144,137,
+140,0,30,144,92,8,0,152,140,28,216,255,9,0,30,48,92,0,0,64,140,159,25,32,
+88,11,22,128,92,80,224,143,140,140,12,0,11,0,48,128,144,76,20,0,80,26,12,
+60,89,64,225,1,58,28,12,164,89,20,253,151,144,76,0,0,0,80,224,167,144,0,
+48,232,140,0,0,255,0,146,64,175,88,148,64,167,88,40,0,173,61,146,208,160,
+88,20,57,176,144,96,82,0,0,0,48,232,140,255,255,0,0,146,64,55,88,8,201,
+169,89,6,128,165,89,46,64,165,58,0,48,160,144,204,243,0,0,0,30,48,92,0,
+0,64,140,20,80,160,89,159,25,32,88,0,48,160,146,204,243,0,0,212,0,0,8,0,
+0,0,0,22,16,162,89,20,193,57,89,0,80,129,140,12,22,136,92,224,211,255,9,
+38,32,4,61,0,48,160,144,208,243,0,0,0,30,48,92,0,0,64,140,20,80,160,89,
+159,25,32,88,148,0,0,8,0,0,0,0,48,96,161,144,5,22,128,92,0,16,5,134,144,
+112,0,90,0,16,84,140,54,0,0,21,0,48,160,144,208,243,0,0,20,80,160,89,0,
+48,160,146,208,243,0,0,12,96,129,144,0,30,48,92,0,0,64,140,159,25,32,88,
+92,212,255,9,84,0,0,8,0,0,0,0,7,208,128,89,2,12,132,89,0,144,138,140,84,
+224,151,140,152,219,255,11,5,22,128,92,1,0,136,140,6,22,144,92,2,0,152,
+140,220,214,255,9,0,30,48,92,32,0,0,8,11,22,128,92,180,11,0,11,0,48,160,
+144,208,243,0,0,20,80,160,89,0,48,160,146,208,243,0,0,13,80,104,89,232,
+90,106,52,8,96,49,146,28,96,81,146,24,96,65,146,56,96,33,146,152,2,0,8,
+76,96,161,144,50,0,101,58,42,160,1,59,8,96,137,144,134,156,145,140,18,58,
+144,140,0,0,0,0,5,22,128,92,2,0,152,140,116,214,255,9,8,96,241,146,24,96,
+241,146,76,96,97,146,98,98,2,59,24,96,161,144,230,32,5,61,8,96,161,144,
+134,32,5,61,5,22,128,92,0,16,139,140,184,210,255,9,14,34,4,58,72,96,169,
+144,32,96,161,144,26,0,173,62,52,96,161,144,5,22,128,92,0,16,5,134,36,96,
+161,144,20,0,0,8,48,96,161,144,5,22,128,92,0,16,5,134,32,96,161,144,24,
+96,161,146,16,22,80,92,146,32,12,61,0,48,160,144,184,243,0,0,9,0,165,89,
+0,48,160,146,184,243,0,0,12,96,129,144,60,211,255,9,11,22,128,92,0,80,138,
+140,184,5,0,11,24,96,241,146,212,1,0,8,0,0,0,0,52,96,161,144,5,22,128,92,
+0,16,5,134,144,112,0,90,0,16,84,140,62,0,0,21,0,48,160,144,184,243,0,0,
+11,22,128,92,9,0,165,89,0,80,138,140,0,48,160,146,184,243,0,0,116,5,0,11,
+8,96,137,144,5,22,128,92,0,0,144,140,2,30,152,92,136,213,255,9,8,96,241,
+146,36,96,161,144,24,96,161,146,8,96,177,144,72,96,169,144,9,22,32,92,9,
+128,165,89,10,64,165,60,22,65,37,89,24,96,137,144,4,65,74,89,130,64,36,
+62,11,22,128,92,0,144,146,140,68,96,153,140,72,2,0,9,24,96,161,144,8,96,
+169,144,52,96,177,144,21,0,173,89,0,80,129,140,8,96,169,146,20,1,33,89,
+0,144,5,134,144,112,0,90,0,16,84,140,54,0,0,21,8,96,137,144,5,22,128,92,
+0,0,144,140,8,30,152,92,8,213,255,9,11,22,128,92,0,16,137,140,212,4,0,11,
+24,96,241,146,8,96,241,146,16,0,0,8,0,0,0,0,36,96,161,144,24,96,161,146,
+24,96,137,144,136,95,36,57,30,32,1,58,11,22,128,92,0,16,137,140,10,22,144,
+92,68,96,153,140,196,1,0,9,16,22,80,92,8,96,161,144,72,96,169,144,4,0,141,
+89,8,96,137,146,46,64,141,61,145,92,148,140,18,58,144,140,0,0,0,0,5,22,
+128,92,2,0,152,140,148,212,255,9,8,96,241,146,24,96,241,146,24,0,0,8,0,
+0,0,0,24,96,161,144,28,96,81,146,4,1,165,89,24,96,161,146,16,126,2,60,104,
+0,0,8,8,96,137,144,5,22,128,92,0,0,144,140,8,30,152,92,88,212,255,9,11,
+22,128,92,0,80,138,140,36,4,0,11,24,96,241,146,8,96,241,146,60,0,0,8,0,
+0,0,0,0,48,160,144,184,243,0,0,11,22,128,92,9,0,165,89,0,80,138,140,0,48,
+160,146,184,243,0,0,244,3,0,11,20,0,0,8,0,0,0,0,11,22,128,92,0,80,138,140,
+224,3,0,11,0,16,166,144,48,51,5,61,11,80,88,89,11,240,0,90,4,32,198,140,
+0,225,24,140,0,97,206,140,12,243,255,22,0,116,192,176,240,255,255,255,0,
+0,0,10,0,0,0,0,0,0,0,0,12,32,36,144,4,29,129,140,16,58,128,140,0,0,0,0,
+112,16,0,9,0,48,128,146,224,193,3,0,14,32,4,61,8,30,128,92,0,0,0,10,0,48,
+128,144,224,193,3,0,4,29,41,140,5,58,40,140,0,0,0,0,5,22,136,92,224,24,
+0,9,1,9,161,89,0,48,240,146,100,180,3,0,0,48,160,146,96,180,3,0,110,32,
+1,59,0,47,1,90,0,0,176,140,40,0,0,16,0,48,168,144,224,193,3,0,80,0,176,
+140,150,96,1,90,9,0,184,140,4,96,189,146,159,25,184,88,56,96,189,146,62,
+0,0,19,9,30,144,92,159,25,152,88,0,48,168,144,224,193,3,0,22,64,165,89,
+4,32,149,146,56,32,157,146,21,188,165,140,80,0,0,0,160,160,181,140,150,
+96,1,90,4,32,149,146,56,32,157,146,212,255,255,20,2,30,128,92,0,0,0,10,
+0,0,0,0,0,208,172,144,17,58,160,140,0,0,0,0,17,57,136,140,0,0,0,0,17,1,
+237,89,127,0,64,140,29,32,2,90,0,48,168,146,64,20,0,80,190,0,0,22,8,14,
+164,89,0,52,141,140,112,4,0,96,135,25,72,88,127,0,80,140,0,48,144,146,84,
+0,0,32,0,48,72,146,96,0,0,32,0,80,164,176,0,80,164,176,0,80,164,176,0,80,
+164,176,0,80,164,176,0,80,164,176,0,80,164,176,0,80,164,176,0,80,164,176,
+0,80,164,176,0,80,164,176,0,80,164,176,0,80,164,176,0,80,164,176,0,80,164,
+176,0,80,164,176,0,80,164,176,0,80,164,176,0,80,164,176,0,80,164,176,0,
+80,164,176,0,80,164,176,0,80,164,176,0,80,164,176,0,80,164,176,0,80,164,
+176,0,80,164,176,0,80,164,176,0,80,164,176,0,116,239,140,128,255,255,255,
+0,162,148,140,29,160,2,90,0,80,164,176,0,80,164,176,0,80,164,176,96,255,
+255,17,63,0,64,140,114,0,234,54,0,48,144,146,84,0,0,32,134,25,64,88,0,48,
+64,146,96,0,0,32,8,14,164,89,0,52,165,140,112,4,0,96,0,16,37,176,0,16,37,
+176,0,16,37,176,0,16,37,176,0,16,37,176,0,16,37,176,0,16,37,176,0,16,37,
+176,0,16,37,176,0,16,37,176,0,16,37,176,0,16,37,176,0,16,37,176,0,161,148,
+140,0,116,239,140,192,255,255,255,0,16,37,176,0,16,37,176,0,16,165,176,
+82,96,255,51,0,48,144,146,84,0,0,32,31,88,64,89,0,48,64,146,96,0,0,32,8,
+14,164,89,0,52,165,140,112,4,0,96,0,16,37,176,0,16,37,176,0,16,37,176,0,
+16,37,176,0,16,37,176,128,160,148,140,0,116,239,140,224,255,255,255,0,16,
+37,176,0,16,37,176,0,16,165,176,58,96,191,51,0,48,144,146,80,0,0,32,8,14,
+164,89,0,52,165,140,112,4,0,96,0,16,37,176,0,16,37,176,0,16,37,176,96,160,
+148,140,24,73,239,89,0,16,37,176,0,16,37,176,0,16,165,176,46,96,95,51,8,
+14,164,89,0,48,144,146,72,0,0,32,0,52,165,140,112,4,0,96,48,160,148,140,
+12,73,239,89,0,16,37,176,0,16,37,176,0,16,165,176,46,96,31,51,8,14,164,
+89,0,52,141,140,112,4,0,96,0,48,144,146,64,0,0,32,4,73,239,89,16,160,148,
+140,29,240,0,90,0,80,164,176,232,255,255,17,46,96,7,58,8,14,132,89,0,52,
+132,140,124,4,0,96,0,48,144,146,56,0,0,32,1,73,239,89,4,160,148,140,157,
+48,0,90,0,16,164,144,232,255,255,21,0,48,160,144,64,20,0,80,18,22,128,92,
+0,208,164,146,0,0,0,10,0,0,0,0,0,0,0,0,0,48,240,140,144,169,0,0,30,22,144,
+92,0,0,240,140,8,78,140,89,0,116,140,140,64,4,0,64,0,80,164,176,0,16,164,
+178,0,80,164,176,16,16,132,89,0,16,164,178,0,80,164,176,16,16,132,89,0,
+16,164,178,0,144,4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,48,240,140,224,
+169,0,0,30,22,152,92,0,0,240,140,8,14,132,89,145,48,0,90,0,80,148,140,0,
+52,132,140,64,4,0,64,26,0,0,18,0,16,164,176,0,16,164,176,1,137,148,89,0,
+16,164,176,240,191,4,61,0,208,4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,55,
+160,172,140,31,88,76,89,137,69,173,116,1,25,72,89,0,48,72,146,64,12,0,80,
+0,48,160,144,0,244,0,0,8,206,236,89,0,48,184,144,240,243,0,0,17,22,56,92,
+0,144,53,140,20,80,160,89,145,48,0,90,0,48,160,146,0,244,0,0,21,192,173,
+89,0,48,168,146,240,243,0,0,0,116,135,146,64,8,0,64,0,116,71,140,64,0,0,
+112,0,30,232,92,2,222,87,89,255,0,88,140,146,0,0,22,4,160,161,144,0,144,
+41,144,6,16,50,89,1,201,57,89,130,13,37,89,118,32,1,58,29,129,170,89,4,
+96,5,90,0,16,161,140,10,0,0,22,21,22,160,92,29,0,237,89,0,48,40,146,48,
+0,0,32,20,1,33,89,0,48,160,146,96,0,0,32,29,240,2,90,20,93,41,140,202,255,
+255,22,0,48,160,128,8,0,0,16,148,192,138,88,34,96,92,51,0,16,162,176,12,
+73,140,89,0,16,162,176,17,240,2,90,0,16,162,176,12,73,239,89,232,255,255,
+17,212,127,95,52,148,255,255,8,0,0,0,0,120,255,1,60,255,0,72,140,0,48,160,
+128,8,0,0,16,148,64,162,88,244,95,167,52,18,98,95,52,29,57,160,144,16,171,
+0,0,0,16,5,132,96,171,0,0,208,172,0,0,176,172,0,0,136,172,0,0,104,172,0,
+0,64,172,0,0,24,172,0,0,240,171,0,0,200,171,0,0,160,171,0,0,128,171,0,0,
+64,171,0,0,0,16,162,176,0,16,162,176,0,16,162,152,8,206,164,89,0,16,170,
+144,0,52,165,140,64,0,0,64,0,16,245,146,8,206,164,89,129,9,132,88,0,52,
+133,146,64,8,0,64,0,52,165,140,64,0,0,64,0,16,245,146,112,1,0,8,8,206,164,
+89,129,9,132,88,0,52,133,146,64,8,0,64,0,16,162,176,0,16,162,176,0,16,162,
+152,116,1,0,8,8,206,164,89,129,9,132,88,0,52,133,146,64,8,0,64,0,16,34,
+176,0,52,165,140,64,0,0,64,0,16,34,176,0,16,170,144,72,1,0,8,8,206,164,
+89,129,9,132,88,0,52,133,146,64,8,0,64,0,16,34,176,0,52,165,140,64,0,0,
+64,0,16,34,176,32,1,0,8,0,0,0,0,8,206,164,89,129,9,132,88,0,52,133,146,
+64,8,0,64,0,16,34,176,0,16,178,152,0,16,170,144,0,52,165,140,64,0,0,64,
+240,0,0,8,8,206,164,89,129,9,132,88,0,52,133,146,64,8,0,64,0,16,34,176,
+0,16,178,152,0,52,165,140,64,0,0,64,200,0,0,8,0,0,0,0,8,206,164,89,129,
+9,132,88,0,52,133,146,64,8,0,64,0,16,34,176,0,16,170,144,0,52,165,140,64,
+0,0,64,156,0,0,8,0,0,0,0,8,206,164,89,129,9,132,88,0,52,133,146,64,8,0,
+64,0,16,34,176,0,52,165,140,64,0,0,64,116,0,0,8,8,206,164,89,129,9,132,
+88,0,52,133,146,64,8,0,64,0,16,178,152,0,16,170,144,0,52,165,140,64,0,0,
+64,76,0,0,8,0,0,0,0,8,206,164,89,129,9,132,88,0,52,133,146,64,8,0,64,0,
+16,178,152,0,52,165,140,64,0,0,64,36,0,0,8,8,206,164,89,129,9,132,88,0,
+52,133,146,64,8,0,64,0,16,170,144,0,52,165,140,64,0,0,64,0,16,245,146,0,
+16,245,146,0,16,245,146,0,16,245,146,0,16,245,146,0,16,245,146,0,16,245,
+146,0,16,245,146,0,16,245,146,8,206,156,89,0,244,156,140,64,0,0,64,0,208,
+148,146,0,48,160,144,64,12,0,80,20,21,160,88,0,208,164,146,0,0,0,10,0,0,
+0,0,0,0,0,0,0,0,0,0,0,48,240,140,136,173,0,0,30,22,144,92,0,0,240,140,1,
+9,132,89,1,25,168,89,42,64,133,58,8,78,140,89,0,116,164,140,80,8,0,64,0,
+116,140,140,96,8,0,64,0,16,245,146,1,9,132,89,0,80,244,146,244,95,133,61,
+0,144,4,132,0,0,0,0,0,0,0,10,0,0,0,0,0,48,136,146,64,0,0,32,8,14,172,89,
+0,116,181,140,240,4,0,96,0,144,37,176,17,16,164,89,0,48,160,146,64,0,0,
+32,0,144,37,176,32,96,132,140,0,48,128,146,56,0,0,32,0,116,173,140,252,
+4,0,96,16,16,129,89,0,80,165,144,0,48,128,146,56,0,0,32,16,16,129,89,0,
+80,165,144,0,0,0,10,0,0,0,0,0,0,0,0,0,48,240,140,64,174,0,0,30,22,144,92,
+0,0,240,140,8,14,132,89,0,52,132,140,192,4,0,64,0,16,164,144,1,30,176,92,
+4,96,172,140,0,80,164,146,0,16,164,144,0,80,165,146,0,16,164,144,22,144,
+176,89,150,176,2,90,4,96,165,146,21,16,170,89,228,255,255,22,0,144,4,132,
+0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,48,240,140,160,174,0,0,30,22,144,92,
+0,0,240,140,8,14,132,89,0,52,132,140,192,4,0,64,0,16,164,144,1,30,176,92,
+4,96,172,140,0,80,164,146,0,16,164,144,0,80,165,146,0,16,164,144,22,144,
+176,89,150,176,2,90,4,96,165,146,21,16,170,89,228,255,255,22,0,144,4,132,
+0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,48,240,140,232,174,0,0,30,22,136,92,
+0,0,240,140,8,14,132,89,0,52,132,140,192,4,0,64,0,16,164,176,0,16,164,176,
+0,16,164,144,0,16,164,144,0,16,164,144,0,80,4,132,0,0,0,0,0,0,0,10,0,0,
+0,0,21,22,80,92,48,0,232,140,0,48,184,144,176,243,0,0,157,133,172,116,0,
+208,92,140,8,206,156,89,3,160,148,140,21,192,173,89,130,141,68,89,0,48,
+168,146,176,243,0,0,0,244,132,146,64,8,0,64,22,22,72,92,148,48,0,90,0,144,
+181,152,20,22,96,92,0,0,32,140,0,244,52,140,64,0,0,112,0,30,128,92,130,
+205,45,89,0,144,61,140,162,0,0,21,136,48,0,90,2,222,119,89,255,0,24,140,
+138,0,0,22,22,96,1,61,9,16,74,89,0,80,162,152,130,77,45,89,0,16,61,140,
+4,129,171,89,133,96,5,90,0,80,161,140,10,0,0,22,21,22,160,92,4,0,37,89,
+20,1,66,89,0,48,56,146,48,0,0,32,20,65,41,89,0,48,160,146,96,0,0,32,132,
+240,2,90,20,221,57,140,54,0,0,22,0,48,160,128,8,0,0,16,28,0,0,8,0,144,161,
+176,0,144,161,176,0,144,161,176,0,48,160,128,8,0,0,16,12,9,33,89,148,192,
+160,88,228,63,93,52,212,63,89,60,128,63,2,60,0,0,0,10,0,0,0,0,136,48,0,
+90,2,222,111,89,255,0,120,140,158,0,0,22,22,96,1,61,9,16,74,89,0,80,162,
+152,130,77,45,89,0,16,61,140,4,65,171,89,133,96,5,90,0,80,161,140,10,0,
+0,22,21,22,160,92,4,0,37,89,20,1,66,89,0,48,56,146,48,0,0,32,20,65,41,89,
+0,48,160,146,96,0,0,32,132,240,2,90,20,221,57,140,74,0,0,22,48,0,0,8,0,
+0,0,0,0,144,161,176,16,80,128,89,0,144,161,176,144,32,3,90,0,144,161,176,
+12,9,33,89,18,0,0,21,10,22,128,93,208,252,255,11,0,30,128,92,0,48,160,128,
+8,0,0,16,148,192,163,88,204,63,93,52,240,63,89,60,108,63,2,60,10,22,128,
+93,164,252,255,8,0,30,32,92,0,16,36,146,132,73,140,89,16,16,129,89,244,
+127,36,62,0,0,0,10,0,0,0,0,0,0,0,0,16,72,8,89,0,116,192,178,240,255,255,
+255,18,208,136,89,4,25,216,89,145,192,142,88,51,96,188,140,31,88,219,89,
+155,197,189,112,0,80,197,140,0,48,168,144,212,243,0,0,0,48,232,144,192,
+243,0,0,18,22,208,92,1,96,173,140,19,22,120,92,0,48,168,146,212,243,0,0,
+23,64,191,89,0,48,184,146,192,243,0,0,20,22,24,92,130,77,84,89,0,144,37,
+152,22,22,104,92,0,0,232,140,17,16,114,89,2,222,223,89,130,77,73,89,137,
+224,6,90,0,80,138,140,10,0,0,22,2,222,143,89,18,208,144,89,4,25,216,89,
+146,192,150,88,0,48,184,144,112,82,0,0,17,22,64,92,44,0,216,140,14,224,
+6,90,0,48,168,144,116,82,0,0,255,3,216,140,23,80,184,89,151,192,190,88,
+0,48,216,140,0,0,255,255,149,192,174,88,0,48,216,140,255,255,255,0,0,48,
+32,146,48,0,0,32,149,131,172,88,0,116,173,140,0,0,1,0,149,192,182,88,0,
+48,216,140,0,0,255,0,0,48,136,146,96,0,0,32,17,129,82,89,17,29,97,140,17,
+65,74,89,0,48,184,146,112,82,0,0,0,48,176,146,116,82,0,0,149,192,206,88,
+250,0,0,17,42,160,2,59,13,16,106,89,0,80,163,152,130,77,77,89,0,48,160,
+146,48,0,0,32,9,129,82,89,0,48,72,146,96,0,0,32,224,191,2,60,8,206,36,89,
+0,52,161,140,64,8,0,64,0,16,133,146,0,48,160,144,112,82,0,0,0,48,176,144,
+116,82,0,0,14,222,216,89,148,195,166,88,0,52,169,140,192,0,0,64,0,48,160,
+146,76,12,0,80,0,80,181,146,80,206,255,9,0,48,160,128,8,0,0,16,30,32,5,
+58,0,52,153,140,192,0,0,112,0,208,164,144,0,48,160,128,8,0,0,16,244,63,
+5,61,8,206,163,89,153,131,182,88,44,0,216,140,14,193,174,89,0,52,165,140,
+192,0,0,64,2,76,173,89,0,16,181,146,149,48,0,90,0,0,176,140,34,0,0,18,12,
+96,5,48,1,30,176,92,12,0,0,8,0,16,245,146,22,144,176,89,0,16,245,146,244,
+95,181,52,8,206,163,89,26,142,171,89,0,52,165,140,192,0,0,64,0,16,173,146,
+0,116,192,176,240,255,255,255,0,0,0,10,145,112,2,90,2,222,223,89,78,0,0,
+17,13,16,106,89,0,80,35,152,8,193,190,89,130,77,73,89,137,224,5,90,0,80,
+138,140,10,0,0,22,23,22,136,92,8,64,68,89,17,65,74,89,0,48,32,146,48,0,
+0,32,17,129,82,89,0,48,136,146,96,0,0,32,136,112,2,90,17,29,97,140,188,
+255,255,22,0,48,184,144,112,82,0,0,8,206,180,89,0,180,173,140,64,8,0,64,
+0,80,133,146,0,48,168,144,116,82,0,0,255,0,216,140,143,201,93,88,0,180,
+181,140,192,0,0,64,0,48,88,146,76,12,0,80,0,144,173,146,0,48,168,128,8,
+0,0,16,149,192,174,88,244,127,77,51,8,206,180,89,0,180,173,140,192,0,0,
+112,0,80,37,176,0,80,37,176,148,48,0,90,0,80,165,152,0,180,181,140,192,
+0,0,64,0,48,216,140,0,0,0,176,10,9,66,89,0,144,221,146,0,180,115,140,212,
+255,255,255,230,1,0,21,14,160,2,61,31,88,219,89,252,193,118,54,82,160,2,
+59,22,96,2,61,13,16,106,89,0,80,163,152,130,77,77,89,0,16,101,140,2,222,
+223,89,8,193,166,89,137,32,5,90,0,80,138,140,10,0,0,22,20,22,136,92,8,64,
+68,89,0,48,96,146,48,0,0,32,17,65,74,89,17,29,99,140,17,129,82,89,0,48,
+136,146,96,0,0,32,134,32,82,59,8,206,164,89,0,52,133,140,192,0,0,112,0,
+52,141,140,192,0,0,64,0,48,160,128,8,0,0,16,255,0,216,140,84,0,0,8,0,0,
+0,0,0,228,162,140,0,48,216,140,255,63,0,0,148,192,94,88,0,48,88,146,76,
+12,0,80,0,16,164,176,0,16,164,176,0,16,164,152,0,16,164,144,0,48,216,140,
+0,0,0,176,0,80,220,146,0,48,160,128,8,0,0,16,255,0,216,140,11,9,66,89,0,
+180,115,140,212,255,255,255,148,192,166,88,176,63,85,52,152,63,82,60,46,
+191,2,61,31,88,219,89,36,223,118,49,24,1,0,8,82,160,2,59,22,96,2,61,13,
+16,106,89,0,80,163,152,130,77,77,89,0,16,101,140,2,222,223,89,8,193,166,
+89,137,32,5,90,0,80,138,140,10,0,0,22,20,22,136,92,8,64,68,89,0,48,96,146,
+48,0,0,32,17,65,74,89,17,29,99,140,17,129,82,89,0,48,136,146,96,0,0,32,
+162,32,82,59,8,206,163,89,0,52,37,140,192,0,0,112,0,52,53,140,192,0,0,64,
+0,48,160,128,8,0,0,16,255,0,216,140,148,192,166,88,118,32,85,51,0,160,41,
+140,29,80,232,89,22,192,232,61,24,22,128,92,0,208,139,140,24,248,255,11,
+0,30,232,92,0,228,162,140,0,48,216,140,255,63,0,0,148,192,94,88,0,48,88,
+146,76,12,0,80,0,16,161,176,0,16,161,176,0,16,161,152,0,16,161,144,0,48,
+216,140,0,0,0,176,0,80,217,146,0,48,160,128,8,0,0,16,255,0,216,140,11,9,
+66,89,0,180,115,140,212,255,255,255,148,192,166,88,152,63,85,52,124,63,
+82,60,18,191,2,61,31,88,219,89,8,223,118,49,29,80,232,89,22,192,232,61,
+24,22,128,92,0,208,139,140,156,247,255,11,0,30,232,92,0,228,162,140,0,48,
+216,140,255,63,0,0,148,192,166,88,142,9,165,88,0,48,160,146,76,12,0,80,
+255,0,216,140,0,48,160,128,8,0,0,16,148,192,166,88,244,31,162,60,0,48,160,
+128,8,0,0,16,34,32,5,58,8,206,163,89,0,52,173,140,192,0,0,112,0,80,165,
+144,0,48,160,128,8,0,0,16,244,63,5,61,8,206,163,89,153,131,182,88,44,0,
+216,140,14,193,174,89,2,76,173,89,0,52,165,140,192,0,0,64,1,73,173,89,1,
+25,216,89,0,16,181,146,18,192,174,58,1,73,173,89,0,16,245,146,248,223,174,
+61,8,206,163,89,26,142,171,89,0,52,165,140,192,0,0,64,0,16,173,146,26,224,
+0,58,29,80,232,89,18,192,232,61,24,22,128,92,0,208,139,140,220,246,255,
+11,0,116,192,176,240,255,255,255,0,0,0,10,0,0,0,0,0,48,160,144,124,82,0,
+0,16,16,180,89,0,48,184,140,0,192,2,0,0,48,32,144,120,82,0,0,22,0,165,89,
+18,192,165,62,0,30,128,92,0,0,0,10,0,0,0,0,0,48,168,144,120,82,0,0,0,48,
+160,144,124,82,0,0,4,22,128,92,0,144,141,140,21,188,173,140,0,0,0,0,22,
+0,165,89,0,48,168,146,120,82,0,0,0,48,160,146,124,82,0,0,60,8,0,9,4,208,
+131,89,16,25,184,89,144,192,133,88,0,32,132,140,0,0,0,10,0,0,0,0,0,48,160,
+140,128,82,0,0,20,16,153,89,3,32,149,140,0,48,136,140,0,4,0,136,0,30,128,
+92,0,0,232,140,255,0,104,140,135,25,112,88,0,16,173,128,36,96,5,58,0,80,
+172,144,0,144,180,128,0,208,188,128,8,76,173,89,149,128,173,88,149,64,171,
+88,8,192,173,58,16,80,128,89,8,32,173,128,36,96,5,58,4,96,172,144,8,160,
+180,128,8,224,188,128,8,76,173,89,149,128,173,88,149,64,171,88,8,192,173,
+58,16,80,128,89,16,32,173,128,36,96,5,58,8,96,172,144,16,160,180,128,16,
+224,188,128,8,76,173,89,149,128,173,88,149,64,171,88,8,192,173,58,16,80,
+128,89,29,208,232,89,157,160,3,90,12,96,140,140,20,16,166,89,24,224,156,
+140,18,16,150,89,112,255,255,22,14,32,4,58,0,30,128,92,0,0,0,10,0,30,80,
+92,0,0,72,140,0,30,32,92,0,0,40,140,0,30,48,92,52,1,0,9,0,30,64,92,0,0,
+56,140,0,48,64,154,128,243,0,0,0,48,32,178,112,243,0,0,1,30,128,92,0,48,
+240,146,136,243,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,48,240,140,152,184,0,0,30,
+22,152,92,0,0,240,140,135,25,184,88,14,192,133,54,0,30,128,92,0,208,4,132,
+144,57,168,140,128,82,0,0,0,80,165,128,14,32,5,61,0,30,128,92,0,208,4,132,
+2,96,165,128,255,0,184,140,16,57,176,140,0,4,0,136,145,0,141,88,145,192,
+141,88,8,78,164,89,146,48,0,90,0,144,165,146,38,0,0,18,0,144,165,144,1,
+96,173,128,8,12,165,89,148,64,165,88,148,192,165,88,14,64,164,58,0,30,128,
+92,0,208,4,132,1,30,128,92,0,208,4,132,0,0,0,10,0,0,0,0,0,48,240,140,0,
+185,0,0,30,22,144,92,0,0,240,140,135,25,176,88,14,128,133,54,0,30,128,92,
+0,144,4,132,144,57,168,140,128,82,0,0,0,80,165,128,14,32,5,61,0,30,128,
+92,0,144,4,132,16,57,128,140,0,4,0,136,0,16,164,144,1,96,173,128,8,12,165,
+89,148,64,165,88,1,0,128,140,0,80,164,130,0,144,4,132,0,0,0,0,0,0,0,10,
+0,0,0,0,0,0,0,0,0,0,0,0,16,72,8,89,0,48,160,128,64,84,0,0,1,30,136,92,64,
+224,143,130,32,32,5,58,0,48,160,128,66,84,0,0,136,25,136,88,8,14,165,89,
+148,64,164,88,0,48,160,146,224,4,0,136,232,3,128,140,160,5,0,11,0,48,160,
+128,16,83,0,0,32,32,5,58,0,48,160,144,72,4,0,136,0,48,168,128,17,83,0,0,
+8,12,165,89,148,64,165,88,64,224,167,130,0,48,160,128,24,83,0,0,65,224,
+183,140,32,32,5,58,0,48,160,144,76,4,0,136,0,48,168,128,25,83,0,0,8,12,
+165,89,148,64,165,88,0,144,165,130,65,224,167,128,64,224,175,128,0,48,176,
+144,112,243,0,0,0,48,184,128,80,83,0,0,8,14,165,89,148,67,165,88,20,128,
+165,89,0,48,160,146,112,243,0,0,32,224,5,58,0,48,160,144,104,4,0,136,0,
+48,168,128,81,83,0,0,8,12,165,89,148,64,165,88,64,224,167,130,0,48,160,
+128,88,83,0,0,66,224,183,140,32,32,5,58,0,48,160,144,108,4,0,136,0,48,168,
+128,89,83,0,0,8,12,165,89,148,64,165,88,0,144,165,130,0,48,160,128,96,83,
+0,0,65,224,183,140,32,32,5,58,0,48,160,144,112,4,0,136,0,48,168,128,97,
+83,0,0,8,12,165,89,148,64,165,88,0,144,165,130,65,224,167,128,66,224,175,
+128,64,224,183,128,0,48,184,144,120,243,0,0,0,48,128,128,104,83,0,0,16,
+14,165,89,8,78,173,89,148,67,165,88,148,131,165,88,20,192,165,89,0,48,160,
+146,120,243,0,0,32,32,4,58,0,48,160,144,116,4,0,136,0,48,168,128,105,83,
+0,0,8,12,165,89,148,64,165,88,64,224,167,130,0,48,160,128,112,83,0,0,66,
+224,183,140,32,32,5,58,0,48,160,144,120,4,0,136,0,48,168,128,113,83,0,0,
+8,12,165,89,148,64,165,88,0,144,165,130,0,48,160,128,120,83,0,0,65,224,
+183,140,32,32,5,58,0,48,160,144,124,4,0,136,0,48,168,128,121,83,0,0,8,12,
+165,89,148,64,165,88,0,144,165,130,65,224,167,128,66,224,175,128,64,224,
+183,128,0,48,184,144,124,243,0,0,0,48,128,128,64,84,0,0,16,14,165,89,8,
+78,173,89,148,67,165,88,148,131,165,88,20,192,165,89,0,48,160,146,124,243,
+0,0,32,32,4,58,0,48,160,144,224,4,0,136,0,48,168,128,65,84,0,0,8,12,165,
+89,148,64,165,88,64,224,167,130,0,48,160,128,72,84,0,0,65,224,183,140,32,
+32,5,58,0,48,160,144,228,4,0,136,0,48,168,128,73,84,0,0,8,12,165,89,148,
+64,165,88,0,144,165,130,65,224,167,128,64,224,175,128,0,48,176,144,116,
+243,0,0,0,48,184,128,80,84,0,0,8,14,165,89,148,67,165,88,20,128,165,89,
+0,48,160,146,116,243,0,0,32,224,5,58,0,48,160,144,232,4,0,136,0,48,168,
+128,81,84,0,0,8,12,165,89,148,64,165,88,64,224,167,130,0,48,160,128,88,
+84,0,0,65,224,183,140,32,32,5,58,0,48,160,144,236,4,0,136,0,48,168,128,
+89,84,0,0,8,12,165,89,148,64,165,88,0,144,165,130,65,224,167,128,64,224,
+135,128,0,48,176,144,128,243,0,0,255,0,136,140,0,48,184,128,40,85,0,0,8,
+14,165,89,144,64,172,88,148,67,165,88,20,128,165,89,0,48,160,146,128,243,
+0,0,32,224,5,58,0,48,160,128,42,85,0,0,144,0,165,88,148,64,164,88,8,14,
+165,89,0,48,160,146,84,5,0,136,232,3,128,140,160,2,0,11,0,48,160,128,32,
+85,0,0,148,48,0,90,65,224,183,140,32,0,0,18,0,48,160,144,80,5,0,136,0,48,
+168,128,33,85,0,0,8,12,165,89,148,64,165,88,0,144,165,130,65,224,167,128,
+0,48,168,144,132,243,0,0,0,48,176,128,40,85,0,0,20,64,165,89,65,224,191,
+140,0,48,160,146,132,243,0,0,32,160,5,58,0,48,160,144,84,5,0,136,0,48,168,
+128,41,85,0,0,8,12,165,89,148,64,165,88,0,208,165,130,65,224,167,128,0,
+48,168,144,136,243,0,0,20,64,165,89,0,48,160,146,136,243,0,0,0,0,0,10,0,
+0,0,0,0,48,240,140,32,190,0,0,30,22,152,92,0,0,240,140,16,72,8,89,0,16,
+164,144,0,16,172,144,0,48,144,140,0,0,255,0,149,128,172,88,255,0,144,140,
+0,16,180,144,8,12,189,89,144,77,141,89,151,128,172,88,135,25,144,88,24,
+140,133,89,14,128,172,54,0,30,176,92,64,0,0,8,149,57,176,140,128,82,0,0,
+0,144,165,128,14,32,5,61,0,30,176,92,40,0,0,8,21,57,160,140,0,4,0,136,0,
+16,165,144,1,160,173,128,8,12,165,89,148,64,165,88,1,0,176,140,0,116,160,
+130,240,255,255,255,22,160,5,61,0,30,128,92,16,73,8,89,0,208,4,132,0,0,
+0,0,16,73,176,89,0,144,165,128,145,0,172,88,255,0,144,140,151,128,188,88,
+135,25,144,88,16,1,165,88,148,67,141,88,23,160,4,90,0,144,141,130,80,0,
+0,17,151,57,128,140,128,82,0,0,0,16,164,128,64,32,5,58,2,32,172,128,255,
+0,144,140,23,57,176,140,0,4,0,136,145,64,173,88,149,128,172,88,8,78,165,
+89,0,144,165,146,0,144,165,144,1,32,180,128,1,30,184,92,8,12,165,89,148,
+128,165,88,148,128,164,88,10,64,165,58,0,30,184,92,22,224,5,61,0,30,128,
+92,16,73,8,89,0,208,4,132,0,0,0,0,1,30,128,92,16,73,8,89,0,208,4,132,0,
+0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,48,160,140,240,193,3,0,20,22,128,
+92,0,48,184,140,0,4,0,136,0,48,176,140,128,82,0,0,0,30,136,92,255,0,152,
+140,1,25,232,89,135,25,144,88,0,144,173,128,18,96,5,61,0,16,237,146,24,
+0,0,8,0,0,0,0,0,208,173,144,8,76,173,89,149,192,172,88,0,16,172,146,8,160,
+173,128,14,96,5,61,4,32,237,146,20,0,0,8,4,224,173,144,8,76,173,89,149,
+192,172,88,4,32,172,146,16,160,173,128,14,96,5,61,8,32,237,146,20,0,0,8,
+8,224,173,144,8,76,173,89,149,192,172,88,8,32,172,146,17,208,136,89,145,
+160,4,90,12,32,132,140,20,16,163,89,12,224,189,140,22,16,182,89,132,255,
+255,22,0,0,0,10,0,48,240,140,8,191,0,0,30,22,136,92,0,0,240,140,16,22,160,
+92,148,48,0,90,1,9,132,89,244,255,255,19,0,80,4,132,0,0,0,0,0,0,0,10,0,
+0,0,0,3,30,128,92,188,5,0,8,0,0,0,0,0,0,0,0,17,22,144,92,0,0,136,140,136,
+0,0,8,0,0,0,0,1,30,128,92,12,30,0,9,12,32,244,146,0,0,0,10,16,22,48,92,
+108,1,0,9,88,30,0,9,12,32,36,144,4,22,160,92,1,9,33,89,38,32,5,59,4,29,
+44,140,16,96,161,144,0,16,5,134,4,22,160,92,148,48,0,90,4,73,41,89,1,9,
+33,89,232,255,255,17,132,10,0,9,6,22,128,92,92,24,0,8,0,0,0,0,0,0,0,0,160,
+255,255,9,76,6,0,9,16,9,0,11,1,30,128,92,192,4,0,8,0,0,0,0,0,0,0,0,0,0,
+0,0,144,208,163,88,255,0,152,140,148,192,164,88,148,48,0,90,0,16,180,140,
+18,22,184,92,42,0,0,18,38,160,4,58,0,144,141,130,22,80,176,89,150,208,163,
+88,148,192,164,88,148,48,0,90,1,201,189,89,10,0,0,18,228,255,5,61,10,224,
+5,61,0,0,0,10,58,224,125,51,145,192,164,88,8,14,173,89,149,3,173,88,16,
+78,165,89,148,67,165,88,0,144,165,146,4,160,165,146,8,160,165,146,16,201,
+189,89,12,160,165,146,23,240,3,90,16,160,181,140,228,255,255,17,26,224,
+5,58,1,201,189,89,0,144,141,130,151,48,0,90,1,160,181,140,240,255,255,21,
+0,0,0,10,0,0,0,0,0,48,240,140,112,192,0,0,30,22,136,92,0,0,240,140,0,16,
+164,144,20,80,160,89,0,16,164,146,0,80,4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,
+0,0,0,100,101,102,97,117,108,116,46,112,102,0,0,0,0,0,0,112,114,111,102,
+105,108,101,32,99,111,108,108,101,99,116,105,111,110,32,101,114,114,111,
+114,46,10,0,0,0,0,0,0,0,48,160,144,136,86,0,0,10,32,5,61,0,0,0,10,0,48,
+160,144,200,92,0,0,0,48,48,140,200,92,0,0,0,30,32,92,0,0,64,140,34,32,5,
+58,8,160,169,144,6,16,51,89,0,144,161,144,8,80,64,89,148,48,0,90,4,64,37,
+89,232,255,255,21,0,48,128,140,128,192,0,0,182,1,136,140,28,25,0,9,144,
+48,0,90,0,16,44,140,224,2,0,20,136,13,161,89,0,48,160,130,241,195,3,0,144,
+13,169,89,0,48,32,130,240,195,3,0,152,13,161,89,0,48,168,130,242,195,3,
+0,0,48,136,140,240,195,3,0,0,48,160,130,243,195,3,0,4,30,144,92,36,27,0,
+9,18,32,36,58,158,98,1,57,144,2,0,8,0,0,0,0,0,48,160,144,200,92,0,0,0,48,
+48,140,200,92,0,0,162,32,5,58,0,144,161,144,8,160,57,144,0,32,37,140,4,
+192,137,89,4,96,4,90,0,16,185,140,66,0,0,19,4,208,128,89,0,208,165,144,
+0,208,165,130,23,16,185,89,23,96,4,90,136,13,173,89,144,13,181,89,0,52,
+172,130,254,255,255,255,152,13,165,89,0,52,180,130,255,255,255,255,0,16,
+164,130,16,16,129,89,204,255,255,20,5,22,128,92,0,16,137,140,7,22,144,92,
+152,26,0,9,14,192,129,58,18,98,1,57,4,2,0,8,4,192,145,89,4,160,4,90,0,16,
+185,140,18,0,0,19,0,208,245,146,23,16,185,89,248,159,188,52,6,16,51,89,
+0,144,161,144,104,63,5,61,0,48,72,144,136,86,0,0,8,80,64,89,136,28,170,
+140,2,78,173,89,21,65,74,89,136,93,178,140,0,48,176,146,136,86,0,0,0,116,
+85,140,200,92,0,0,0,48,168,128,136,86,0,0,5,22,128,92,0,48,136,140,240,
+195,3,0,136,141,165,89,4,0,144,140,0,48,160,130,241,195,3,0,144,141,165,
+89,152,141,181,89,0,48,160,130,242,195,3,0,0,48,176,130,243,195,3,0,0,48,
+168,130,240,195,3,0,240,25,0,9,14,32,36,58,106,97,1,57,92,1,0,8,0,48,160,
+144,200,92,0,0,0,48,48,140,200,92,0,0,0,30,32,92,210,32,5,58,6,16,58,89,
+0,244,169,144,252,255,255,255,2,14,162,89,20,65,173,89,0,80,129,140,0,48,
+168,130,240,195,3,0,0,48,136,140,240,195,3,0,4,30,144,92,136,77,165,89,
+144,77,181,89,0,48,160,130,241,195,3,0,152,77,173,89,0,48,176,130,242,195,
+3,0,0,48,168,130,243,195,3,0,120,25,0,9,14,32,36,58,242,96,1,57,228,0,0,
+8,136,13,161,89,0,48,160,130,241,195,3,0,5,22,128,92,144,13,169,89,0,48,
+32,130,240,195,3,0,152,13,161,89,0,48,168,130,242,195,3,0,0,48,136,140,
+240,195,3,0,0,48,160,130,243,195,3,0,4,30,144,92,44,25,0,9,18,32,36,58,
+166,96,1,57,152,0,0,8,0,0,0,0,6,16,51,89,0,144,161,144,0,208,169,144,7,
+16,59,89,148,48,0,90,4,64,37,89,60,255,255,21,0,48,240,130,240,195,3,0,
+0,48,240,130,241,195,3,0,5,22,128,92,0,48,240,130,242,195,3,0,0,48,136,
+140,240,195,3,0,0,48,240,130,243,195,3,0,4,30,144,92,204,24,0,9,30,32,36,
+61,5,22,128,92,0,48,136,140,240,195,3,0,4,30,144,92,180,24,0,9,16,32,36,
+58,46,96,1,57,32,0,0,8,0,0,0,0,5,22,128,92,0,144,138,140,9,22,144,92,148,
+24,0,9,38,64,130,58,14,96,1,57,5,22,128,92,164,21,0,9,2,30,128,92,0,48,
+136,140,144,192,0,0,26,30,144,92,112,24,0,8,5,22,128,92,136,21,0,8,0,0,
+0,0,0,48,240,140,88,196,0,0,30,22,144,92,0,0,240,140,0,48,136,144,140,86,
+0,0,0,48,168,140,101,139,88,93,21,64,180,103,31,175,5,90,23,192,165,91,
+20,128,165,89,31,14,165,88,0,16,133,140,0,48,160,146,140,86,0,0,0,144,4,
+132,0,0,0,0,0,0,0,10,0,0,0,0,0,48,240,140,128,196,0,0,30,22,136,92,0,0,
+240,140,0,48,128,146,140,86,0,0,0,80,4,132,0,0,0,0,0,0,0,10,0,0,0,0,0,0,
+0,0,0,0,0,0,10,32,4,59,28,32,52,59,104,25,0,9,31,216,168,89,0,16,172,146,
+0,48,128,140,255,255,255,255,0,0,0,10,16,57,128,140,252,195,3,0,0,16,164,
+144,0,16,140,146,20,22,128,92,0,0,0,10,0,0,0,0,0,0,0,0,144,48,0,90,0,16,
+44,140,10,0,0,22,12,32,52,59,1,25,128,89,0,0,0,10,16,57,32,144,252,195,
+3,0,18,32,1,61,1,30,128,92,232,18,0,9,32,0,0,8,1,0,160,140,14,0,37,61,0,
+30,128,92,0,0,0,10,16,57,240,146,252,195,3,0,0,16,1,134,0,30,128,92,5,57,
+32,146,252,195,3,0,0,0,0,10,0,0,0,0,0,48,240,140,112,197,0,0,30,22,128,
+92,0,0,240,140,0,48,160,140,16,196,3,0,8,32,245,146,0,16,245,146,4,32,245,
+146,0,48,160,140,0,196,3,0,0,16,245,146,4,32,245,146,8,32,245,146,12,32,
+245,146,0,16,4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,129,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,130,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+130,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,32,96,8,140,0,116,192,178,240,255,255,
+255,0,48,128,176,128,197,0,0,0,48,32,176,144,197,0,0,0,48,64,176,160,197,
+0,0,0,48,128,178,144,86,0,0,0,48,128,176,192,197,0,0,0,48,192,176,176,197,
+0,0,0,48,128,178,208,86,0,0,0,48,136,140,144,86,0,0,0,116,136,146,224,255,
+255,255,0,48,96,176,208,197,0,0,0,48,144,140,176,86,0,0,0,116,144,146,228,
+255,255,255,0,48,152,140,208,86,0,0,0,116,152,146,232,255,255,255,0,48,
+160,140,240,86,0,0,0,116,128,144,232,255,255,255,12,32,245,146,8,32,133,
+146,0,116,144,152,224,255,255,255,0,48,32,178,160,86,0,0,4,30,168,92,0,
+16,149,154,0,48,160,140,0,87,0,0,0,48,64,178,176,86,0,0,0,48,192,178,192,
+86,0,0,0,48,96,178,224,86,0,0,0,16,245,146,4,32,245,146,8,32,245,146,21,
+16,169,89,12,32,245,146,149,240,4,90,16,32,165,140,228,255,255,22,0,116,
+192,176,240,255,255,255,0,0,0,10,0,0,0,0,0,0,0,0,0,16,164,152,30,0,173,
+51,4,32,172,144,21,80,160,89,4,32,164,146,0,80,133,128,12,0,0,8,0,0,0,0,
+192,1,0,8,0,0,0,10,0,0,0,0,0,0,0,0,0,48,128,144,240,86,0,0,0,16,164,152,
+30,0,173,51,4,32,172,144,21,80,160,89,4,32,164,146,0,80,133,128,12,0,0,
+8,0,0,0,0,136,1,0,8,0,0,0,10,0,80,164,152,38,0,173,51,4,96,164,144,16,22,
+168,92,255,0,184,140,20,80,176,89,149,192,173,88,4,96,180,146,0,16,133,
+130,12,0,0,8,248,2,0,9,16,22,168,92,21,22,128,92,0,0,0,10,0,0,0,0,0,0,0,
+0,0,48,136,144,244,86,0,0,0,80,164,152,38,0,173,51,4,96,164,144,16,22,168,
+92,255,0,184,140,20,80,176,89,149,192,173,88,4,96,180,146,0,16,133,130,
+12,0,0,8,176,2,0,9,16,22,168,92,21,22,128,92,0,0,0,10,0,48,240,140,232,
+199,0,0,30,22,136,92,0,0,240,140,16,32,164,136,0,48,168,140,207,255,255,
+255,148,64,165,88,16,32,164,138,0,80,4,132,0,0,0,10,0,0,0,0,0,48,240,140,
+24,200,0,0,30,22,136,92,0,0,240,140,16,32,164,128,14,32,37,48,1,30,128,
+92,0,80,4,132,0,30,128,92,0,80,4,132,0,0,0,10,0,0,0,0,0,48,240,140,72,200,
+0,0,30,22,136,92,0,0,240,140,16,32,164,128,14,32,45,48,1,30,128,92,0,80,
+4,132,0,30,128,92,0,80,4,132,0,0,0,10,0,0,0,0,0,48,240,140,152,200,0,0,
+30,22,144,92,0,0,240,140,144,48,0,90,0,80,180,140,10,0,0,18,14,96,4,61,
+0,30,128,92,0,144,4,132,1,9,172,89,0,144,165,128,21,80,168,89,1,160,181,
+140,0,80,165,130,240,63,5,61,0,144,4,132,0,0,0,0,0,0,0,10,0,0,0,0,0,48,
+240,140,184,200,0,0,30,22,128,92,0,0,240,140,0,16,4,132,0,0,0,0,0,0,0,10,
+0,0,0,0,16,16,180,89,0,144,173,136,16,22,32,92,3,1,152,140,149,192,164,
+88,136,25,152,88,10,192,164,61,128,73,173,88,113,0,152,140,149,192,164,
+88,0,144,173,138,14,32,13,58,1,25,128,89,0,0,0,10,8,32,164,144,30,32,5,
+61,144,10,0,9,22,32,4,62,16,32,161,136,133,9,165,88,168,0,0,8,0,0,0,0,0,
+48,128,152,240,86,0,0,22,0,36,61,16,96,164,128,14,32,61,48,17,22,128,92,
+96,3,0,9,204,20,0,9,4,16,50,89,1,25,56,89,0,16,44,140,16,32,161,128,18,
+32,129,128,1,30,144,92,0,144,177,152,10,32,21,55,23,22,144,92,22,22,136,
+92,80,18,0,9,14,192,129,61,0,80,161,144,216,63,37,58,8,32,81,144,1,25,152,
+89,144,224,4,90,0,144,74,140,10,22,64,92,10,0,0,18,10,0,68,89,0,16,65,146,
+4,32,81,146,14,192,132,58,26,32,4,58,44,0,0,8,16,32,161,136,133,9,165,88,
+16,0,0,8,0,0,0,0,16,32,161,136,132,9,165,88,1,25,128,89,16,32,161,138,0,
+0,0,10,0,0,0,0,4,32,169,144,21,80,160,89,4,32,161,146,0,80,133,128,0,0,
+0,10,0,0,0,0,0,48,240,140,248,201,0,0,30,22,128,92,0,0,240,140,0,16,4,132,
+0,0,0,0,0,0,0,10,0,0,0,0,0,48,32,140,240,86,0,0,0,30,40,92,0,16,129,144,
+10,32,4,58,252,1,0,9,4,32,129,144,10,32,4,58,240,1,0,9,8,32,129,144,10,
+32,4,58,228,1,0,9,12,32,129,144,10,32,4,58,216,1,0,9,16,32,129,144,10,32,
+4,58,204,1,0,9,5,80,41,89,133,240,4,90,20,32,33,140,184,255,255,22,0,0,
+0,10,0,0,0,0,16,96,180,128,16,22,56,92,0,80,36,140,38,160,53,48,4,96,164,
+144,255,0,232,140,20,80,168,89,135,64,183,88,4,96,172,146,0,16,133,130,
+22,22,128,92,0,0,0,10,17,16,188,89,0,208,173,136,2,1,232,140,149,64,167,
+88,136,25,232,88,30,64,167,61,18,160,5,55,129,73,173,88,16,0,0,8,0,0,0,
+0,10,160,37,48,21,211,172,88,31,216,232,89,149,64,167,88,0,208,173,138,
+14,32,21,58,1,25,128,89,0,0,0,10,8,96,164,144,30,32,5,61,17,22,128,92,172,
+8,0,9,18,32,4,62,1,25,128,89,0,0,0,10,0,0,0,0,16,32,161,128,132,0,232,140,
+148,64,167,88,92,32,5,58,16,32,161,128,30,32,21,48,4,32,169,144,21,80,160,
+89,0,0,48,140,4,32,161,146,0,80,61,130,64,0,0,8,0,16,129,176,18,192,164,
+89,50,0,141,51,4,32,161,144,255,0,232,140,135,64,175,88,1,32,181,140,149,
+176,2,90,4,32,177,146,0,16,61,130,174,0,0,21,0,30,48,92,12,0,0,8,0,0,0,
+0,1,30,48,92,0,16,161,176,22,65,45,89,70,96,1,58,18,32,129,128,8,32,137,
+144,5,22,144,92,244,16,0,9,50,64,129,58,16,32,161,136,8,32,113,144,1,25,
+128,89,133,9,165,88,0,144,107,140,14,22,96,92,16,32,161,138,0,16,113,146,
+4,32,113,146,0,0,0,10,0,0,0,0,16,32,161,128,8,32,81,152,132,0,232,140,148,
+64,167,88,148,48,0,90,0,144,74,140,10,22,160,92,10,0,0,21,10,192,162,89,
+20,22,64,92,0,16,161,146,255,0,232,140,134,64,167,88,4,32,81,146,22,32,
+5,58,4,32,169,144,21,80,160,89,4,32,161,146,0,80,61,130,135,64,135,88,0,
+0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,16,22,32,92,68,8,0,11,144,48,0,90,0,16,44,
+140,18,0,0,21,1,25,128,89,0,0,0,10,0,0,0,0,0,48,168,140,240,86,0,0,21,1,
+164,89,130,13,165,89,10,32,21,59,0,16,244,146,4,22,128,92,68,0,0,9,18,32,
+129,128,60,13,0,9,18,32,4,58,1,25,128,89,0,0,0,10,0,0,0,0,16,32,161,128,
+14,32,29,48,8,32,129,144,244,3,0,11,0,80,161,144,14,32,5,61,4,22,128,92,
+228,3,0,11,0,30,128,92,0,0,0,10,16,32,164,128,16,22,40,92,18,32,45,48,1,
+25,128,89,0,0,0,10,0,0,0,0,26,32,13,55,8,32,164,144,122,32,5,58,0,16,164,
+146,4,32,164,146,108,0,0,8,50,32,21,55,0,16,164,176,22,65,37,89,38,32,1,
+58,18,32,132,128,8,96,137,144,4,22,144,92,148,15,0,9,18,0,129,58,1,25,128,
+89,0,0,0,10,0,0,0,0,8,96,161,144,54,32,5,58,16,96,161,128,8,96,81,152,132,
+0,152,140,148,192,164,88,148,48,0,90,0,144,74,140,10,22,160,92,10,0,0,21,
+10,192,162,89,20,22,64,92,0,80,161,146,4,96,81,146,0,30,128,92,0,0,0,10,
+26,32,28,51,16,208,128,89,4,25,144,89,144,128,132,88,12,0,0,8,0,0,0,0,4,
+30,128,92,16,48,2,90,16,0,56,140,10,0,0,17,16,22,56,92,7,17,48,89,4,32,
+68,140,7,208,73,89,0,0,40,140,0,48,160,144,32,196,3,0,54,32,5,61,8,30,128,
+92,212,10,0,9,4,25,144,89,3,32,132,140,144,128,132,88,4,32,132,140,0,48,
+128,146,32,196,3,0,0,48,128,146,36,196,3,0,0,52,244,146,252,255,255,255,
+0,48,128,144,40,196,3,0,0,30,136,92,178,32,4,58,7,60,164,140,255,255,255,
+255,148,128,169,88,0,96,173,140,0,52,188,144,252,255,255,255,18,0,172,58,
+16,64,162,89,148,128,169,88,0,96,173,140,21,0,178,89,116,192,181,49,22,
+0,172,58,0,116,189,146,252,255,255,255,0,52,172,146,252,255,255,255,22,
+16,161,89,34,192,165,51,0,180,189,146,252,255,255,255,0,116,181,146,252,
+255,255,255,0,16,164,144,0,144,165,146,0,16,180,146,0,48,160,144,32,196,
+3,0,182,0,173,52,34,0,172,61,18,96,4,58,0,16,164,144,0,80,164,146,16,0,
+0,8,0,16,164,144,0,48,160,146,40,196,3,0,21,22,128,92,0,0,0,10,0,0,0,0,
+16,22,136,92,0,16,132,144,88,63,4,61,0,48,168,144,36,196,3,0,0,48,144,140,
+0,252,255,255,21,60,162,140,19,4,0,0,148,128,36,88,0,32,33,140,18,64,37,
+52,4,22,128,92,148,10,0,9,16,32,4,58,0,30,160,92,48,0,0,8,0,0,0,0,0,48,
+128,144,36,196,3,0,0,52,36,146,252,255,255,255,0,52,241,146,252,255,255,
+255,176,1,0,11,1,30,160,92,0,48,32,146,36,196,3,0,22,32,5,58,5,80,40,89,
+16,78,161,89,144,25,144,88,148,158,164,62,0,30,128,92,0,0,0,10,0,0,0,0,
+0,0,0,0,0,0,0,0,144,48,0,90,0,16,60,140,14,0,0,21,17,22,128,92,48,254,255,
+8,22,96,28,51,17,208,136,89,4,25,152,89,145,192,44,88,8,0,0,8,4,30,40,92,
+5,48,2,90,16,0,184,140,10,0,0,17,5,22,184,92,0,52,180,144,252,255,255,255,
+0,48,160,144,40,196,3,0,5,16,137,89,0,0,168,140,16,129,53,89,86,32,5,58,
+82,128,165,49,66,128,165,61,0,180,165,144,252,255,255,255,0,52,164,146,
+252,255,255,255,149,48,0,90,0,144,165,144,14,0,0,18,0,80,165,146,12,0,0,
+8,0,48,160,146,40,196,3,0,0,52,180,144,252,255,255,255,20,0,0,8,0,0,0,0,
+20,22,168,92,0,16,165,144,180,63,5,61,23,17,168,89,23,60,164,140,255,255,
+255,255,148,64,165,88,0,32,165,140,58,0,164,61,16,64,36,89,50,128,37,49,
+4,16,161,89,30,128,165,51,0,52,177,146,252,255,255,255,0,52,36,146,252,
+255,255,255,4,22,128,92,144,0,0,11,7,22,128,92,0,0,0,10,0,0,0,0,5,22,128,
+92,68,253,255,9,144,48,0,90,0,16,36,140,18,0,0,21,0,30,128,92,0,0,0,10,
+0,0,0,0,10,64,49,54,5,22,48,92,7,22,128,92,0,16,137,140,6,22,144,92,212,
+2,0,9,7,22,128,92,68,0,0,11,4,22,128,92,0,0,0,10,144,64,36,112,0,16,129,
+140,248,252,255,9,144,48,0,90,0,16,44,140,14,0,0,21,0,30,128,92,0,0,0,10,
+4,22,136,92,204,238,255,9,5,22,128,92,0,0,0,10,0,48,240,140,8,209,0,0,30,
+22,136,92,0,0,240,140,10,32,4,61,0,80,4,132,0,48,168,144,40,196,3,0,0,30,
+176,92,22,96,5,58,18,64,133,52,21,22,176,92,0,80,173,144,244,127,5,61,0,
+16,172,146,14,160,5,58,0,144,133,146,12,0,0,8,0,48,128,146,40,196,3,0,42,
+96,5,58,0,52,164,144,252,255,255,255,30,64,165,61,0,116,165,144,252,255,
+255,255,0,52,164,146,252,255,255,255,0,80,165,144,0,16,164,146,42,160,5,
+58,0,180,165,144,252,255,255,255,30,0,164,61,0,52,164,144,252,255,255,255,
+0,180,165,146,252,255,255,255,0,16,164,144,0,144,165,146,0,80,4,132,0,0,
+0,0,0,0,0,10,0,0,0,0,16,96,4,90,0,16,36,140,17,22,184,92,1,137,164,89,17,
+0,181,89,16,0,173,89,10,0,0,20,28,128,133,54,10,64,172,52,20,128,173,54,
+120,0,0,9,4,22,128,92,0,0,0,10,0,0,0,0,50,64,132,51,1,137,148,89,1,25,152,
+89,78,192,148,58,0,208,165,128,23,80,184,89,1,137,148,89,146,224,4,90,0,
+16,161,130,4,80,32,89,232,255,255,21,44,0,0,8,1,137,148,89,1,25,152,89,
+34,192,148,58,0,144,165,128,1,137,181,89,1,137,148,89,146,224,4,90,0,80,
+165,130,1,73,173,89,232,255,255,21,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,144,
+67,164,88,148,208,163,88,255,0,32,140,148,0,161,88,148,48,0,90,0,16,156,
+140,17,22,184,92,0,144,180,140,16,22,232,92,70,0,0,21,4,140,156,89,34,224,
+4,59,0,80,164,176,1,201,156,89,16,96,140,140,147,48,0,90,0,80,167,178,29,
+16,236,89,232,255,255,17,146,208,179,88,150,48,0,90,0,80,159,140,17,22,
+184,92,14,0,0,21,0,0,0,10,0,0,0,0,147,195,165,88,148,208,161,88,148,0,161,
+88,46,32,5,61,34,160,61,51,0,208,165,152,8,137,181,89,22,240,1,90,0,208,
+164,154,19,16,154,89,8,224,189,140,232,255,255,17,10,160,5,61,0,0,0,10,
+147,195,165,88,148,208,160,88,148,0,161,88,46,32,5,61,34,160,29,51,0,208,
+165,144,4,137,181,89,22,240,0,90,0,208,164,146,19,16,153,89,4,224,189,140,
+232,255,255,17,10,160,5,61,0,0,0,10,147,195,165,88,46,32,5,55,34,160,13,
+51,0,208,165,136,2,137,181,89,22,112,0,90,0,208,164,138,19,144,152,89,2,
+224,189,140,232,255,255,17,10,160,5,61,0,0,0,10,1,137,181,89,1,25,32,89,
+34,0,177,58,0,208,165,128,23,80,184,89,1,137,181,89,150,32,1,90,0,208,164,
+130,19,80,152,89,232,255,255,21,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,17,32,
+4,90,0,16,236,140,17,22,152,92,0,16,188,140,1,137,164,89,16,0,181,89,17,
+0,173,89,10,0,0,20,24,128,141,54,10,0,172,52,16,128,173,54,17,22,128,92,
+0,80,143,140,140,254,255,8,50,0,140,51,1,137,148,89,1,25,32,89,34,0,145,
+58,0,208,165,128,23,80,184,89,1,137,148,89,146,32,1,90,0,208,164,130,19,
+80,152,89,232,255,255,21,0,0,0,10,1,137,148,89,1,25,32,89,34,0,145,58,0,
+144,165,128,1,137,181,89,1,137,148,89,146,32,1,90,0,80,165,130,1,73,173,
+89,232,255,255,21,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,16,32,164,128,16,22,
+64,92,34,32,21,48,16,208,164,89,0,16,181,140,16,16,130,89,1,0,184,140,0,
+16,164,146,4,32,188,146,80,0,0,8,138,25,128,88,116,249,255,9,8,32,130,146,
+14,32,4,61,1,25,128,89,0,0,0,10,16,32,162,136,131,9,165,88,16,32,162,138,
+16,32,162,128,138,25,168,88,12,32,170,146,30,32,5,55,18,32,130,128,96,6,
+0,9,18,32,4,58,16,32,162,136,135,9,165,88,16,32,162,138,16,32,162,128,0,
+16,34,176,46,32,13,48,132,0,168,140,148,64,165,88,148,48,0,90,0,144,41,
+140,6,22,160,92,10,0,0,21,6,192,161,89,20,22,32,92,16,0,0,8,0,0,0,0,6,22,
+40,92,0,144,33,140,0,16,34,146,7,22,128,92,4,32,50,146,0,0,0,10,0,48,240,
+140,160,212,0,0,30,22,136,92,0,0,240,140,0,48,160,144,240,86,0,0,20,30,
+176,92,0,48,168,140,240,86,0,0,26,0,164,58,1,137,181,89,30,160,5,57,21,
+16,169,89,0,80,165,144,240,31,164,61,21,22,128,92,0,80,4,132,0,0,0,0,0,
+30,128,92,0,80,4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,16,72,8,89,30,22,
+32,92,0,48,128,140,48,196,3,0,138,25,136,88,220,2,0,9,16,22,160,92,148,
+48,0,90,36,0,0,19,0,30,160,92,64,224,167,146,0,48,160,140,64,87,0,0,68,
+224,167,146,0,30,160,92,72,224,167,146,20,0,0,8,64,224,135,140,0,48,136,
+140,48,196,3,0,32,0,0,9,72,224,167,144,0,48,160,146,0,201,3,0,64,224,135,
+176,0,0,0,10,0,0,0,10,0,0,0,0,32,96,8,140,30,22,32,92,64,224,135,146,68,
+224,143,146,68,224,135,144,28,2,0,9,16,22,160,92,68,224,175,144,20,64,165,
+89,1,32,173,140,76,224,175,146,64,224,167,144,0,30,168,92,0,16,173,146,
+64,224,167,144,0,48,168,140,48,200,3,0,4,32,173,146,68,224,167,144,72,224,
+167,146,72,224,167,144,0,16,173,128,149,48,0,90,52,1,0,18,64,224,167,144,
+0,16,173,144,149,176,4,90,36,1,0,17,72,224,167,144,0,16,173,128,149,48,
+0,90,92,0,0,18,72,224,167,144,0,16,173,128,31,88,184,89,149,224,5,90,56,
+0,0,18,72,224,167,144,0,16,173,128,149,112,2,90,40,0,0,18,72,224,167,144,
+0,16,173,128,149,112,3,90,24,0,0,18,72,224,167,144,0,16,173,128,149,176,
+2,90,8,0,0,18,20,0,0,8,72,224,167,144,20,80,168,89,72,224,175,146,156,255,
+255,8,72,224,167,144,0,16,173,128,149,48,0,90,168,0,0,18,64,224,175,144,
+0,80,165,144,20,80,176,89,0,80,181,146,72,224,175,144,20,57,168,146,48,
+200,3,0,72,224,167,144,0,16,173,128,149,48,0,90,88,0,0,18,72,224,167,144,
+0,16,173,128,31,88,184,89,149,224,5,90,68,0,0,18,72,224,167,144,0,16,173,
+128,149,112,2,90,52,0,0,18,72,224,167,144,0,16,173,128,149,112,3,90,36,
+0,0,18,72,224,167,144,0,16,173,128,149,176,2,90,20,0,0,18,72,224,167,144,
+20,80,168,89,72,224,175,146,160,255,255,8,72,224,167,144,0,16,173,128,149,
+48,0,90,24,0,0,18,72,224,167,144,20,80,168,89,72,224,175,146,0,30,168,92,
+0,16,173,130,196,254,255,8,64,224,167,144,0,16,173,144,0,30,160,92,21,57,
+160,146,48,200,3,0,0,30,160,92,80,224,167,146,76,224,167,144,0,16,173,128,
+149,48,0,90,72,0,0,18,80,224,167,144,148,48,7,90,60,0,0,17,80,224,167,144,
+20,80,168,89,80,224,175,146,76,224,175,144,20,57,168,146,128,200,3,0,76,
+224,135,144,76,0,0,9,16,22,160,92,1,32,173,140,76,224,167,144,21,0,173,
+89,76,224,175,146,176,255,255,8,80,224,167,144,0,30,168,92,20,57,168,146,
+128,200,3,0,64,224,167,144,0,48,168,140,128,200,3,0,8,32,173,146,0,0,0,
+10,0,0,0,0,0,0,0,0,0,0,0,0,16,72,8,89,30,22,160,92,64,224,135,146,0,30,
+168,92,68,224,175,146,64,224,175,144,21,80,176,89,64,224,183,146,0,80,173,
+128,149,48,0,90,20,0,0,18,68,224,175,144,21,80,176,89,68,224,183,146,220,
+255,255,8,68,224,135,144,0,0,0,10,0,0,0,10,0,0,0,0,0,0,0,0,16,72,8,89,30,
+22,32,92,16,22,40,92,17,22,48,92,242,0,56,140,64,224,63,146,5,22,128,92,
+6,22,136,92,7,48,0,102,16,22,72,92,68,224,79,146,68,224,135,144,0,0,0,10,
+0,0,0,10,0,0,0,10,0,0,0,0,16,72,8,89,30,22,32,92,64,224,135,146,64,224,
+135,144,16,0,0,9,0,0,0,10,0,0,0,0,0,0,0,0,16,72,8,89,30,22,32,92,16,22,
+40,92,1,1,48,140,64,224,55,146,5,22,128,92,6,48,0,102,0,0,0,10,0,0,0,10,
+0,0,0,0,0,0,0,0,0,0,0,0,30,22,160,92,0,48,168,140,112,202,3,0,0,48,168,
+146,16,201,3,0,0,0,0,10,0,0,0,0,0,0,0,0,16,72,8,89,30,22,32,92,64,224,135,
+146,64,224,167,144,148,48,0,90,16,0,0,21,0,48,128,144,16,201,3,0,0,0,0,
+10,0,48,160,144,16,201,3,0,20,208,43,89,16,25,176,89,133,128,45,88,5,22,
+160,92,0,32,45,140,64,224,175,144,1,73,165,89,5,0,53,89,0,48,160,144,16,
+201,3,0,5,32,5,90,32,0,0,20,6,96,1,90,24,0,0,20,0,48,176,140,111,218,3,
+0,6,160,5,90,8,0,0,17,36,0,0,8,56,5,0,9,16,22,160,92,20,22,168,92,12,30,
+160,92,0,80,165,146,0,48,128,140,255,255,255,255,0,0,0,10,6,80,160,89,0,
+48,160,146,16,201,3,0,0,48,160,144,16,201,3,0,20,160,1,90,20,0,0,19,0,48,
+160,140,111,218,3,0,0,48,160,146,16,201,3,0,5,22,128,92,0,0,0,10,0,0,0,
+10,16,72,8,89,30,22,32,92,64,224,135,146,64,224,167,144,0,48,176,140,112,
+202,3,0,20,160,5,90,32,0,0,20,64,224,175,144,1,73,165,89,0,48,176,140,111,
+218,3,0,20,160,5,90,8,0,0,17,32,0,0,8,164,4,0,9,16,22,160,92,20,22,168,
+92,12,30,160,92,0,80,165,146,1,25,128,89,0,0,0,10,64,224,167,144,0,48,160,
+146,16,201,3,0,0,30,128,92,0,0,0,10,0,0,0,10,16,72,8,89,30,22,32,92,64,
+224,135,146,64,224,135,144,64,0,0,9,68,224,135,146,68,224,167,144,148,48,
+0,90,32,0,0,18,76,4,0,9,16,22,160,92,20,22,168,92,68,224,167,144,0,80,165,
+146,1,25,128,89,0,0,0,10,0,30,128,92,0,0,0,10,0,0,0,10,0,0,0,0,16,72,8,
+89,30,22,32,92,16,22,40,92,234,0,48,140,64,224,55,146,5,22,128,92,6,48,
+0,102,16,22,64,92,68,224,71,146,68,224,135,144,0,0,0,10,0,0,0,10,0,0,0,
+10,0,0,0,0,0,0,0,0,0,0,0,0,16,72,8,89,30,22,32,92,64,224,135,146,68,224,
+143,146,64,224,135,144,1,3,136,140,68,224,151,144,180,0,0,9,0,0,0,10,0,
+0,0,10,0,0,0,0,0,0,0,0,16,72,8,89,30,22,32,92,64,224,135,146,68,224,167,
+140,64,224,135,144,20,22,136,92,72,0,0,9,72,224,135,146,72,224,167,144,
+148,48,0,90,32,0,0,18,132,3,0,9,16,22,160,92,20,22,168,92,72,224,167,144,
+0,80,165,146,1,25,128,89,0,0,0,10,68,224,135,144,0,0,0,10,0,0,0,10,0,0,
+0,0,0,0,0,0,0,0,0,0,16,72,8,89,30,22,32,92,16,22,40,92,17,22,48,92,235,
+0,56,140,64,224,63,146,5,22,128,92,6,22,136,92,7,48,0,102,16,22,72,92,68,
+224,79,146,68,224,135,144,0,0,0,10,0,0,0,10,0,0,0,10,0,0,0,0,32,96,8,140,
+30,22,32,92,64,224,135,146,68,224,143,146,72,224,151,146,76,224,167,140,
+64,224,135,144,68,224,143,144,72,224,151,144,20,22,152,92,72,0,0,9,80,224,
+135,146,80,224,167,144,148,48,0,90,32,0,0,18,212,2,0,9,16,22,160,92,20,
+22,168,92,80,224,167,144,0,80,165,146,1,25,128,89,0,0,0,10,76,224,135,144,
+0,0,0,10,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,16,72,8,89,30,22,32,92,16,22,
+40,92,17,22,48,92,18,22,56,92,19,22,64,92,230,0,72,140,64,224,79,146,5,
+22,128,92,6,22,136,92,7,22,144,92,8,22,152,92,9,48,0,102,16,22,88,92,68,
+224,95,146,68,224,135,144,0,0,0,10,0,0,0,10,0,0,0,10,0,0,0,0,32,96,8,140,
+30,22,32,92,64,224,135,146,68,224,143,146,72,224,151,146,76,224,167,140,
+64,224,135,144,68,224,143,144,72,224,151,144,20,22,152,92,72,0,0,9,80,224,
+135,146,80,224,167,144,148,48,0,90,32,0,0,18,20,2,0,9,16,22,160,92,20,22,
+168,92,80,224,167,144,0,80,165,146,1,25,128,89,0,0,0,10,76,224,135,144,
+0,0,0,10,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,16,72,8,89,30,22,32,92,16,22,
+40,92,17,22,48,92,18,22,56,92,19,22,64,92,231,0,72,140,64,224,79,146,5,
+22,128,92,6,22,136,92,7,22,144,92,8,22,152,92,9,48,0,102,16,22,88,92,68,
+224,95,146,68,224,135,144,0,0,0,10,0,0,0,10,0,0,0,10,0,0,0,0,32,96,8,140,
+30,22,32,92,64,224,135,146,68,224,143,146,72,224,151,146,76,224,167,140,
+64,224,135,144,68,224,143,144,72,224,151,144,20,22,152,92,72,0,0,9,80,224,
+135,146,80,224,167,144,148,48,0,90,32,0,0,18,84,1,0,9,16,22,160,92,20,22,
+168,92,80,224,167,144,0,80,165,146,1,25,128,89,0,0,0,10,76,224,135,144,
+0,0,0,10,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,16,72,8,89,30,22,32,92,16,22,
+40,92,17,22,48,92,18,22,56,92,19,22,64,92,232,0,72,140,64,224,79,146,5,
+22,128,92,6,22,136,92,7,22,144,92,8,22,152,92,9,48,0,102,16,22,88,92,68,
+224,95,146,68,224,135,144,0,0,0,10,0,0,0,10,0,0,0,10,0,0,0,0,30,22,32,92,
+252,250,255,9,0,0,0,10,0,0,0,0,16,72,8,89,30,22,160,92,64,224,135,146,0,
+48,128,140,32,201,3,0,0,0,0,10,0,0,0,10,0,0,0,0,16,72,8,89,30,22,160,92,
+64,224,135,146,0,48,128,140,176,201,3,0,0,0,0,10,0,0,0,10,0,0,0,0,16,72,
+8,89,30,22,160,92,64,224,135,146,0,48,128,140,16,202,3,0,0,0,0,10,0,0,0,
+10,0,0,0,0,30,22,160,92,0,48,128,140,32,201,3,0,0,0,0,10,0,0,0,10,0,0,0,
+0,0,0,0,0,0,0,0,0,30,22,160,92,0,48,128,140,176,201,3,0,0,0,0,10,0,0,0,
+10,0,0,0,0,0,0,0,0,0,0,0,0,30,22,160,92,0,48,128,140,16,202,3,0,0,0,0,10,
+0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,30,22,160,92,0,48,128,140,16,202,3,0,0,
+0,0,10,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,30,22,32,92,16,22,40,92,4,96,161,
+144,0,80,169,144,20,96,5,90,24,0,0,19,4,96,161,144,20,80,168,89,4,96,169,
+146,0,16,133,128,12,0,0,8,5,22,128,92,112,234,255,9,0,0,0,10,0,0,0,10,0,
+0,0,10,30,22,32,92,16,22,40,92,17,22,48,92,4,160,161,144,0,144,169,144,
+20,96,5,90,36,0,0,19,4,160,161,144,20,80,168,89,4,160,169,146,5,22,168,
+92,0,16,173,130,255,0,176,140,149,128,133,88,16,0,0,8,5,22,128,92,6,22,
+136,92,188,235,255,9,0,0,0,10,0,0,0,10,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,
+30,22,32,92,108,254,255,9,0,0,0,10,0,0,0,0,30,22,160,92,0,0,0,10,0,0,0,
+0,0,0,0,0,30,22,160,92,1,25,136,89,145,2,132,100,0,0,0,10,144,48,0,90,19,
+4,32,140,86,0,0,20,16,22,40,92,133,16,96,100,66,0,0,16,12,17,101,89,140,
+48,0,90,18,0,0,20,12,70,137,89,0,30,128,92,20,0,0,8,12,17,104,89,13,68,
+137,89,32,32,107,140,13,70,129,89,20,78,140,88,12,1,33,89,20,14,33,89,132,
+67,140,88,0,0,0,10,0,30,128,93,248,255,255,8,16,17,40,89,133,16,96,100,
+12,17,101,89,140,48,0,90,18,0,0,20,12,70,137,89,0,30,128,92,20,0,0,8,12,
+17,104,89,13,68,137,89,32,32,107,140,13,70,129,89,20,78,140,88,12,1,33,
+89,20,14,33,89,132,67,140,88,159,73,140,88,176,255,255,8,1,78,36,89,21,
+12,33,89,19,4,104,140,4,65,107,89,32,96,99,140,140,48,0,90,46,0,0,22,145,
+48,0,90,50,0,0,20,141,48,5,90,34,0,0,17,12,4,132,89,12,6,132,89,141,48,
+0,90,14,0,0,22,13,68,140,89,13,70,140,89,0,0,0,10,0,30,128,93,248,255,255,
+8,141,48,5,90,58,0,0,17,0,56,0,90,16,17,128,91,17,17,136,91,12,4,132,89,
+12,6,132,89,141,48,0,90,14,0,0,22,13,68,140,89,13,70,140,89,0,56,0,90,16,
+17,128,91,17,17,136,91,188,255,255,8,0,48,136,140,0,0,240,191,0,30,128,
+92,172,255,255,8,1,78,36,89,0,48,96,140,0,0,224,255,4,32,3,90,106,0,0,17,
+21,12,33,89,10,78,44,89,158,73,41,88,31,78,41,88,22,12,100,89,133,3,43,
+88,145,48,0,90,29,4,96,140,38,0,0,20,4,32,3,90,4,1,35,89,14,0,0,17,4,68,
+129,89,0,0,0,10,1,25,128,89,31,14,132,88,244,255,255,8,4,32,3,90,4,1,35,
+89,18,0,0,17,5,17,40,89,132,69,129,89,220,255,255,8,31,94,128,89,212,255,
+255,8,0,30,128,93,204,255,255,8,1,78,36,89,0,48,96,140,0,0,224,255,4,32,
+3,90,106,0,0,17,21,12,33,89,10,78,44,89,158,73,41,88,31,78,41,88,22,12,
+100,89,133,3,43,88,145,48,0,90,29,4,96,140,38,0,0,20,4,32,3,90,4,1,35,89,
+14,0,0,17,4,68,129,89,0,0,0,10,1,25,128,89,31,14,132,88,244,255,255,8,4,
+32,3,90,4,1,35,89,18,0,0,17,4,68,41,89,5,17,128,89,220,255,255,8,31,94,
+128,89,212,255,255,8,0,30,128,92,204,255,255,8,1,78,36,89,0,48,96,140,0,
+0,224,255,4,32,3,90,66,0,0,17,21,12,33,89,11,78,44,89,159,73,41,88,21,12,
+100,89,133,3,43,88,145,48,0,90,38,0,0,20,30,4,96,140,4,32,3,90,18,0,0,17,
+4,1,35,89,4,68,129,89,0,0,0,10,1,25,128,89,248,255,255,8,0,30,128,92,240,
+255,255,8,1,78,36,89,0,48,96,140,255,255,223,255,12,32,1,90,4,49,0,90,82,
+0,0,22,21,12,33,89,128,3,96,140,12,1,33,89,255,0,96,140,4,32,3,90,46,0,
+0,19,23,14,33,89,12,78,44,89,9,76,41,89,132,67,33,88,29,12,44,89,28,47,
+4,90,5,0,129,91,31,111,4,90,159,15,132,88,0,0,0,10,132,48,0,90,48,0,0,20,
+24,0,0,8,42,0,0,18,0,48,96,140,0,0,224,255,4,32,3,90,18,0,0,17,0,48,128,
+140,0,0,128,127,204,255,255,8,1,25,128,89,204,255,255,8,0,30,128,92,188,
+255,255,8,31,12,84,89,31,142,82,89,1,14,36,89,0,48,96,140,255,255,255,254,
+12,32,1,90,4,49,0,90,58,0,0,22,24,12,33,89,0,48,96,140,128,252,255,255,
+12,1,33,89,20,14,33,89,9,14,44,89,12,76,41,89,132,67,137,88,29,14,132,89,
+145,131,138,88,0,0,0,10,0,30,128,93,244,255,255,8,250,255,255,18,0,48,96,
+140,0,0,0,255,12,32,1,90,22,0,0,20,0,48,136,140,0,0,240,127,0,30,128,92,
+208,255,255,8,1,25,128,89,1,25,136,89,200,255,255,8,15,140,84,89,31,142,
+82,89,17,142,36,89,17,12,33,89,31,111,4,90,90,0,0,16,0,48,96,140,0,60,0,
+0,12,1,33,89,255,7,96,140,4,32,3,90,46,0,0,19,31,78,140,88,11,76,60,89,
+21,78,52,89,20,14,33,89,135,3,137,88,10,47,4,90,11,12,132,89,6,0,132,91,
+138,67,140,88,0,0,0,10,34,0,0,17,0,48,136,140,0,0,240,127,0,30,128,92,232,
+255,255,8,144,67,140,88,17,48,0,90,220,255,255,18,1,25,128,89,1,25,136,
+89,212,255,255,8,31,72,84,88,8,0,0,8,31,78,84,88,1,78,36,89,0,48,96,140,
+0,0,224,255,4,32,3,90,10,0,0,17,10,22,136,92,0,0,0,10,16,22,24,92,0,30,
+48,93,0,30,64,93,0,30,80,93,47,0,144,140,57,0,152,140,3,206,57,88,0,16,
+36,128,1,8,132,89,19,32,1,90,4,161,4,90,134,0,0,22,143,8,33,88,4,239,1,
+90,82,0,0,18,8,22,112,93,2,78,74,89,30,12,106,89,2,14,66,89,137,67,75,88,
+0,120,0,90,14,0,66,91,15,64,74,91,8,0,66,91,9,64,74,91,4,0,66,91,0,72,74,
+91,9,193,123,88,143,48,0,90,254,1,0,20,134,201,57,88,2,239,1,90,0,136,49,
+91,148,255,255,8,10,22,88,92,2,142,82,89,11,128,82,89,1,142,82,89,4,128,
+82,89,10,193,90,88,139,48,0,90,214,1,0,20,135,201,57,88,108,255,255,8,46,
+0,88,140,4,224,2,90,234,1,0,18,45,0,88,140,4,224,2,90,238,1,0,18,43,0,88,
+140,4,224,2,90,6,2,0,18,69,0,88,140,5,14,97,88,12,224,2,90,14,2,0,18,68,
+0,88,140,12,224,2,90,2,2,0,18,3,1,28,89,1,201,24,89,0,80,28,146,6,239,1,
+90,102,1,0,16,144,0,96,140,135,0,99,88,16,40,3,90,86,1,0,18,5,239,1,90,
+8,0,0,16,10,17,80,89,6,129,82,89,54,1,96,140,12,17,104,89,1,73,107,89,140,
+160,2,90,138,97,3,90,58,1,0,22,255,3,32,140,137,16,112,100,32,0,0,18,136,
+16,112,100,54,1,0,16,14,209,119,89,14,6,74,89,0,30,64,92,32,160,115,140,
+28,0,0,8,1,136,123,89,14,209,119,89,15,4,122,89,14,6,66,89,14,70,74,89,
+143,67,74,88,14,1,33,89,0,48,40,140,0,88,0,0,138,48,0,90,18,0,0,19,0,48,
+40,140,176,89,0,0,10,17,80,89,143,136,90,88,139,93,97,152,13,64,114,103,
+12,64,146,103,0,120,0,90,14,192,116,91,15,16,120,91,13,0,146,103,14,192,
+116,91,15,16,120,91,11,125,89,144,128,0,0,0,132,192,34,89,4,140,90,89,139,
+125,65,152,192,0,0,0,15,64,98,103,14,64,146,103,0,120,0,90,12,192,100,91,
+13,16,104,91,15,0,146,103,12,192,100,91,13,16,104,91,11,125,89,144,96,1,
+0,0,132,192,34,89,63,32,33,140,31,111,3,90,20,0,0,18,12,0,99,91,13,64,107,
+91,1,9,33,89,236,255,255,8,132,48,0,90,90,0,0,20,12,22,88,92,31,78,107,
+88,11,12,131,89,21,78,115,89,11,76,139,89,142,3,132,88,20,14,33,89,145,
+3,137,88,10,239,2,90,0,8,132,91,0,72,140,91,1,239,1,90,159,79,140,88,0,
+0,0,10,1,25,128,89,1,25,136,89,244,255,255,8,16,0,0,18,28,0,0,8,5,239,1,
+90,22,0,0,16,0,30,128,93,212,255,255,8,138,48,0,90,244,255,255,20,0,48,
+136,140,0,0,240,127,0,30,128,92,188,255,255,8,2,239,1,90,194,255,255,18,
+130,201,57,88,108,253,255,8,1,9,100,89,12,224,0,90,14,0,0,21,129,201,57,
+88,88,253,255,8,3,239,1,90,158,255,255,16,133,201,57,88,72,253,255,8,1,
+9,100,89,12,224,0,90,62,253,255,18,3,239,1,90,54,253,255,18,124,255,255,
+8,4,239,1,90,118,255,255,18,132,201,57,88,131,201,57,88,32,253,255,8,0,
+0,0,0,0,48,96,140,0,0,224,255,1,78,36,89,12,32,1,90,202,4,0,22,21,12,33,
+89,32,0,96,140,145,48,0,90,8,0,0,19,45,0,96,140,0,144,100,130,1,136,148,
+89,51,4,96,140,12,1,49,89,16,22,32,92,11,78,44,89,11,76,41,89,148,73,41,
+88,1,78,100,89,144,3,99,88,12,48,0,90,12,0,0,21,0,30,32,93,0,30,48,92,0,
+48,96,140,151,117,0,0,140,128,57,116,0,48,96,140,160,134,1,0,1,12,107,89,
+159,205,113,89,14,67,107,88,7,64,59,89,140,197,57,116,7,22,24,92,134,48,
+0,90,10,2,0,19,9,30,96,92,140,192,105,89,141,48,0,90,8,0,0,22,135,17,96,
+89,140,192,57,89,12,57,104,144,160,92,0,0,5,64,83,103,4,64,35,103,0,120,
+0,90,10,64,41,91,0,200,90,91,139,16,96,100,54,0,0,16,12,128,49,89,1,136,
+49,89,12,4,33,89,0,47,1,90,1,12,33,89,12,68,81,89,1,140,82,89,12,209,103,
+89,12,70,41,89,4,64,33,91,12,198,90,89,10,192,42,91,135,48,0,90,146,255,
+255,20,6,48,0,90,38,0,0,18,32,160,97,140,6,17,48,89,1,137,121,89,15,39,
+1,90,6,4,33,89,12,70,97,89,6,68,41,89,12,0,33,91,19,30,120,92,143,57,96,
+152,0,92,0,0,1,201,123,89,5,96,3,90,240,255,255,20,22,0,0,17,12,112,0,90,
+14,0,0,18,4,32,3,90,220,255,255,20,2,200,123,89,15,192,112,89,142,48,0,
+90,8,0,0,17,1,30,112,92,1,136,107,89,20,64,107,89,20,48,0,90,0,0,96,33,
+12,64,107,89,13,224,4,90,34,2,0,17,19,176,4,90,8,0,0,22,18,30,152,92,14,
+17,100,89,20,32,3,90,8,0,0,22,12,22,160,92,0,30,64,92,14,224,3,90,8,0,0,
+22,15,129,67,89,15,192,96,89,140,48,0,90,8,0,0,17,3,81,120,89,159,205,96,
+89,3,3,107,88,12,65,107,89,140,64,107,88,13,32,5,90,132,0,0,22,20,65,107,
+89,13,240,4,90,16,0,0,22,0,30,32,93,1,30,120,92,108,0,0,8,141,57,80,152,
+0,92,0,0,1,140,82,89,31,206,130,89,1,204,90,89,144,131,82,88,0,120,0,90,
+10,0,33,91,11,64,41,91,13,176,2,90,32,0,0,20,0,48,80,140,0,202,154,59,138,
+0,33,103,5,22,32,92,0,30,40,92,9,73,107,89,9,201,123,89,13,57,80,144,160,
+92,0,0,10,68,137,112,138,69,41,112,4,22,128,92,138,0,132,103,17,22,32,92,
+13,193,123,89,4,22,128,93,184,2,0,11,136,48,0,90,24,0,0,22,48,0,96,140,
+0,144,100,130,1,136,148,89,1,9,66,89,232,255,255,8,0,30,104,92,0,144,108,
+130,1,136,148,89,0,30,240,92,0,0,0,10,0,48,96,140,16,91,0,0,0,30,104,92,
+0,16,131,144,16,17,128,89,16,160,1,90,20,0,0,17,4,8,99,89,1,72,107,89,13,
+48,4,90,228,255,255,20,4,9,99,89,1,73,107,89,0,16,131,144,16,48,0,90,70,
+0,0,18,16,128,49,89,4,78,99,89,12,193,57,89,141,57,96,152,112,90,0,0,13,
+64,81,103,12,64,129,103,0,120,0,90,10,64,84,91,11,16,88,91,13,0,129,103,
+10,64,36,91,11,16,40,91,53,0,96,140,6,32,3,90,134,255,255,19,0,48,96,140,
+48,90,0,0,0,30,104,92,0,16,131,144,16,17,128,89,16,160,1,90,20,0,0,17,4,
+8,99,89,1,72,107,89,13,48,4,90,228,255,255,20,4,9,99,89,1,73,107,89,0,16,
+131,144,16,48,0,90,54,0,0,18,16,128,49,89,13,193,57,89,141,57,96,152,176,
+89,0,0,13,64,81,103,12,64,129,103,0,120,0,90,10,64,84,91,11,16,88,91,13,
+0,129,103,10,64,36,91,11,16,40,91,6,70,41,89,32,0,96,140,6,1,99,89,12,4,
+97,89,6,6,33,89,140,67,41,88,7,193,24,89,140,253,255,8,19,176,1,90,22,1,
+0,20,15,192,24,89,1,201,24,89,0,30,104,92,19,240,1,90,22,0,0,22,7,201,108,
+89,141,240,3,90,8,0,0,22,15,30,104,92,1,72,107,89,15,96,3,90,148,0,0,22,
+13,193,115,89,142,57,80,152,0,92,0,0,1,140,82,89,31,206,130,89,1,204,90,
+89,144,131,82,88,0,120,0,90,10,0,33,91,11,64,41,91,14,176,2,90,32,0,0,20,
+0,48,80,140,0,202,154,59,138,0,33,103,5,22,32,92,0,30,40,92,9,137,115,89,
+9,201,123,89,14,57,80,144,160,92,0,0,10,68,137,112,138,69,41,112,4,22,128,
+92,138,0,132,103,17,22,32,92,14,193,123,89,143,57,96,152,0,92,0,0,5,96,
+3,90,24,0,0,20,14,0,0,17,4,32,3,90,12,0,0,20,1,200,123,89,1,200,24,89,4,
+22,128,93,1,30,112,92,164,0,0,11,69,0,96,140,0,144,100,130,1,136,148,89,
+43,0,96,140,131,48,0,90,12,0,0,19,3,17,24,89,45,0,96,140,0,144,100,130,
+1,136,148,89,3,22,128,92,0,30,136,92,3,30,120,92,15,22,112,92,15,22,104,
+92,100,0,0,11,200,253,255,8,42,0,96,140,0,144,100,130,1,136,148,89,2,203,
+156,90,240,255,255,20,176,253,255,8,0,48,96,140,78,97,78,0,36,0,0,21,43,
+0,96,140,145,48,0,90,8,0,0,19,45,0,96,140,0,144,100,130,1,136,148,89,0,
+48,96,140,73,78,70,0,0,144,100,130,1,136,148,89,12,48,0,90,8,12,99,89,240,
+255,255,21,120,253,255,8,143,57,96,140,248,91,0,0,14,48,0,90,16,0,0,21,
+46,0,120,140,0,144,124,130,1,136,148,89,0,16,83,152,8,9,99,89,47,0,120,
+140,1,200,123,89,0,56,0,90,10,1,132,91,11,65,140,91,240,255,255,18,10,0,
+132,91,11,64,140,91,0,144,124,130,1,136,148,89,1,137,115,89,144,67,124,
+88,15,48,0,90,12,0,0,21,142,48,0,90,14,0,0,20,10,112,0,90,160,255,255,17,
+0,144,7,132,0,0,0,0,31,200,156,88,1,78,36,89,1,206,44,89,21,94,96,89,4,
+0,107,89,13,32,3,90,154,1,0,22,5,0,107,89,13,32,3,90,142,1,0,22,11,78,60,
+89,21,12,100,89,11,14,52,89,140,195,57,88,159,201,57,88,11,206,76,89,21,
+140,100,89,11,142,68,89,140,67,74,88,159,73,74,88,21,12,33,89,21,76,41,
+89,31,76,84,89,31,142,82,89,4,96,1,90,32,0,104,140,52,0,0,20,5,1,97,89,
+12,96,3,90,16,0,0,20,9,22,64,92,0,30,72,92,13,1,99,89,12,4,66,89,12,65,
+107,89,13,70,106,89,12,68,74,89,141,3,66,88,52,0,0,8,4,65,97,89,12,96,3,
+90,16,0,0,20,7,22,48,92,0,30,56,92,13,1,99,89,12,132,49,89,12,65,107,89,
+13,198,105,89,12,196,57,89,141,131,49,88,5,22,32,92,17,195,100,88,31,47,
+3,90,62,0,0,18,8,128,49,91,9,192,57,91,150,0,0,16,1,140,49,89,31,206,97,
+89,1,204,57,89,140,131,49,88,1,8,33,89,255,7,96,140,4,32,3,90,116,0,0,20,
+12,22,32,92,0,30,48,93,104,0,0,8,8,129,49,91,9,193,57,91,22,0,0,18,0,56,
+0,90,6,17,48,91,7,17,56,91,31,136,82,88,135,16,96,100,32,0,0,18,134,16,
+96,100,102,0,0,16,12,209,103,89,12,134,57,89,0,30,48,92,32,32,99,140,28,
+0,0,8,12,132,105,89,1,76,107,89,12,209,103,89,12,198,57,89,12,134,49,89,
+141,195,57,88,12,1,33,89,132,48,0,90,54,0,0,20,10,175,1,90,31,206,57,88,
+11,140,129,89,21,206,105,89,11,204,137,89,20,14,33,89,145,3,137,88,13,0,
+132,91,10,64,140,91,0,0,0,10,0,30,128,93,248,255,255,8,0,30,128,92,10,22,
+136,92,236,255,255,8,0,48,96,140,0,0,224,255,12,32,1,90,70,0,0,18,90,0,
+0,20,12,96,1,90,50,0,0,18,78,0,0,20,132,3,108,88,13,48,0,90,22,0,0,18,133,
+131,108,88,13,48,0,90,70,254,255,21,176,255,255,8,133,131,108,88,13,48,
+0,90,50,0,0,18,18,22,128,93,156,255,255,8,12,96,1,90,22,0,0,20,146,255,
+255,17,17,195,100,88,140,48,0,90,134,255,255,19,1,25,128,89,1,25,136,89,
+120,255,255,8,145,192,140,88,0,30,128,92,108,255,255,8,17,195,28,88,31,
+204,24,89,31,206,24,89,1,78,36,89,1,206,44,89,21,94,96,89,4,0,107,89,13,
+32,3,90,238,0,0,22,5,0,107,89,13,32,3,90,226,0,0,22,11,78,60,89,1,204,57,
+89,10,14,52,89,22,12,108,89,141,195,57,88,158,201,57,88,11,206,76,89,11,
+142,68,89,21,140,108,89,141,67,74,88,159,73,74,88,21,12,33,89,21,76,41,
+89,137,128,81,103,10,22,104,92,0,30,96,92,137,0,99,103,13,22,80,92,1,12,
+106,89,0,30,96,92,137,0,99,103,11,64,99,103,0,56,0,90,13,129,82,91,0,201,
+90,91,13,129,82,91,0,201,90,91,31,239,2,90,16,0,0,18,10,128,82,91,11,192,
+90,91,1,9,33,89,255,35,33,140,5,1,33,89,254,7,96,140,12,32,1,90,46,0,0,
+20,10,175,2,90,11,140,130,89,21,206,106,89,11,204,138,89,20,78,140,88,20,
+14,33,89,145,3,137,88,13,0,132,91,3,64,140,91,0,0,0,10,132,48,0,90,16,0,
+0,20,1,8,35,89,0,30,80,93,200,255,255,8,0,30,32,92,0,30,80,93,188,255,255,
+8,0,48,96,140,0,0,224,255,12,32,1,90,90,0,0,18,94,0,0,20,12,96,1,90,66,
+0,0,18,82,0,0,20,132,3,108,88,13,48,0,90,38,0,0,18,133,131,108,88,13,48,
+0,90,242,254,255,21,0,48,136,140,0,0,240,127,0,30,128,92,131,67,140,88,
+148,255,255,8,133,131,108,88,13,48,0,90,26,0,0,18,0,30,128,93,131,67,140,
+88,124,255,255,8,12,96,1,90,210,255,255,17,1,25,128,89,1,25,136,89,104,
+255,255,8,0,0,0,0,0,0,0,0,0,0,0,0,17,195,28,88,31,204,24,89,31,206,24,89,
+1,78,36,89,1,206,44,89,21,94,96,89,4,0,107,89,13,32,3,90,214,0,0,22,5,0,
+107,89,13,32,3,90,202,0,0,22,11,78,60,89,11,14,52,89,21,12,108,89,141,195,
+57,88,11,206,76,89,11,142,68,89,21,140,108,89,141,67,74,88,159,201,57,88,
+159,73,74,88,21,12,33,89,21,76,41,89,7,64,98,103,6,64,82,103,0,120,0,90,
+12,192,98,91,13,16,104,91,7,0,114,103,10,128,115,91,12,192,99,91,13,16,
+104,91,31,111,3,90,16,0,0,18,12,0,99,91,13,64,107,91,1,9,33,89,4,124,33,
+140,2,252,255,255,254,7,80,140,10,32,1,90,46,0,0,20,10,47,3,90,11,12,131,
+89,21,78,83,89,11,76,139,89,20,78,140,88,20,14,33,89,145,3,137,88,10,0,
+132,91,3,64,140,91,0,0,0,10,132,48,0,90,16,0,0,20,1,136,34,89,0,30,96,93,
+200,255,255,8,0,30,32,92,0,30,96,93,188,255,255,8,31,204,24,89,31,206,24,
+89,0,48,96,140,0,0,224,255,12,32,1,90,58,0,0,18,70,0,0,20,12,96,1,90,62,
+0,0,20,70,0,0,18,132,3,108,88,13,48,0,90,18,0,0,18,133,131,108,88,13,48,
+0,90,2,255,255,21,0,30,128,93,131,67,140,88,148,255,255,8,12,96,1,90,14,
+0,0,20,5,48,0,90,30,0,0,21,1,25,128,89,1,25,136,89,120,255,255,8,132,3,
+108,88,13,48,0,90,238,255,255,18,0,48,136,140,0,0,240,127,131,67,140,88,
+0,30,128,92,88,255,255,8,0,0,0,0,1,78,36,89,1,206,52,89,0,48,96,140,0,0,
+224,255,12,32,1,90,126,0,0,22,12,160,1,90,198,0,0,22,132,131,49,88,6,48,
+0,90,98,0,0,18,159,77,100,89,31,78,60,88,16,3,51,88,7,3,59,88,0,56,0,90,
+12,129,49,91,0,201,57,91,159,205,100,89,31,206,76,88,18,3,67,88,9,3,75,
+88,0,56,0,90,12,1,66,91,0,73,74,91,135,96,2,90,1,25,128,89,34,0,0,20,1,
+30,128,92,26,0,0,17,6,32,2,90,18,0,0,17,1,25,128,89,10,0,0,20,0,30,128,
+92,0,0,0,10,12,32,1,90,30,0,0,18,3,30,128,92,240,255,255,8,1,30,128,92,
+232,255,255,8,1,25,128,89,224,255,255,8,12,160,1,90,230,255,255,20,30,0,
+0,17,17,195,100,88,140,48,0,90,198,255,255,19,31,111,4,90,214,255,255,16,
+216,255,255,8,145,48,0,90,208,255,255,20,196,255,255,8,12,160,1,90,182,
+255,255,20,31,239,4,90,182,255,255,18,184,255,255,8,0,0,0,10
+};
+
+int pca200e_microcode_size_3 = sizeof(pca200e_microcode_3);
+
+u_char pca200e_microcode_4[] = {
+102,111,114,101,0,1,0,0,64,81,0,0,192,86,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,64,77,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,3,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,
+0,80,45,3,0,0,0,0,0,1,255,128,255,48,0,0,0,1,126,255,
+126,112,0,0,0,1,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+15,0,0,0,0,0,0,1,231,167,167,32,0,0,0,1,11,3,3,0,0,0,
+0,1,11,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+223,111,223,0,0,0,0,1,127,0,0,0,0,0,0,1,255,0,0,0,0,
+0,0,1,255,0,0,0,0,0,0,1,127,65,127,0,0,0,0,1,7,7,7,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,251,0,
+251,0,0,0,0,1,255,240,240,0,0,0,0,1,255,0,0,0,0,0,0,
+1,255,0,0,0,0,0,0,1,15,0,0,0,0,0,0,1,255,0,0,0,0,0,
+0,1,255,0,0,0,0,0,0,1,15,0,0,0,0,0,0,1,127,1,127,0,
+0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+173,0,129,0,0,0,0,1,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,255,175,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,1,255,0,0,0,0,0,0,1,255,255,0,0,0,
+0,0,1,255,0,0,0,0,0,0,1,255,0,0,0,0,0,0,1,255,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,1,7,3,7,0,0,0,0,1,127,126,127,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,1,255,255,255,0,0,0,0,1,255,255,255,144,0,0,0,0,0,
+0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,255,255,255,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,255,
+127,127,4,0,0,0,1,254,224,224,0,0,0,0,1,255,255,255,0,
+0,0,0,1,255,255,255,0,0,0,0,1,255,0,0,0,0,0,0,1,255,
+255,0,0,0,0,0,1,255,0,0,0,0,0,0,1,255,0,0,0,0,0,0,1,
+3,0,0,0,0,0,0,1,255,255,255,252,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,255,159,159,4,0,0,
+0,1,255,255,255,0,0,0,0,1,255,255,255,106,0,0,0,1,222,
+206,207,0,0,0,0,1,255,0,0,0,0,0,0,1,255,0,0,0,0,0,0,
+1,7,0,0,0,0,0,0,1,255,255,255,8,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,1,5,31,5,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,
+0,48,128,140,80,221,3,0,12,192,135,50,16,22,8,92,4,0,0,
+9,128,62,0,102,0,0,0,140,0,30,240,92,0,48,128,140,0,228,
+0,0,0,48,136,140,80,237,0,0,0,16,244,146,4,8,132,89,248,
+95,132,52,0,48,128,140,0,16,0,59,60,131,0,9,236,140,0,9,
+244,127,0,9,96,105,0,9,60,123,0,9,0,48,48,144,0,228,0,
+0,40,160,1,50,16,22,32,93,0,20,128,140,0,0,0,0,0,48,
+136,140,48,87,0,0,17,1,132,89,16,128,49,89,0,144,1,134,
+4,22,128,93,8,0,0,9,116,107,0,8,16,72,8,89,12,38,0,9,
+56,34,0,9,0,48,160,144,68,20,0,80,8,222,144,89,148,128,
+36,88,0,16,129,140,240,32,0,9,136,25,160,88,66,0,37,58,
+14,0,37,49,26,32,1,58,212,0,0,8,137,25,144,88,78,128,36,
+58,200,0,0,8,0,0,0,0,0,48,168,144,140,81,0,0,13,30,144,
+92,0,48,144,130,108,228,0,0,5,30,160,92,164,0,0,8,0,0,
+0,0,0,48,168,144,140,81,0,0,9,30,144,92,0,48,144,130,
+108,228,0,0,4,30,160,92,132,0,0,8,0,0,0,0,0,48,160,136,
+2,4,0,136,8,30,144,92,1,47,5,90,0,48,144,130,108,228,0,
+0,26,0,0,18,0,48,168,144,140,81,0,0,6,30,160,92,84,0,0,
+8,0,0,0,0,42,32,21,48,22,32,5,48,0,48,168,144,140,81,0,
+0,22,30,160,92,56,0,0,8,0,48,168,144,140,81,0,0,31,216,
+161,89,40,0,0,8,26,32,5,48,0,48,168,144,140,81,0,0,31,
+216,165,89,20,0,0,8,0,0,0,0,0,48,168,144,140,81,0,0,70,
+0,160,140,176,96,165,146,136,35,0,9,180,42,0,9,224,30,0,
+9,164,32,0,11,137,25,144,88,78,128,36,61,240,69,0,9,30,
+32,4,61,0,48,168,144,112,228,0,0,0,48,160,140,20,0,173,
+222,0,80,165,146,196,106,0,9,0,30,128,92,64,224,143,140,
+16,71,0,11,30,32,4,58,64,224,167,144,0,48,168,144,140,81,
+0,0,127,0,144,140,148,128,164,88,16,0,0,8,0,48,168,144,
+140,81,0,0,1,25,160,89,180,96,165,146,0,48,160,140,48,3,
+0,0,0,48,160,146,4,228,0,0,168,33,0,9,196,81,0,9,48,85,
+0,9,1,30,128,92,216,105,0,9,0,0,0,10,0,48,128,144,8,
+228,0,0,0,16,164,144,127,0,232,140,148,64,167,88,10,35,5,
+58,2,9,165,89,238,33,77,52,20,57,160,144,32,89,0,0,0,16,
+5,132,0,0,0,0,72,89,0,0,44,91,0,0,88,89,0,0,40,90,0,
+0,104,89,0,0,240,89,0,0,8,90,0,0,248,90,0,0,48,90,0,0,
+144,90,0,0,88,42,0,9,16,22,152,92,224,1,0,8,0,0,0,0,
+24,45,0,9,16,22,152,92,208,1,0,8,0,0,0,0,0,48,160,144,
+68,20,0,80,19,47,5,90,2,0,152,140,34,0,0,16,147,9,165,
+88,0,48,240,146,164,86,0,0,0,48,160,146,68,20,0,80,20,0,
+0,8,0,0,0,0,1,30,160,92,0,48,160,146,164,86,0,0,4,32,
+172,144,0,48,128,140,96,0,0,32,31,88,166,89,0,48,144,140,
+208,85,0,0,14,30,136,92,0,48,168,146,84,0,0,32,0,48,160,
+146,96,0,0,32,1,73,140,89,0,144,164,176,145,48,0,90,16,
+160,148,140,0,16,164,178,236,255,255,21,72,1,0,8,0,0,0,
+0,24,74,0,11,144,48,0,90,2,0,152,140,54,1,0,21,10,30,
+152,92,44,1,0,8,232,74,0,9,0,48,160,144,8,228,0,0,4,32,
+141,144,16,30,128,92,0,48,144,140,176,228,0,0,236,20,0,9,
+2,30,152,92,4,1,0,8,0,48,160,144,8,228,0,0,4,32,165,
+144,0,48,160,146,64,0,0,32,0,48,160,144,0,225,1,240,0,
+48,160,146,96,0,0,48,0,48,160,144,4,225,1,240,0,48,160,
+146,96,0,0,48,0,48,160,144,100,224,1,240,0,48,160,146,96,
+0,0,48,0,48,160,144,104,224,1,240,2,30,152,92,0,48,160,
+146,96,0,0,48,168,0,0,8,0,0,0,0,0,48,160,144,8,228,0,
+0,4,32,165,144,20,144,169,89,0,48,232,140,240,255,15,0,0,
+48,168,146,252,2,0,0,149,69,175,89,0,48,232,140,0,240,15,
+0,20,70,167,89,0,48,232,140,0,0,240,15,148,64,167,88,0,
+48,232,140,240,255,15,0,0,48,160,146,248,2,0,0,149,64,
+175,88,21,4,165,88,0,48,168,146,244,2,0,0,0,48,160,146,
+240,2,0,0,60,0,0,8,0,48,160,144,8,228,0,0,0,16,165,144,
+0,48,232,140,94,208,94,208,34,64,167,61,0,48,168,144,112,
+228,0,0,0,48,160,140,13,0,173,222,0,30,128,92,0,80,165,
+146,152,103,0,9,8,30,152,92,0,48,168,144,8,228,0,0,0,80,
+165,144,150,32,61,48,0,48,160,144,12,228,0,0,16,96,189,
+144,32,96,181,140,22,32,5,90,0,80,245,146,14,0,0,22,0,
+48,176,144,16,228,0,0,0,48,160,144,116,228,0,0,0,48,184,
+146,56,0,0,32,0,48,152,146,96,0,0,48,0,16,165,144,148,
+48,0,90,0,48,176,146,8,228,0,0,66,0,0,18,0,48,168,144,
+120,228,0,0,0,80,165,144,20,80,160,89,0,80,165,146,0,48,
+168,144,96,81,0,0,255,1,232,140,21,80,168,89,149,64,167,
+88,136,9,165,88,0,48,168,146,96,81,0,0,0,48,160,146,4,0,
+0,32,0,0,0,10,0,48,160,144,12,228,0,0,16,96,189,144,32,
+96,181,140,22,32,5,90,0,80,245,146,14,0,0,22,0,48,176,
+144,16,228,0,0,0,48,184,146,56,0,0,32,0,48,176,146,8,
+228,0,0,0,48,152,146,96,0,0,48,0,0,0,10,24,32,36,152,0,
+48,168,144,140,228,0,0,4,65,161,89,20,96,5,90,0,16,52,
+140,18,0,0,20,1,30,128,92,0,0,0,10,0,0,0,0,0,48,168,
+144,80,3,0,0,58,96,5,58,0,48,160,144,104,3,0,0,1,73,
+173,89,0,48,168,146,80,3,0,0,149,29,181,140,0,144,165,
+144,5,16,170,89,0,80,161,146,28,32,172,146,4,160,133,144,
+0,0,0,10,0,0,0,0,0,48,128,140,80,3,0,0,208,92,0,9,0,
+48,168,144,80,3,0,0,54,96,5,58,0,48,160,144,104,3,0,0,
+1,73,173,89,0,48,168,146,80,3,0,0,149,29,181,140,0,144,
+165,144,5,16,170,89,0,80,161,146,28,160,169,146,4,160,133,
+144,0,0,0,10,0,48,160,144,144,86,0,0,20,80,160,89,1,0,
+128,140,0,48,160,146,144,86,0,0,0,0,0,10,0,0,0,0,24,32,
+36,152,0,48,168,144,140,228,0,0,4,65,161,89,20,96,5,90,
+0,16,52,140,18,0,0,20,1,30,128,92,0,0,0,10,0,0,0,0,0,
+48,168,144,128,3,0,0,58,96,5,58,0,48,160,144,152,3,0,0,
+1,73,173,89,0,48,168,146,128,3,0,0,149,29,181,140,0,144,
+165,144,5,16,170,89,0,80,161,146,28,32,172,146,4,160,133,
+144,0,0,0,10,0,0,0,0,0,48,128,140,128,3,0,0,0,92,0,9,
+0,48,168,144,128,3,0,0,54,96,5,58,0,48,160,144,152,3,0,
+0,1,73,173,89,0,48,168,146,128,3,0,0,149,29,181,140,0,
+144,165,144,5,16,170,89,0,80,161,146,28,160,169,146,4,160,
+133,144,0,0,0,10,0,48,160,144,148,86,0,0,20,80,160,89,1,
+0,128,140,0,48,160,146,148,86,0,0,0,0,0,10,0,0,0,0,24,
+32,36,152,0,48,168,144,140,228,0,0,4,65,161,89,20,96,5,
+90,0,16,52,140,18,0,0,20,1,30,128,92,0,0,0,10,0,0,0,0,
+0,48,168,144,176,3,0,0,58,96,5,58,0,48,160,144,200,3,0,
+0,1,73,173,89,0,48,168,146,176,3,0,0,149,29,181,140,0,
+144,165,144,5,16,170,89,0,80,161,146,28,32,172,146,4,160,
+133,144,0,0,0,10,0,0,0,0,0,48,128,140,176,3,0,0,48,91,
+0,9,0,48,168,144,176,3,0,0,54,96,5,58,0,48,160,144,200,
+3,0,0,1,73,173,89,0,48,168,146,176,3,0,0,149,29,181,140,
+0,144,165,144,5,16,170,89,0,80,161,146,28,160,169,146,4,
+160,133,144,0,0,0,10,0,48,160,144,152,86,0,0,20,80,160,
+89,1,0,128,140,0,48,160,146,152,86,0,0,0,0,0,10,0,0,0,
+0,24,32,36,152,0,48,168,144,140,228,0,0,4,65,161,89,20,
+96,5,90,0,16,52,140,18,0,0,20,1,30,128,92,0,0,0,10,0,
+0,0,0,0,48,168,144,32,228,0,0,58,96,5,58,0,48,160,144,
+56,228,0,0,1,73,173,89,0,48,168,146,32,228,0,0,149,29,
+181,140,0,144,165,144,5,16,170,89,0,80,161,146,28,32,172,
+146,4,160,133,144,0,0,0,10,0,0,0,0,0,48,128,140,32,228,
+0,0,96,90,0,9,0,48,168,144,32,228,0,0,54,96,5,58,0,48,
+160,144,56,228,0,0,1,73,173,89,0,48,168,146,32,228,0,0,
+149,29,181,140,0,144,165,144,5,16,170,89,0,80,161,146,28,
+160,169,146,4,160,133,144,0,0,0,10,0,48,160,144,156,86,0,
+0,20,80,160,89,1,0,128,140,0,48,160,146,156,86,0,0,0,0,
+0,10,0,0,0,0,64,96,8,140,112,224,199,178,16,22,192,92,0,
+48,128,140,80,3,0,0,31,88,140,89,232,96,0,9,0,48,128,
+140,128,3,0,0,31,88,140,89,216,96,0,9,0,48,128,140,176,
+3,0,0,31,88,140,89,200,96,0,9,0,48,128,140,32,228,0,0,
+31,88,140,89,184,96,0,9,0,48,160,140,144,86,0,0,0,48,
+160,146,120,3,0,0,0,48,160,140,148,86,0,0,0,48,160,146,
+168,3,0,0,0,48,160,140,152,86,0,0,0,48,160,146,216,3,0,
+0,0,48,160,140,156,86,0,0,0,48,160,146,72,228,0,0,120,
+32,166,144,0,48,160,146,96,3,0,0,136,32,174,144,0,48,168,
+146,144,3,0,0,152,32,182,144,0,48,176,146,192,3,0,0,168,
+32,190,144,64,32,38,140,148,48,0,90,0,48,184,146,48,228,
+0,0,30,0,0,21,26,96,5,61,22,160,5,61,18,224,5,61,8,30,
+128,92,112,224,199,176,0,0,0,10,52,32,169,144,0,48,168,
+146,100,3,0,0,68,32,177,144,0,48,176,146,148,3,0,0,84,
+32,185,144,0,48,184,146,196,3,0,0,0,48,160,144,96,3,0,0,
+100,32,129,144,148,48,0,90,0,48,128,146,52,228,0,0,14,0,
+0,18,63,0,200,140,178,95,174,62,0,48,160,144,144,3,0,0,
+14,32,5,58,63,0,200,140,158,95,182,62,0,48,160,144,192,3,
+0,0,14,32,5,58,63,0,200,140,138,95,190,62,0,48,160,144,
+48,228,0,0,14,32,5,58,63,0,200,140,118,95,134,62,0,48,
+160,144,96,3,0,0,38,32,5,58,0,48,160,144,144,3,0,0,26,
+32,5,58,0,48,168,144,100,3,0,0,0,48,160,144,148,3,0,0,
+74,31,173,57,0,48,160,144,192,3,0,0,38,32,5,58,0,48,160,
+144,48,228,0,0,26,32,5,58,0,48,168,144,196,3,0,0,0,48,
+160,144,52,228,0,0,30,31,173,57,0,48,160,144,96,3,0,0,
+46,32,5,61,0,48,168,144,148,3,0,0,0,48,160,140,224,92,0,
+0,0,48,160,146,80,228,0,0,0,48,168,146,100,3,0,0,24,0,
+0,8,0,0,0,0,0,48,160,140,16,92,0,0,0,48,160,146,80,228,
+0,0,0,48,160,144,192,3,0,0,42,32,5,61,0,48,168,144,52,
+228,0,0,0,48,160,140,128,94,0,0,0,48,160,146,84,228,0,0,
+0,48,168,146,196,3,0,0,20,0,0,8,0,48,160,140,176,93,0,
+0,0,48,160,146,84,228,0,0,60,32,185,144,0,48,184,146,92,
+3,0,0,76,32,129,144,0,48,128,146,140,3,0,0,92,32,145,
+144,0,48,144,146,188,3,0,0,0,48,232,144,96,3,0,0,108,32,
+153,144,157,48,0,90,0,48,152,146,44,228,0,0,10,0,0,18,
+86,254,5,59,31,216,39,89,78,30,185,49,151,208,160,88,70,
+62,5,61,0,48,136,144,144,3,0,0,10,96,4,58,54,62,4,59,
+50,30,129,49,144,208,160,88,42,62,5,61,0,48,176,144,192,
+3,0,0,10,160,5,58,26,190,4,59,22,30,145,49,146,208,160,
+88,14,62,5,61,0,48,168,144,48,228,0,0,10,96,5,58,254,
+253,4,59,250,29,153,49,147,208,160,88,242,61,5,61,10,96,
+7,58,234,221,237,60,10,96,4,58,226,29,140,60,10,160,5,58,
+218,157,180,60,10,96,5,58,210,221,172,60,0,48,104,144,144,
+3,0,0,0,48,128,176,128,3,0,0,0,48,24,144,96,3,0,0,0,
+48,160,176,80,3,0,0,0,48,120,144,48,228,0,0,0,48,64,176,
+32,228,0,0,0,48,112,144,192,3,0,0,0,48,32,176,176,3,0,
+0,23,193,232,89,1,205,96,89,0,80,175,140,12,22,176,92,0,
+48,160,146,80,3,0,0,0,48,232,146,84,3,0,0,0,48,184,146,
+92,3,0,0,0,48,96,146,88,3,0,0,19,65,163,89,1,77,107,89,
+0,16,141,140,13,22,144,92,0,48,128,146,128,3,0,0,0,48,
+160,146,132,3,0,0,0,48,104,146,136,3,0,0,0,48,152,146,
+140,3,0,0,7,129,163,89,1,141,115,89,0,16,45,140,14,22,
+48,92,0,48,32,146,176,3,0,0,0,48,160,146,180,3,0,0,0,
+48,112,146,184,3,0,0,0,48,56,146,188,3,0,0,11,193,163,
+89,1,205,123,89,0,16,77,140,15,22,80,92,0,48,64,146,32,
+228,0,0,0,48,160,146,36,228,0,0,131,48,0,90,0,48,120,
+146,40,228,0,0,0,48,128,140,80,3,0,0,0,30,32,92,0,48,
+88,146,44,228,0,0,98,0,0,18,112,32,142,144,16,22,208,92,
+0,48,216,140,56,3,0,0,0,48,128,146,48,3,0,0,0,48,216,
+146,52,3,0,0,228,38,0,9,144,48,2,90,1,0,32,140,142,252,
+255,18,0,48,160,144,108,3,0,0,12,32,166,146,0,48,128,144,
+96,3,0,0,3,14,132,89,76,58,0,9,144,48,0,90,0,48,128,
+146,104,3,0,0,98,252,255,18,0,48,160,144,144,3,0,0,148,
+48,0,90,0,48,128,140,144,3,0,0,126,0,0,18,16,9,204,89,
+132,57,160,140,0,0,0,0,0,48,168,140,48,3,0,0,96,224,207,
+146,20,124,181,140,8,0,0,0,100,224,183,146,96,224,207,144,
+128,32,142,144,20,64,165,89,16,9,132,89,0,16,205,146,4,
+32,181,146,84,38,0,9,144,48,2,90,1,32,33,140,254,251,255,
+18,0,48,160,144,156,3,0,0,16,32,166,146,0,48,128,144,144,
+3,0,0,3,14,132,89,188,57,0,9,144,48,0,90,0,48,128,146,
+152,3,0,0,210,251,255,18,0,48,160,144,192,3,0,0,148,48,
+0,90,0,48,128,140,192,3,0,0,126,0,0,18,16,9,204,89,132,
+57,160,140,0,0,0,0,0,48,168,140,48,3,0,0,80,224,207,146,
+20,124,181,140,8,0,0,0,84,224,183,146,80,224,207,144,144,
+32,142,144,20,64,165,89,16,9,132,89,0,16,205,146,4,32,
+181,146,196,37,0,9,144,48,2,90,1,32,33,140,110,251,255,
+18,0,48,160,144,204,3,0,0,20,32,166,146,0,48,128,144,192,
+3,0,0,3,14,132,89,44,57,0,9,144,48,0,90,0,48,128,146,
+200,3,0,0,66,251,255,18,0,48,160,144,48,228,0,0,148,48,
+0,90,0,48,128,140,48,228,0,0,126,0,0,18,16,9,204,89,132,
+57,160,140,0,0,0,0,0,48,168,140,48,3,0,0,64,224,207,146,
+20,124,181,140,8,0,0,0,68,224,183,146,64,224,207,144,160,
+32,142,144,20,64,165,89,16,9,132,89,0,16,205,146,4,32,
+181,146,52,37,0,9,144,48,2,90,1,32,33,140,222,250,255,18,
+0,48,160,144,60,228,0,0,24,32,166,146,0,48,128,144,48,
+228,0,0,3,14,132,89,156,56,0,9,144,48,0,90,0,48,128,146,
+56,228,0,0,178,250,255,18,0,48,160,140,48,3,0,0,2,30,
+128,92,132,61,165,146,252,255,255,255,112,224,199,176,0,0,
+0,10,0,0,0,0,0,0,0,0,0,0,0,0,16,22,32,92,0,48,128,
+146,104,228,0,0,1,9,164,89,0,80,44,140,144,64,129,112,0,
+48,240,146,8,3,0,0,0,48,240,146,4,3,0,0,0,48,160,146,
+12,3,0,0,48,56,0,9,144,48,0,90,0,48,128,146,96,228,0,0,
+18,0,0,21,8,30,128,92,0,0,0,10,0,0,0,0,4,57,128,140,0,
+0,0,0,8,56,0,9,144,48,0,90,0,48,128,146,0,3,0,0,18,0,
+0,21,8,30,128,92,0,0,0,10,0,0,0,0,0,0,176,140,150,32,
+1,90,0,48,184,144,96,228,0,0,98,0,0,19,0,47,1,90,0,0,
+136,140,30,0,0,16,1,30,176,92,150,32,1,90,23,64,164,89,
+23,64,185,89,0,16,164,146,62,0,0,19,0,48,168,144,0,3,0,
+0,23,64,164,89,22,93,165,146,0,48,168,144,0,3,0,0,23,64,
+185,89,23,64,164,89,22,125,165,146,4,0,0,0,22,144,176,89,
+150,32,1,90,23,64,185,89,204,255,255,20,2,14,129,89,116,
+55,0,9,144,48,0,90,0,48,128,146,100,228,0,0,14,0,0,21,
+8,30,128,92,0,0,0,10,0,0,176,140,62,0,177,59,22,32,1,
+48,1,30,176,92,150,32,1,90,0,16,244,146,42,0,0,19,0,48,
+160,144,100,228,0,0,22,29,245,146,0,48,160,144,100,228,0,
+0,22,61,245,146,4,0,0,0,22,144,176,89,224,31,177,60,2,
+30,128,92,0,0,0,10,0,48,168,144,4,3,0,0,0,48,160,144,0,
+3,0,0,21,29,165,140,0,16,149,144,146,48,0,90,0,16,44,
+140,17,22,32,92,54,0,0,18,0,16,245,146,0,48,160,144,4,3,
+0,0,0,48,168,144,104,228,0,0,0,144,140,146,16,16,134,89,
+8,160,180,140,0,16,148,146,4,32,180,146,92,0,0,8,0,0,0,
+0,0,48,160,144,84,81,0,0,106,32,5,59,188,1,0,9,0,48,
+168,144,4,3,0,0,0,48,160,144,0,3,0,0,21,29,165,140,0,
+16,149,144,74,160,4,58,0,16,245,146,0,48,160,144,4,3,0,
+0,0,48,168,144,104,228,0,0,0,144,36,146,5,16,182,89,8,
+160,188,140,0,144,149,146,4,160,189,146,1,73,173,89,1,32,
+165,140,148,64,165,88,1,0,128,140,0,48,160,146,4,3,0,0,
+0,0,0,10,0,48,160,144,160,86,0,0,20,80,160,89,0,0,128,
+140,0,48,160,146,160,86,0,0,0,0,0,10,0,0,0,0,0,0,0,0,
+0,0,0,0,0,48,136,144,140,228,0,0,16,22,32,92,16,47,0,9,
+0,48,176,144,132,228,0,0,0,48,128,144,8,3,0,0,0,48,160,
+144,8,3,0,0,0,48,168,144,104,228,0,0,0,48,184,144,0,3,
+0,0,20,80,160,89,22,58,176,140,16,0,0,0,1,73,173,89,0,
+16,177,146,148,64,165,88,16,221,37,146,0,48,160,146,8,3,
+0,0,0,0,0,10,0,48,240,140,152,104,0,0,30,22,136,92,0,0,
+240,140,0,48,168,144,88,81,0,0,0,48,184,144,100,228,0,0,
+0,48,160,144,104,228,0,0,0,48,176,144,84,81,0,0,21,221,
+133,144,1,9,165,89,1,96,173,140,149,0,173,88,1,137,181,
+89,0,48,168,146,88,81,0,0,0,48,176,146,84,81,0,0,0,80,
+4,132,0,0,0,10,0,0,0,0,0,48,240,140,12,105,0,0,30,22,
+144,92,0,0,240,140,0,48,160,144,160,86,0,0,0,48,184,144,
+92,81,0,0,0,48,136,144,100,228,0,0,0,48,168,144,104,228,
+0,0,0,48,176,144,84,81,0,0,20,80,160,89,0,48,160,146,
+160,86,0,0,1,73,173,89,1,224,165,140,148,64,165,88,23,93,
+132,146,22,80,176,89,0,48,160,146,92,81,0,0,0,48,176,146,
+84,81,0,0,0,144,4,132,0,0,0,10,0,48,64,140,88,81,0,0,
+0,48,56,140,16,3,0,0,0,48,72,140,4,0,0,32,0,48,48,140,
+8,3,0,0,0,48,136,144,16,3,0,0,0,80,164,144,110,33,5,58,
+0,16,162,144,0,48,176,144,100,228,0,0,0,48,168,144,104,
+228,0,0,20,157,37,144,1,73,173,89,1,32,165,140,148,64,
+165,88,0,16,162,146,4,16,169,89,0,80,181,144,0,48,160,
+144,84,81,0,0,0,48,128,140,255,255,0,0,150,0,132,88,0,
+80,133,146,0,80,140,144,1,9,165,89,0,48,160,146,84,81,0,
+0,4,22,144,92,144,56,128,140,9,0,0,0,3,12,132,89,144,
+141,45,89,100,5,0,9,0,208,161,144,0,48,168,144,20,3,0,0,
+20,16,178,89,4,32,189,144,22,96,5,90,0,16,245,146,14,0,
+0,22,0,48,176,144,24,3,0,0,0,48,160,144,116,228,0,0,0,
+48,184,146,56,0,0,32,129,73,169,88,0,48,168,146,96,0,0,
+48,0,16,165,144,148,48,0,90,0,208,177,146,62,0,0,18,0,
+48,168,144,120,228,0,0,0,80,165,144,20,80,160,89,0,80,
+165,146,0,48,168,144,96,81,0,0,255,1,152,140,21,80,168,
+89,149,192,164,88,0,48,168,146,96,81,0,0,136,9,165,88,0,
+80,162,146,0,48,136,144,140,228,0,0,4,22,128,92,180,44,0,
+9,0,48,160,144,132,228,0,0,0,48,128,144,8,3,0,0,0,48,
+184,144,0,3,0,0,0,48,176,144,104,228,0,0,0,48,168,144,
+84,81,0,0,20,58,160,140,16,0,0,0,0,16,161,146,0,144,161,
+144,149,48,0,90,16,221,37,146,1,137,181,89,1,32,165,140,
+148,128,165,88,0,144,161,146,148,254,255,17,0,0,0,10,0,0,
+0,0,0,0,0,10,0,0,0,0,88,32,164,144,148,48,1,90,0,144,
+236,140,19,22,48,92,26,0,0,18,14,32,37,52,210,32,5,58,0,
+1,0,8,106,32,45,58,248,0,0,8,50,224,20,61,0,48,160,144,
+52,86,0,0,0,48,168,144,72,86,0,0,17,0,141,89,1,96,173,
+140,0,48,136,146,52,86,0,0,0,48,168,146,72,86,0,0,200,0,
+0,8,0,48,160,144,64,86,0,0,0,48,168,144,80,86,0,0,17,0,
+141,89,1,96,173,140,0,48,136,146,64,86,0,0,0,48,168,146,
+80,86,0,0,156,0,0,8,0,0,0,0,50,224,20,61,0,48,160,144,
+100,86,0,0,0,48,168,144,116,86,0,0,17,0,141,89,1,96,173,
+140,0,48,136,146,100,86,0,0,0,48,168,146,116,86,0,0,104,
+0,0,8,0,48,160,144,104,86,0,0,0,48,168,144,128,86,0,0,
+17,0,141,89,1,96,173,140,0,48,136,146,104,86,0,0,0,48,
+168,146,128,86,0,0,60,0,0,8,0,0,0,0,34,224,20,61,0,48,
+160,144,36,86,0,0,17,0,141,89,0,48,136,146,36,86,0,0,28,
+0,0,8,0,0,0,0,0,48,160,144,40,86,0,0,17,0,141,89,0,48,
+136,146,40,86,0,0,24,32,36,152,4,65,161,89,8,9,165,89,3,
+12,165,89,4,32,161,146,88,32,164,144,4,16,138,89,148,48,
+0,90,48,32,164,176,22,0,0,18,18,64,149,62,4,16,140,89,
+21,129,236,89,12,32,169,146,26,128,237,60,22,65,239,89,4,
+96,180,146,157,160,5,90,8,96,140,140,240,255,255,19,10,96,
+7,59,4,96,236,146,0,48,160,144,84,81,0,0,10,32,5,59,224,
+252,255,9,0,48,136,144,16,3,0,0,0,80,164,144,122,32,5,
+61,4,32,129,144,0,48,144,144,92,81,0,0,0,48,136,144,100,
+228,0,0,0,48,168,144,92,81,0,0,0,48,176,144,104,228,0,0,
+0,48,184,144,84,81,0,0,16,142,161,89,148,3,164,88,4,32,
+161,146,18,93,36,146,0,48,160,144,160,86,0,0,1,137,181,
+89,1,96,173,140,149,128,173,88,1,224,189,140,0,48,168,146,
+92,81,0,0,0,48,184,146,84,81,0,0,20,80,160,89,0,48,160,
+146,160,86,0,0,0,0,0,10,4,32,129,144,0,80,140,144,4,22,
+144,92,144,56,128,140,9,0,0,0,131,13,132,89,64,2,0,9,0,
+48,160,144,16,3,0,0,0,48,168,144,20,3,0,0,20,16,178,89,
+4,32,189,144,22,96,5,90,0,16,245,146,14,0,0,22,0,48,176,
+144,24,3,0,0,134,48,2,90,0,48,184,146,56,0,0,32,129,137,
+161,88,0,48,176,146,16,3,0,0,0,48,160,146,96,0,0,48,34,
+0,0,21,0,144,165,144,102,32,5,61,0,48,160,144,116,228,0,
+0,0,16,165,144,86,32,5,58,20,0,0,8,0,48,160,144,116,228,
+0,0,0,16,165,144,66,32,5,58,0,48,168,144,120,228,0,0,0,
+80,165,144,20,80,160,89,0,80,165,146,0,48,168,144,96,81,
+0,0,255,1,56,140,21,80,168,89,149,192,161,88,136,9,165,
+88,0,48,168,146,96,81,0,0,0,48,160,146,4,0,0,32,0,48,
+136,144,140,228,0,0,4,22,128,92,100,41,0,9,0,48,176,144,
+132,228,0,0,0,48,128,144,8,3,0,0,0,48,160,144,8,3,0,0,
+0,48,168,144,104,228,0,0,0,48,184,144,0,3,0,0,20,80,160,
+89,22,58,176,140,16,0,0,0,1,73,173,89,0,16,177,146,148,
+64,165,88,16,221,37,146,0,48,160,146,8,3,0,0,0,0,0,10,
+144,68,148,101,0,0,0,10,5,56,0,102,0,0,0,10,33,22,128,
+92,0,0,0,10,16,54,8,92,16,22,128,92,16,22,128,92,16,22,
+128,92,16,22,128,92,16,22,128,92,16,22,128,92,16,22,128,
+92,16,22,128,92,0,0,0,10,16,54,0,92,0,0,0,10,32,22,128,
+92,0,0,0,10,0,48,32,140,0,0,31,0,16,14,44,89,132,2,41,
+101,5,22,128,92,0,0,0,10,144,18,128,101,0,0,0,10,0,48,
+32,140,255,255,255,255,16,64,36,97,4,22,128,92,0,0,0,10,
+16,64,148,97,18,22,128,92,0,0,0,10,16,65,36,97,4,22,128,
+92,0,0,0,10,0,0,0,0,0,0,0,0,0,30,152,92,0,48,136,146,
+48,0,0,32,147,32,4,90,144,57,160,140,0,0,0,0,0,48,160,
+146,96,0,0,32,82,0,0,19,0,48,232,140,8,0,0,16,0,48,136,
+140,0,0,0,48,0,80,167,144,22,32,61,52,0,48,168,140,8,0,
+0,16,0,80,165,144,252,63,61,51,0,80,164,176,0,144,164,
+178,0,80,164,176,19,80,152,89,16,160,148,140,147,32,4,90,
+0,144,164,178,18,16,148,89,200,255,255,20,0,0,0,10,0,0,
+0,0,0,0,0,0,0,0,0,0,0,30,152,92,0,48,136,146,84,0,0,
+32,147,32,4,90,144,57,160,140,0,0,0,0,0,48,160,146,96,0,
+0,32,50,0,0,19,0,48,136,140,96,0,0,48,0,144,164,176,18,
+16,148,89,1,224,156,140,147,32,4,90,0,80,164,178,0,144,
+164,176,18,16,148,89,0,80,164,178,224,255,255,20,0,0,0,
+10,0,0,0,0,0,0,0,0,0,0,0,0,144,176,0,90,0,48,136,146,
+84,0,0,32,16,22,152,92,0,144,140,140,0,48,128,146,96,0,
+0,32,42,0,0,18,18,32,20,60,50,32,12,58,64,0,0,8,0,0,0,
+0,58,32,28,61,0,144,164,144,18,16,137,89,0,48,160,146,96,
+0,0,32,0,80,164,144,17,16,137,89,0,48,160,146,96,0,0,32,
+0,80,164,144,0,48,160,146,96,0,0,32,0,0,0,10,0,0,0,0,
+146,208,163,88,50,32,69,58,18,32,69,52,22,32,37,58,76,0,
+0,8,0,0,0,0,50,32,101,58,64,0,0,8,0,144,164,144,18,16,
+137,89,1,9,156,89,0,48,160,146,96,0,0,32,0,80,164,144,
+17,16,137,89,1,201,156,89,0,48,160,146,96,0,0,32,0,80,
+164,144,17,16,137,89,1,201,156,89,0,48,160,146,96,0,0,32,
+38,224,28,59,0,48,128,140,96,0,0,32,4,201,156,89,0,80,
+164,176,147,240,0,90,16,96,140,140,0,16,164,178,236,255,
+255,17,38,224,4,59,0,48,168,140,96,0,0,32,1,201,156,89,
+0,80,164,144,147,48,0,90,4,96,140,140,0,80,165,146,236,
+255,255,17,0,0,0,10,255,0,176,140,144,128,133,88,0,0,168,
+140,0,48,136,140,255,179,196,4,0,48,184,140,4,0,0,16,0,
+48,128,146,4,0,0,32,0,208,165,144,148,128,165,88,38,0,
+164,58,21,80,168,89,240,95,172,62,0,48,168,144,112,228,0,
+0,0,48,160,140,6,0,173,222,0,80,165,146,132,82,0,8,0,0,
+0,10,0,0,0,0,0,0,0,0,0,0,0,0,255,0,184,140,0,30,168,
+92,0,48,136,140,255,179,196,4,0,48,128,140,4,0,0,16,0,
+48,176,128,96,81,0,0,0,16,164,144,148,192,165,88,42,128,
+165,58,21,80,168,89,240,95,172,62,0,48,168,144,112,228,0,
+0,0,48,160,140,7,0,173,222,0,80,165,146,40,82,0,8,0,0,
+0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,48,240,140,
+160,113,0,0,30,22,128,92,0,0,240,140,0,48,160,144,116,
+228,0,0,0,16,165,144,66,32,5,58,0,48,168,144,120,228,0,
+0,0,80,165,144,20,80,160,89,0,80,165,146,0,48,168,144,96,
+81,0,0,255,1,176,140,21,80,168,89,149,128,165,88,136,9,
+165,88,0,48,168,146,96,81,0,0,0,48,160,146,4,0,0,32,0,
+16,4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,16,22,40,
+92,1,73,49,89,0,0,32,140,0,48,64,140,16,39,0,0,2,142,
+57,89,108,252,255,9,5,1,132,88,92,252,255,9,4,22,160,92,
+148,32,2,90,1,32,33,140,158,0,0,22,150,160,249,52,0,244,
+161,144,248,113,0,0,0,16,5,132,0,0,0,0,120,114,0,0,120,
+114,0,0,120,114,0,0,120,114,0,0,120,114,0,0,120,114,0,0,
+120,114,0,0,120,114,0,0,120,114,0,0,120,114,0,0,120,114,
+0,0,120,114,0,0,120,114,0,0,120,114,0,0,120,114,0,0,120,
+114,0,0,120,114,0,0,120,114,0,0,120,114,0,0,120,114,0,0,
+120,114,0,0,120,114,0,0,120,114,0,0,120,114,0,0,120,114,
+0,0,120,114,0,0,120,114,0,0,120,114,0,0,120,114,0,0,120,
+114,0,0,120,114,0,0,120,114,0,0,216,80,0,9,184,251,255,
+9,144,64,129,88,68,63,4,61,1,30,128,92,0,0,0,10,64,96,
+8,140,0,116,128,178,192,255,255,255,0,116,160,178,208,255,
+255,255,0,116,192,178,224,255,255,255,0,116,224,178,240,
+255,255,255,0,48,160,144,108,81,0,0,34,32,5,61,0,48,168,
+144,112,228,0,0,0,48,160,140,1,0,173,222,0,80,165,146,
+124,80,0,9,232,79,0,9,0,48,160,144,0,0,0,16,210,0,136,
+140,148,64,164,88,40,0,136,140,148,67,164,88,0,0,32,140,
+0,48,40,140,16,39,0,0,0,48,160,146,0,0,0,16,44,251,255,
+9,0,14,132,88,28,251,255,9,4,22,160,92,148,96,1,90,1,32,
+33,140,10,0,0,22,44,80,0,9,12,251,255,9,220,63,4,55,0,
+48,168,144,120,81,0,0,0,48,160,144,116,81,0,0,149,48,0,
+90,1,32,165,140,0,48,160,146,116,81,0,0,90,0,0,18,0,48,
+160,144,116,228,0,0,0,16,165,144,82,32,5,58,0,48,168,144,
+120,228,0,0,0,80,165,144,20,80,160,89,0,80,165,146,0,48,
+168,144,96,81,0,0,255,1,136,140,21,80,168,89,149,64,164,
+88,136,9,165,88,0,48,168,146,96,81,0,0,0,48,160,146,4,0,
+0,32,16,0,0,8,0,0,0,0,0,48,240,146,108,81,0,0,0,116,
+128,176,192,255,255,255,0,116,160,176,208,255,255,255,0,
+116,192,176,224,255,255,255,0,116,224,176,240,255,255,255,
+3,54,8,92,0,0,0,10,0,0,0,0,0,0,0,0,64,96,8,140,0,116,
+128,178,192,255,255,255,0,116,160,178,208,255,255,255,0,
+116,192,178,224,255,255,255,0,116,224,178,240,255,255,255,
+0,48,160,144,68,20,0,80,14,32,37,48,10,32,133,48,156,3,
+0,9,0,48,160,144,68,20,0,80,14,32,45,48,10,32,141,48,
+168,3,0,9,0,48,160,144,68,20,0,80,14,32,53,48,10,32,149,
+48,180,3,0,9,0,48,160,144,68,12,0,80,14,32,21,48,10,32,
+29,48,192,3,0,9,0,30,32,92,0,48,40,140,16,39,0,0,212,
+249,255,9,4,14,132,88,196,249,255,9,4,22,160,92,148,96,1,
+90,1,32,33,140,10,0,0,22,212,78,0,9,180,249,255,9,220,
+63,36,55,0,116,128,176,192,255,255,255,0,116,160,176,208,
+255,255,255,0,116,192,176,224,255,255,255,0,116,224,176,
+240,255,255,255,3,54,8,92,0,0,0,10,64,96,8,140,0,116,
+128,178,192,255,255,255,0,116,160,178,208,255,255,255,0,
+116,192,178,224,255,255,255,0,116,224,178,240,255,255,255,
+0,48,160,144,132,81,0,0,34,32,5,61,0,48,168,144,112,228,
+0,0,0,48,160,140,2,0,173,222,0,80,165,146,92,78,0,9,200,
+77,0,9,0,30,32,92,0,48,40,140,16,39,0,0,56,4,0,9,40,
+249,255,9,5,14,132,88,24,249,255,9,4,22,160,92,148,96,1,
+90,1,32,33,140,10,0,0,22,40,78,0,9,8,249,255,9,220,63,
+44,55,0,116,128,176,192,255,255,255,0,116,160,176,208,255,
+255,255,0,116,192,176,224,255,255,255,0,116,224,176,240,
+255,255,255,3,54,8,92,0,0,0,10,0,0,0,0,64,96,8,140,0,
+116,128,178,192,255,255,255,0,116,160,178,208,255,255,255,
+0,116,192,178,224,255,255,255,0,116,224,178,240,255,255,
+255,0,48,160,144,104,81,0,0,34,32,5,61,0,48,168,144,112,
+228,0,0,0,48,160,140,3,0,173,222,0,80,165,146,172,77,0,
+9,24,77,0,9,0,48,160,144,0,0,0,16,210,0,136,140,148,64,
+164,88,128,9,165,88,0,0,32,140,0,48,40,140,16,39,0,0,0,
+48,160,146,0,0,0,16,96,248,255,9,2,14,132,88,80,248,255,
+9,4,22,160,92,148,96,1,90,1,32,33,140,10,0,0,22,96,77,
+0,9,64,248,255,9,220,63,20,55,0,116,128,176,192,255,255,
+255,0,116,160,176,208,255,255,255,0,116,192,176,224,255,
+255,255,0,116,224,176,240,255,255,255,3,54,8,92,0,0,0,10,
+0,0,0,0,0,0,0,0,0,0,0,0,64,96,8,140,0,116,128,178,192,
+255,255,255,0,116,160,178,208,255,255,255,0,116,192,178,
+224,255,255,255,0,116,224,178,240,255,255,255,0,48,160,144,
+100,81,0,0,34,32,5,61,0,48,168,144,112,228,0,0,0,48,160,
+140,4,0,173,222,0,80,165,146,220,76,0,9,72,76,0,9,0,116,
+128,176,192,255,255,255,0,116,160,176,208,255,255,255,0,
+116,192,176,224,255,255,255,0,116,224,176,240,255,255,255,
+3,54,8,92,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,68,247,
+255,9,4,32,36,144,16,32,44,144,0,30,128,92,0,48,64,140,
+1,50,0,0,60,247,255,9,0,30,128,92,4,0,72,140,88,247,255,
+9,0,48,88,140,32,88,0,0,28,32,89,146,16,32,65,154,0,48,
+160,140,144,114,0,0,76,96,161,146,0,48,160,140,96,117,0,
+0,0,48,168,140,48,118,0,0,0,48,176,140,224,115,0,0,1,4,
+128,140,0,30,136,92,0,0,144,140,140,96,161,146,204,96,169,
+146,12,97,177,146,200,246,255,9,0,30,128,92,0,247,255,9,
+255,15,128,140,208,246,255,9,1,30,128,92,0,247,255,8,180,
+246,255,9,4,32,36,144,16,32,44,144,0,30,128,92,180,246,
+255,9,0,30,128,92,212,246,255,9,84,0,160,140,20,32,161,
+146,0,48,160,140,0,88,0,0,0,48,168,140,224,115,0,0,0,48,
+176,140,176,116,0,0,1,4,128,140,0,30,136,92,0,0,144,140,
+28,32,161,146,12,97,169,146,76,97,177,146,88,246,255,9,0,
+30,128,92,144,246,255,9,255,15,128,140,96,246,255,9,1,30,
+128,92,144,246,255,8,0,48,168,144,112,228,0,0,0,48,160,
+140,16,0,173,222,0,80,165,146,140,75,0,9,1,30,128,92,244,
+74,0,8,0,48,168,144,112,228,0,0,0,48,160,140,17,0,173,
+222,0,80,165,146,108,75,0,9,1,30,128,92,212,74,0,8,0,48,
+168,144,112,228,0,0,0,48,160,140,18,0,173,222,0,80,165,
+146,76,75,0,9,1,30,128,92,180,74,0,8,0,48,168,144,112,
+228,0,0,0,48,160,140,19,0,173,222,0,80,165,146,44,75,0,
+9,1,30,128,92,148,74,0,8,0,48,240,140,84,120,0,0,30,22,
+136,92,0,0,240,140,255,0,160,140,144,0,133,88,0,48,128,
+146,8,28,0,80,0,80,4,132,0,0,0,10,0,0,0,0,0,0,0,0,
+136,25,176,88,144,160,5,90,0,30,160,93,62,0,0,18,18,128,
+133,49,30,32,4,58,88,0,0,8,0,0,0,0,137,25,232,88,62,64,
+135,58,72,0,0,8,0,0,0,0,0,48,160,140,130,184,97,202,0,
+48,168,140,118,116,210,62,48,0,0,8,0,0,0,0,0,48,160,140,
+222,153,139,252,0,48,168,140,59,93,202,62,24,0,0,8,0,0,
+0,0,0,48,160,140,216,182,122,157,0,48,168,140,98,194,199,
+62,0,30,144,92,0,48,152,140,132,215,119,65,20,22,128,93,
+160,104,0,9,192,98,0,9,255,0,160,140,144,0,133,88,0,48,
+128,146,8,28,0,80,0,0,0,10,0,0,0,0,0,48,240,140,48,121,
+0,0,30,22,128,92,0,0,240,140,0,48,160,144,4,28,0,80,0,
+48,240,146,132,81,0,0,1,14,165,88,0,48,160,146,4,28,0,
+80,0,16,4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,48,
+160,144,132,81,0,0,10,32,5,61,116,73,0,9,0,48,168,144,4,
+28,0,80,137,73,173,88,10,96,85,48,138,73,173,88,0,48,160,
+144,124,81,0,0,0,48,168,146,4,28,0,80,20,80,160,89,0,48,
+160,146,124,81,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,
+30,22,32,92,0,48,128,144,140,81,0,0,6,222,136,89,176,70,
+0,9,16,22,160,92,0,48,160,144,0,0,0,16,210,0,168,140,
+148,64,165,88,128,9,173,88,0,48,168,146,0,0,0,16,0,48,
+160,144,140,81,0,0,20,16,175,89,0,48,168,146,116,228,0,0,
+0,48,160,144,140,81,0,0,32,32,173,140,0,48,168,146,120,
+228,0,0,0,48,160,144,140,81,0,0,48,32,173,140,0,48,168,
+146,112,228,0,0,0,48,160,144,140,81,0,0,44,32,173,140,0,
+48,168,146,124,228,0,0,1,30,160,92,0,48,160,146,128,228,
+0,0,0,48,160,144,140,81,0,0,0,48,168,140,0,184,0,0,40,
+32,173,146,0,48,160,144,140,81,0,0,0,48,168,140,80,45,3,
+0,36,32,173,146,0,48,160,144,140,81,0,0,0,48,168,140,12,
+1,4,0,52,32,173,146,0,48,160,144,140,81,0,0,0,48,168,
+144,144,81,0,0,16,96,181,144,16,142,173,89,56,32,173,146,
+0,0,0,10,0,0,0,0,16,72,8,89,0,144,39,140,0,48,160,144,
+144,81,0,0,0,48,168,140,237,254,17,206,8,32,173,146,0,48,
+168,144,112,228,0,0,0,48,160,144,112,228,0,0,0,48,168,
+144,112,228,0,0,0,80,181,144,1,137,173,89,0,16,173,146,0,
+16,165,144,0,48,160,144,140,81,0,0,64,32,173,144,127,0,
+176,140,149,128,165,88,148,48,0,90,222,0,0,18,148,112,0,
+90,14,0,0,18,216,0,0,8,0,0,0,0,0,48,128,144,140,81,0,
+0,40,12,0,9,16,22,160,92,64,224,167,146,0,48,160,144,140,
+81,0,0,64,224,175,144,129,73,181,88,68,32,181,146,0,48,
+160,144,140,81,0,0,64,32,173,144,135,25,136,88,149,64,164,
+88,148,48,0,90,96,0,0,18,0,48,160,144,116,228,0,0,0,16,
+173,144,149,48,0,90,76,0,0,18,0,48,160,144,120,228,0,0,
+0,48,168,144,120,228,0,0,0,80,181,144,22,80,168,89,0,16,
+173,146,0,48,160,144,96,81,0,0,20,80,168,89,0,80,165,140,
+0,48,160,146,96,81,0,0,255,0,168,140,148,64,165,88,136,9,
+173,88,0,48,168,146,4,0,0,32,0,48,160,144,140,81,0,0,0,
+30,168,92,64,32,173,146,64,224,167,144,148,176,0,90,8,0,
+0,21,0,0,0,10,16,0,0,8,0,0,0,0,8,0,0,8,0,0,0,0,16,
+0,0,8,0,0,0,0,16,0,0,8,0,0,0,0,204,254,255,8,0,0,0,
+0,0,0,0,10,0,0,0,0,16,72,8,89,0,144,167,140,0,48,168,
+144,68,12,0,80,128,73,181,88,0,48,176,146,68,12,0,80,0,
+48,168,144,68,12,0,80,0,78,181,88,0,48,176,146,68,12,0,
+80,0,48,168,144,68,12,0,80,2,78,181,88,0,48,176,146,68,
+12,0,80,0,30,168,92,0,48,168,146,72,16,0,80,1,30,168,92,
+0,48,168,146,68,16,0,80,0,30,168,92,64,224,175,146,64,
+224,175,144,21,240,0,90,14,0,0,22,128,0,0,8,0,0,0,0,64,
+224,175,144,21,22,176,92,22,57,168,140,0,0,0,0,0,48,176,
+140,16,16,0,80,21,128,173,89,0,0,176,140,21,128,173,89,0,
+80,181,140,15,30,168,92,0,144,173,146,64,224,175,144,21,
+22,176,92,22,57,168,140,0,0,0,0,0,48,176,140,0,16,0,80,
+21,128,173,89,0,0,176,140,21,128,173,89,0,80,181,140,0,
+30,168,92,0,144,173,146,64,224,183,144,22,80,168,89,0,80,
+181,140,64,224,183,146,124,255,255,8,0,0,0,0,0,48,168,
+144,4,28,0,80,130,73,181,88,0,48,176,146,4,28,0,80,0,48,
+168,144,68,20,0,80,1,78,181,88,0,48,176,146,68,20,0,80,
+244,1,168,140,0,48,168,146,48,20,0,80,0,48,168,144,68,20,
+0,80,135,73,181,88,0,48,176,146,68,20,0,80,0,48,168,144,
+68,20,0,80,134,73,181,88,0,48,176,146,68,20,0,80,0,48,
+168,144,68,20,0,80,4,78,181,88,0,48,176,146,68,20,0,80,
+0,48,168,144,68,20,0,80,5,78,181,88,0,48,176,146,68,20,
+0,80,0,0,0,10,0,0,0,0,0,0,0,0,16,72,8,89,0,144,39,
+140,0,30,160,92,0,48,160,146,0,0,0,16,0,48,160,144,0,0,
+0,16,210,0,168,140,148,64,165,88,129,9,173,88,0,48,168,
+146,0,0,0,16,0,48,160,144,0,0,0,16,208,0,168,140,148,64,
+165,88,0,48,160,146,0,0,0,16,0,48,160,144,0,0,0,16,210,
+0,168,140,148,64,165,88,128,9,173,88,0,48,168,146,0,0,0,
+16,0,48,160,144,0,0,0,16,210,0,168,140,148,64,165,88,40,
+0,168,140,148,67,165,88,0,48,160,146,0,0,0,16,10,30,128,
+92,128,39,0,11,16,22,160,92,0,48,160,144,4,28,0,80,131,
+9,173,88,0,48,168,146,4,28,0,80,31,216,132,89,96,39,0,
+11,16,22,160,92,0,48,160,144,4,28,0,80,11,222,168,89,148,
+67,165,88,0,48,168,140,56,24,0,0,148,96,5,90,16,0,0,18,
+1,30,128,92,140,68,0,9,16,22,160,92,0,48,160,144,68,12,
+0,80,148,48,0,90,16,0,0,18,1,30,128,92,112,68,0,9,16,
+22,160,92,0,48,160,144,68,20,0,80,8,222,168,89,148,67,
+165,88,8,222,168,89,148,96,5,90,16,0,0,18,1,30,128,92,
+72,68,0,9,16,22,160,92,0,48,160,144,68,12,0,80,129,9,
+173,88,0,48,168,146,68,12,0,80,0,48,160,144,68,12,0,80,
+128,9,173,88,0,48,168,146,68,12,0,80,0,48,160,144,68,20,
+0,80,130,9,173,88,0,48,168,146,68,20,0,80,0,48,160,144,
+68,20,0,80,128,9,173,88,0,48,168,146,68,20,0,80,0,48,
+160,144,68,20,0,80,10,14,173,88,0,48,168,146,68,20,0,80,
+0,48,160,144,4,28,0,80,133,9,173,88,0,48,168,146,4,28,0,
+80,0,48,160,144,4,28,0,80,132,9,173,88,0,48,168,146,4,
+28,0,80,0,48,160,144,4,28,0,80,3,14,173,88,0,48,168,146,
+4,28,0,80,0,48,128,140,176,30,4,0,64,38,0,11,16,22,160,
+92,0,0,160,140,64,224,167,146,64,224,167,144,20,240,0,90,
+10,0,0,22,164,0,0,8,64,224,167,144,20,22,168,92,21,57,
+160,140,0,0,0,0,0,48,168,140,0,12,0,144,20,64,165,89,0,
+0,176,140,20,128,173,89,0,80,165,140,64,224,175,144,21,22,
+176,92,10,142,173,89,0,48,176,144,136,81,0,0,22,64,173,
+89,0,16,173,146,64,224,167,144,20,22,168,92,21,57,160,140,
+0,0,0,0,0,48,168,140,16,12,0,144,20,64,165,89,0,0,176,
+140,20,128,173,89,0,80,165,140,64,224,175,144,21,22,176,
+92,10,142,173,89,0,48,176,144,136,81,0,0,22,64,173,89,0,
+16,173,146,64,224,175,144,21,80,160,89,0,16,173,140,64,
+224,175,146,88,255,255,8,0,0,0,0,0,48,160,144,128,228,0,
+0,148,112,0,90,108,0,0,21,0,48,160,144,136,81,0,0,141,
+25,168,88,20,64,165,89,0,48,160,146,0,20,0,144,0,48,160,
+144,136,81,0,0,141,25,168,88,20,64,165,89,0,48,160,146,
+16,20,0,144,0,48,160,144,136,81,0,0,140,25,168,88,20,64,
+165,89,0,48,160,146,0,24,0,144,0,48,160,144,136,81,0,0,
+140,25,168,88,20,64,165,89,0,48,160,146,16,24,0,144,104,
+1,0,8,0,0,0,0,0,30,160,92,64,224,167,146,64,224,167,144,
+20,240,0,90,10,0,0,22,76,1,0,8,64,224,167,144,20,22,168,
+92,21,57,160,140,0,0,0,0,0,48,168,140,0,20,0,144,20,64,
+165,89,0,0,176,140,20,128,173,89,0,80,165,140,64,224,175,
+144,11,222,176,89,149,128,173,112,141,25,176,88,21,128,173,
+89,0,48,176,144,136,81,0,0,22,64,173,89,0,16,173,146,64,
+224,167,144,20,22,168,92,21,57,160,140,0,0,0,0,0,48,168,
+140,16,20,0,144,20,64,165,89,0,0,176,140,20,128,173,89,0,
+80,165,140,64,224,175,144,11,222,176,89,149,128,173,112,
+141,25,176,88,21,128,173,89,0,48,176,144,136,81,0,0,22,
+64,173,89,0,16,173,146,64,224,167,144,20,22,168,92,21,57,
+160,140,0,0,0,0,0,48,168,140,0,24,0,144,20,64,165,89,0,
+0,176,140,20,128,173,89,0,80,165,140,64,224,175,144,21,22,
+176,92,10,142,173,89,140,25,176,88,21,128,173,89,0,48,176,
+144,136,81,0,0,22,64,173,89,0,16,173,146,64,224,167,144,
+20,22,168,92,21,57,160,140,0,0,0,0,0,48,168,140,16,24,0,
+144,20,64,165,89,0,0,176,140,20,128,173,89,0,80,165,140,
+64,224,175,144,21,22,176,92,10,142,173,89,140,25,176,88,
+21,128,173,89,0,48,176,144,136,81,0,0,22,64,173,89,0,16,
+173,146,64,224,175,144,21,80,160,89,0,16,173,140,64,224,
+175,146,176,254,255,8,0,0,0,0,1,30,160,92,0,48,160,146,
+68,16,0,80,0,30,160,92,64,224,167,146,64,224,167,144,20,
+240,0,90,14,0,0,22,128,0,0,8,0,0,0,0,64,224,167,144,20,
+22,168,92,21,57,160,140,0,0,0,0,0,48,168,140,16,16,0,80,
+20,64,165,89,0,0,168,140,20,64,165,89,0,16,173,140,15,30,
+160,92,0,80,165,146,64,224,167,144,20,22,168,92,21,57,160,
+140,0,0,0,0,0,48,168,140,0,16,0,80,20,64,165,89,0,0,
+168,140,20,64,165,89,0,16,173,140,0,30,160,92,0,80,165,
+146,64,224,175,144,21,80,160,89,0,16,173,140,64,224,175,
+146,124,255,255,8,0,0,0,0,0,48,160,144,68,12,0,80,1,14,
+173,88,0,48,168,146,68,12,0,80,0,48,160,144,68,12,0,80,
+0,14,173,88,0,48,168,146,68,12,0,80,0,48,160,144,68,20,
+0,80,2,14,173,88,0,48,168,146,68,20,0,80,0,48,160,144,
+68,20,0,80,0,14,173,88,0,48,168,146,68,20,0,80,0,48,160,
+144,68,20,0,80,138,9,173,88,0,48,168,146,68,20,0,80,0,
+48,160,144,4,28,0,80,5,14,173,88,0,48,168,146,4,28,0,80,
+0,48,160,144,4,28,0,80,4,14,173,88,0,48,168,146,4,28,0,
+80,0,48,160,144,68,20,0,80,147,9,173,88,0,48,168,146,68,
+20,0,80,0,30,128,92,0,0,0,10,0,0,0,10,0,0,0,0,30,22,
+160,92,0,0,168,140,0,48,168,146,68,16,0,80,31,30,168,92,
+0,48,168,146,72,16,0,80,0,48,168,128,108,228,0,0,0,48,
+168,146,0,16,0,80,1,30,168,92,0,48,168,146,16,16,0,80,0,
+48,168,128,108,228,0,0,0,48,168,146,4,16,0,80,1,30,168,
+92,0,48,168,146,20,16,0,80,0,48,168,128,108,228,0,0,0,
+48,168,146,8,16,0,80,1,30,168,92,0,48,168,146,24,16,0,
+80,0,48,168,128,108,228,0,0,0,48,168,146,12,16,0,80,1,
+30,168,92,0,48,168,146,28,16,0,80,0,0,0,10,0,16,188,144,
+0,16,140,144,4,32,164,144,0,48,176,144,244,2,0,0,0,48,
+144,144,248,2,0,0,0,48,152,144,252,2,0,0,0,48,168,144,
+240,2,0,0,0,48,48,140,0,255,0,0,4,14,165,89,148,128,181,
+88,148,128,164,88,19,4,165,89,150,3,181,88,150,64,173,88,
+149,48,0,90,151,128,185,88,8,204,237,89,0,48,48,140,0,0,
+255,0,145,128,137,88,16,76,140,89,18,0,0,18,10,30,128,92,
+0,0,0,10,0,0,0,0,145,48,0,90,4,140,181,89,18,0,0,18,
+118,96,12,58,228,255,255,8,0,0,0,0,0,48,160,144,144,3,0,
+0,214,63,5,58,0,48,160,144,96,3,0,0,26,32,5,58,0,48,
+160,144,144,228,0,0,34,32,5,61,10,160,253,52,26,160,117,
+61,0,48,136,140,224,92,0,0,0,48,184,140,128,3,0,0,20,0,
+0,8,0,48,184,140,80,3,0,0,0,48,136,144,80,228,0,0,0,48,
+144,140,224,92,0,0,0,48,152,140,128,3,0,0,104,0,0,8,0,
+0,0,0,0,48,160,144,48,228,0,0,110,63,5,58,0,48,160,144,
+192,3,0,0,26,32,5,58,0,48,160,144,144,228,0,0,34,32,13,
+61,10,160,253,52,26,160,117,61,0,48,136,140,128,94,0,0,0,
+48,184,140,32,228,0,0,20,0,0,8,0,48,184,140,176,3,0,0,
+0,48,136,144,84,228,0,0,0,48,144,140,128,94,0,0,0,48,
+152,140,32,228,0,0,0,48,168,144,160,228,0,0,157,48,1,90,
+7,142,165,89,20,64,37,89,166,0,0,18,18,96,39,60,22,96,7,
+58,244,254,255,8,0,0,0,0,210,96,47,58,232,254,255,8,8,
+32,164,144,31,88,132,89,144,5,173,112,10,96,5,61,1,30,
+168,92,16,32,161,144,0,48,48,140,0,183,0,0,148,160,1,90,
+84,32,169,146,50,0,0,18,4,16,45,89,0,80,137,144,30,64,
+141,60,145,0,148,112,0,16,129,140,2,30,152,92,68,229,255,
+9,0,80,241,146,36,32,241,146,2,30,128,92,0,0,0,10,60,32,
+137,146,64,32,145,146,20,224,165,144,144,5,165,116,44,32,
+161,146,0,48,168,140,176,183,0,0,20,224,164,144,16,32,169,
+146,144,5,165,116,0,16,148,140,132,0,0,8,16,32,161,144,0,
+48,48,140,0,183,0,0,82,158,161,61,60,32,137,146,64,32,
+145,146,20,224,165,144,31,88,147,89,146,5,165,116,44,32,
+161,146,20,224,164,144,146,5,165,116,0,48,168,140,128,183,
+0,0,4,30,40,92,64,0,0,8,16,32,161,144,0,48,48,140,0,
+183,0,0,18,158,161,61,64,32,153,146,60,32,185,146,20,224,
+165,144,31,88,148,89,146,5,165,116,44,32,161,146,20,224,
+164,144,146,5,165,116,0,48,168,140,80,178,0,0,0,30,40,92,
+16,32,169,146,48,32,161,146,44,32,129,144,48,32,137,140,0,
+80,164,176,146,0,132,112,88,32,233,146,5,1,132,89,0,16,
+172,140,146,0,149,112,4,96,132,146,18,22,176,92,2,0,128,
+140,0,80,164,146,8,96,148,146,12,96,188,146,0,0,0,10,4,
+32,164,144,0,48,176,144,244,2,0,0,0,48,184,144,248,2,0,
+0,0,48,128,144,252,2,0,0,0,48,168,144,240,2,0,0,4,14,
+165,89,148,128,181,88,148,192,165,88,16,4,165,89,150,3,
+181,88,150,64,173,88,14,96,5,58,10,30,128,92,0,0,0,10,0,
+48,160,144,160,228,0,0,4,140,181,89,7,142,173,89,21,0,37,
+89,16,32,161,144,0,48,40,140,0,183,0,0,18,64,161,61,10,
+30,128,92,0,0,0,10,0,0,0,0,20,32,137,144,22,96,4,59,4,
+22,128,92,0,0,144,140,8,30,152,92,180,227,255,9,4,22,128,
+92,135,25,136,88,72,57,0,9,159,25,160,88,16,32,41,146,2,
+30,128,92,68,32,161,146,0,0,0,10,96,32,180,144,96,32,172,
+144,64,32,84,140,16,22,72,92,149,57,168,140,32,0,0,0,149,
+208,167,88,148,48,0,90,0,48,176,146,148,228,0,0,0,48,168,
+146,152,228,0,0,18,0,0,18,8,30,128,92,0,0,0,10,0,0,0,
+0,92,32,164,144,3,14,189,89,32,224,173,140,149,208,167,88,
+148,48,0,90,0,48,168,146,140,228,0,0,14,0,0,18,8,30,128,
+92,0,0,0,10,88,32,164,144,148,208,160,88,18,32,5,58,8,
+30,128,92,0,0,0,10,0,0,0,0,80,32,148,144,84,32,172,144,
+88,32,180,144,88,32,156,144,88,32,236,144,112,32,36,144,
+120,32,44,144,128,32,52,144,136,32,60,144,144,32,68,144,
+31,88,98,89,146,0,147,112,40,224,165,140,148,128,165,112,
+152,32,180,144,160,32,188,144,168,32,140,144,149,157,172,
+140,21,0,173,89,19,93,157,140,29,221,236,140,132,93,39,
+140,133,29,41,140,134,93,49,140,135,157,57,140,136,221,65,
+140,150,29,178,140,0,48,88,140,79,205,3,0,0,48,96,140,80,
+45,3,0,151,157,189,140,12,193,162,89,145,221,141,140,18,0,
+141,54,8,30,128,92,0,0,0,10,0,0,0,0,184,0,0,9,14,32,
+68,61,8,30,128,92,0,0,0,10,0,80,130,140,36,1,0,9,18,32,
+68,61,8,30,128,92,0,0,0,10,0,0,0,0,0,80,130,140,140,1,
+0,9,18,32,68,61,8,30,128,92,0,0,0,10,0,0,0,0,0,80,130,
+140,212,214,255,9,18,32,68,61,8,30,128,92,0,0,0,10,0,0,
+0,0,88,96,130,144,0,48,136,144,140,228,0,0,4,221,255,9,
+18,32,68,61,8,30,128,92,0,0,0,10,0,0,0,0,0,144,130,140,
+252,10,0,9,18,32,68,61,8,30,128,92,0,0,0,10,0,0,0,0,0,
+0,128,140,180,231,255,9,36,160,162,144,2,30,128,92,0,48,
+160,146,144,228,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,
+16,22,32,92,80,32,161,144,5,14,133,89,0,48,160,146,156,
+228,0,0,252,20,0,9,144,48,0,90,0,48,128,146,8,228,0,0,
+14,0,0,21,8,30,128,92,0,0,0,10,0,48,176,144,156,228,0,
+0,0,16,129,146,0,48,128,146,16,228,0,0,0,30,168,92,5,
+142,165,89,149,160,5,90,16,60,165,140,224,255,255,255,0,
+48,160,146,12,228,0,0,26,0,0,19,21,80,168,89,0,16,244,
+146,149,160,5,90,32,32,132,140,240,255,255,20,2,30,128,92,
+0,0,0,10,16,22,40,92,88,96,33,144,3,14,49,89,0,144,129,
+140,128,20,0,9,144,48,0,90,0,48,128,146,16,3,0,0,18,0,
+0,21,8,30,128,92,0,0,0,10,0,0,0,0,8,96,129,146,0,30,
+168,92,0,48,128,146,24,3,0,0,149,32,1,90,16,188,161,140,
+248,255,255,255,0,48,160,146,20,3,0,0,26,0,0,19,21,80,
+168,89,0,16,244,146,149,32,1,90,8,32,132,140,240,255,255,
+20,2,30,128,92,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,16,
+22,40,92,84,96,33,144,3,14,49,89,0,144,129,140,0,20,0,9,
+144,48,0,90,0,48,128,146,32,3,0,0,18,0,0,21,8,30,128,
+92,0,0,0,10,0,0,0,0,4,96,129,146,0,30,168,92,0,48,128,
+146,28,3,0,0,149,32,1,90,16,188,161,140,248,255,255,255,
+0,48,160,146,36,3,0,0,26,0,0,19,21,80,168,89,0,16,244,
+146,149,32,1,90,8,32,132,140,240,255,255,20,2,30,128,92,
+0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,17,22,32,92,132,57,
+48,140,0,0,0,0,16,22,40,92,0,144,129,140,124,19,0,9,144,
+48,0,90,28,96,129,146,18,0,0,21,8,30,128,92,0,0,0,10,0,
+0,0,0,16,188,169,140,248,255,255,255,32,96,161,140,0,30,
+176,92,0,16,133,146,150,32,1,90,4,32,173,146,22,0,0,19,
+28,96,161,144,150,29,245,146,22,80,176,89,244,31,177,60,2,
+30,128,92,0,0,0,10,0,0,0,0,0,0,0,0,0,48,240,140,76,
+139,0,0,30,22,144,92,0,0,240,140,1,9,132,89,1,25,184,89,
+82,192,133,58,8,78,140,89,0,0,168,140,16,81,160,88,21,
+124,180,140,80,8,0,64,148,48,0,90,21,124,164,140,96,8,0,
+64,26,0,0,18,1,9,132,89,144,224,5,90,0,144,245,146,0,16,
+245,146,26,0,0,18,2,9,132,89,144,224,5,90,0,144,245,146,
+0,16,245,146,240,255,255,21,0,144,4,132,0,0,0,10,20,22,
+96,92,48,0,24,140,131,133,164,116,0,144,85,140,0,48,176,
+144,32,86,0,0,19,22,88,92,8,206,138,89,0,0,184,140,21,
+22,104,92,23,124,172,140,64,8,0,64,20,128,165,89,0,48,
+160,146,32,86,0,0,0,80,133,146,0,144,162,152,18,208,144,
+89,0,0,32,140,140,48,0,90,23,124,52,140,64,0,0,112,130,
+141,76,89,0,0,128,140,20,22,56,92,130,77,45,89,210,0,0,
+21,198,96,2,59,2,222,71,89,0,48,232,140,48,0,0,32,0,48,
+152,140,96,0,0,32,0,48,144,140,4,0,0,32,0,48,136,140,8,
+0,0,16,22,96,1,61,10,16,82,89,0,144,162,152,130,77,45,
+89,0,16,61,140,4,1,162,89,133,32,5,90,0,80,177,140,10,0,
+0,22,20,22,176,92,0,80,63,146,0,48,160,144,96,81,0,0,4,
+128,37,89,255,0,168,140,132,240,2,90,22,221,57,140,22,65,
+74,89,0,208,180,146,22,65,41,89,1,32,165,140,148,64,173,
+88,0,48,160,146,96,81,0,0,0,144,172,146,54,0,0,22,0,80,
+164,128,42,32,93,51,0,48,128,140,8,0,0,16,0,144,161,176,
+0,144,161,176,0,144,161,176,0,16,164,128,20,240,2,90,12,
+9,33,89,232,255,255,17,212,63,89,60,106,127,2,60,0,0,0,
+10,0,0,0,0,214,96,2,59,0,48,120,140,4,0,0,32,0,48,112,
+140,8,0,0,16,22,96,1,61,10,16,82,89,0,144,162,152,130,
+77,45,89,0,16,61,140,2,222,31,89,4,193,160,89,133,32,5,
+90,0,80,177,140,10,0,0,22,20,22,176,92,0,48,56,146,48,0,
+0,32,0,48,160,144,96,81,0,0,4,128,37,89,255,0,168,140,
+132,240,2,90,0,48,176,146,96,0,0,32,22,65,74,89,22,221,
+57,140,22,65,41,89,1,32,165,140,148,64,173,88,0,48,160,
+146,96,81,0,0,0,208,171,146,78,0,0,22,0,144,163,128,66,
+32,93,51,0,48,64,140,8,0,0,16,16,80,128,89,0,144,161,
+176,144,32,3,90,0,144,161,176,12,9,33,89,0,144,161,176,
+22,0,0,21,13,22,128,92,0,208,138,140,164,253,255,11,0,30,
+128,92,0,16,162,128,208,63,93,52,188,63,89,60,68,127,2,
+60,13,22,128,92,0,208,138,140,124,253,255,8,0,0,0,0,0,0,
+0,0,0,48,136,146,64,0,0,32,8,14,172,89,0,0,184,140,23,
+124,181,140,240,4,0,96,0,144,37,176,17,16,164,89,0,48,
+160,146,64,0,0,32,0,144,37,176,32,96,132,140,23,124,173,
+140,252,4,0,96,0,48,128,146,56,0,0,32,16,16,129,89,0,80,
+165,144,0,48,128,146,56,0,0,32,16,16,129,89,0,80,165,144,
+0,0,0,10,0,0,0,0,0,48,240,140,12,142,0,0,30,22,144,92,
+0,0,240,140,8,14,132,89,0,0,168,140,0,52,132,140,192,4,
+0,64,0,16,164,144,21,80,168,89,149,176,2,90,0,80,164,146,
+17,16,137,89,236,255,255,22,0,144,4,132,0,0,0,10,0,48,
+240,140,76,142,0,0,30,22,144,92,0,0,240,140,8,14,132,89,
+0,0,168,140,0,52,132,140,192,4,0,64,0,16,164,144,21,80,
+168,89,149,176,2,90,0,80,164,146,17,16,137,89,236,255,255,
+22,0,144,4,132,0,0,0,10,0,48,240,140,132,142,0,0,30,22,
+136,92,0,0,240,140,8,14,132,89,0,52,132,140,192,4,0,64,
+0,16,164,176,0,16,164,176,0,16,164,144,0,16,164,144,0,16,
+164,144,0,80,4,132,0,0,0,10,0,0,0,0,0,0,0,0,112,96,8,
+140,160,224,199,146,16,22,56,92,32,224,161,176,18,22,120,
+92,0,0,104,140,20,224,33,144,13,224,3,90,68,224,41,144,
+17,22,112,92,0,48,192,140,192,4,0,64,22,22,72,92,0,80,
+69,140,198,4,0,19,0,48,88,140,64,86,0,0,4,201,98,89,0,
+48,24,140,76,20,0,80,0,16,166,144,0,48,80,140,255,255,0,
+0,148,128,50,88,170,64,49,61,74,32,2,61,210,5,232,140,22,
+64,39,54,0,30,128,92,84,255,255,11,244,1,0,8,0,0,0,0,
+64,224,137,144,7,22,128,92,0,80,4,134,144,112,0,90,0,16,
+76,140,22,0,0,21,0,30,128,92,44,255,255,11,204,1,0,8,0,
+0,0,0,48,224,65,144,0,48,72,146,68,0,0,32,0,48,160,176,
+240,4,0,96,0,48,160,176,240,4,0,96,44,96,74,140,137,48,
+0,90,1,32,33,140,1,9,66,89,0,48,160,144,252,4,0,96,0,
+48,160,144,252,4,0,96,0,48,160,144,252,4,0,96,130,1,0,
+18,0,48,232,140,255,63,0,0,0,100,161,140,148,64,47,88,
+244,3,0,8,0,0,0,0,14,140,161,89,94,32,13,58,18,32,13,
+49,174,33,21,58,146,34,29,58,196,3,0,8,0,30,128,92,164,
+254,255,11,34,32,1,58,7,22,128,92,0,16,137,140,0,30,144,
+92,8,0,152,140,228,218,255,9,20,0,0,8,0,0,0,0,0,208,
+162,144,20,80,160,89,0,208,162,146,159,25,40,88,0,0,64,
+140,0,30,32,92,148,3,0,8,0,0,0,0,34,32,1,61,0,30,128,
+92,88,254,255,11,0,208,162,144,20,80,160,89,159,25,40,88,
+0,0,64,140,108,3,0,8,142,73,161,88,134,32,5,90,1,32,33,
+140,38,0,0,18,7,22,128,92,0,16,137,140,0,30,144,92,8,0,
+152,140,120,218,255,9,0,30,128,92,24,254,255,11,196,1,0,
+8,0,30,128,92,64,224,143,140,136,253,255,11,0,208,128,144,
+28,12,164,89,20,253,151,144,60,0,0,0,72,224,169,144,0,48,
+232,140,0,0,255,0,146,64,167,88,149,32,5,90,26,12,44,89,
+50,0,0,21,146,208,160,88,44,0,232,140,132,64,175,112,20,
+57,160,144,160,81,0,0,4,73,41,89,146,128,50,88,6,0,165,
+89,5,124,173,140,208,255,255,255,58,64,165,58,0,16,163,
+144,7,22,128,92,0,0,144,140,8,30,152,92,1,32,165,140,4,
+22,136,92,0,0,32,140,0,30,64,92,159,25,40,88,0,16,163,
+146,220,217,255,9,172,2,0,8,0,0,0,0,66,32,2,61,60,224,
+137,144,7,22,128,92,0,80,4,134,144,112,0,90,0,16,76,140,
+42,0,0,21,7,22,128,92,0,16,137,140,0,30,144,92,8,0,152,
+140,164,217,255,9,0,30,32,92,159,25,40,88,108,2,0,8,0,0,
+0,0,26,96,1,58,5,208,128,89,0,80,138,140,2,12,132,89,64,
+224,151,140,60,222,255,9,7,22,128,92,0,16,137,140,6,22,
+144,92,2,0,152,140,188,0,0,8,0,0,0,0,26,32,1,58,0,30,
+128,92,0,253,255,11,7,22,128,92,0,16,137,140,152,0,0,8,
+7,22,128,92,0,144,139,140,128,213,255,9,30,32,4,61,0,30,
+128,92,220,252,255,11,0,208,162,144,20,80,160,89,159,25,
+40,88,244,1,0,8,60,224,137,144,7,22,128,92,0,80,4,134,
+144,112,0,90,0,16,76,140,38,0,0,21,24,224,129,144,52,214,
+255,9,0,30,128,92,164,252,255,11,0,208,162,144,20,80,160,
+89,159,25,40,88,188,1,0,8,0,16,166,144,0,30,128,92,0,80,
+138,140,0,48,232,140,0,0,255,0,148,64,167,88,72,224,161,
+146,140,251,255,9,144,48,0,90,0,16,76,140,42,0,0,21,7,
+22,128,92,0,0,136,140,0,30,144,92,8,0,152,140,172,216,
+255,9,0,30,32,92,0,0,64,140,159,25,40,88,112,1,0,8,44,
+224,169,144,0,48,232,140,255,63,0,0,0,164,161,140,148,64,
+47,88,1,0,32,140,1,73,69,89,80,1,0,8,38,32,1,58,7,22,
+128,92,0,0,144,140,8,30,152,92,0,16,137,140,0,30,32,92,
+0,0,64,140,159,25,40,88,88,216,255,9,0,30,128,92,112,224,
+143,140,180,251,255,11,0,208,128,144,26,12,52,89,22,161,1,
+58,28,12,164,89,20,253,151,144,108,0,0,0,112,224,167,144,
+0,48,168,140,0,0,255,0,146,64,181,88,148,64,165,88,34,0,
+181,61,146,208,160,88,20,57,176,144,160,81,0,0,146,128,34,
+88,8,137,169,89,4,128,165,89,34,64,165,58,0,16,163,144,
+20,80,160,89,0,0,32,140,0,30,64,92,159,25,40,88,0,16,
+163,146,184,0,0,8,22,16,162,89,0,208,129,140,14,22,136,
+92,20,129,49,89,16,212,255,9,30,32,4,61,0,208,162,144,20,
+80,160,89,0,0,32,140,0,30,64,92,159,25,40,88,132,0,0,8,
+60,224,137,144,7,22,128,92,0,80,4,134,144,112,0,90,0,16,
+76,140,46,0,0,21,0,208,162,144,24,224,129,144,20,80,160,
+89,0,0,32,140,0,30,64,92,159,25,40,88,0,208,162,146,172,
+212,255,9,76,0,0,8,0,0,0,0,6,208,128,89,0,80,138,140,2,
+12,132,89,116,224,151,140,32,220,255,9,7,22,128,92,1,0,
+136,140,4,22,144,92,2,0,152,140,76,215,255,9,0,30,32,92,
+24,0,0,8,0,30,128,92,228,250,255,11,0,208,162,144,20,80,
+160,89,0,208,162,146,13,80,104,89,88,219,107,52,20,224,33,
+146,40,224,73,146,36,224,65,146,68,224,41,146,160,224,199,
+144,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,48,160,140,80,
+237,0,0,148,48,0,90,12,32,36,144,0,48,160,146,160,228,0,
+0,18,0,0,21,8,30,128,92,0,0,0,10,0,0,0,0,0,16,133,140,
+7,14,137,89,112,44,0,9,0,30,168,92,149,32,1,90,1,9,161,
+89,0,48,240,146,136,228,0,0,0,48,160,146,132,228,0,0,94,
+0,0,19,0,48,176,144,160,228,0,0,0,47,1,90,0,48,128,140,
+0,183,0,0,159,25,184,88,0,144,165,140,30,0,0,16,16,160,
+133,146,1,30,168,92,68,160,189,146,149,32,1,90,128,160,
+165,140,38,0,0,19,16,32,133,146,68,32,189,146,144,32,133,
+146,21,144,168,89,196,32,189,146,149,32,1,90,0,33,165,140,
+228,255,255,20,2,30,128,92,0,0,0,10,0,0,0,0,0,0,0,0,0,
+0,0,0,92,32,164,144,17,22,80,92,138,32,5,90,0,16,36,140,
+18,22,56,92,40,32,52,144,46,0,0,18,16,16,45,89,0,80,137,
+144,30,96,4,59,31,88,236,89,145,64,151,112,2,0,152,140,
+12,214,255,9,0,80,241,146,36,32,241,146,92,32,81,146,230,
+225,1,59,4,16,77,89,36,32,65,140,36,32,161,144,222,32,5,
+61,20,32,161,144,182,32,5,61,4,22,128,92,0,144,138,140,
+24,210,255,9,198,33,4,58,84,32,169,144,44,32,161,144,26,
+0,173,62,64,32,137,144,4,22,128,92,0,80,4,134,48,32,161,
+144,20,0,0,8,60,32,137,144,4,22,128,92,0,80,4,134,44,32,
+161,144,16,22,48,92,36,32,161,146,136,32,12,61,0,48,160,
+144,40,86,0,0,24,32,129,144,7,0,165,89,0,48,160,146,40,
+86,0,0,172,210,255,9,135,16,139,112,0,0,128,140,40,19,0,
+11,36,32,241,146,0,0,0,10,0,0,0,0,20,32,137,144,4,22,
+128,92,0,0,144,140,8,30,152,92,80,213,255,9,0,30,128,92,
+133,16,139,112,0,0,40,140,248,18,0,11,0,16,242,146,20,32,
+241,146,188,0,0,8,64,32,137,144,4,22,128,92,0,80,4,134,
+144,112,0,90,0,16,52,140,46,1,0,18,48,32,161,144,36,32,
+161,146,20,32,177,144,84,32,169,144,7,128,165,89,148,96,5,
+90,0,208,41,140,10,0,0,20,22,65,45,89,36,32,161,144,133,
+32,5,90,5,193,57,89,86,0,0,22,36,32,137,144,0,30,128,92,
+0,144,145,140,80,32,153,140,196,15,0,9,0,16,162,144,20,
+32,169,144,64,32,137,144,4,22,128,92,21,0,173,89,20,65,
+41,89,20,32,169,146,0,80,4,134,144,112,0,90,0,16,52,140,
+74,255,255,18,48,32,161,144,133,32,5,90,0,16,162,146,180,
+255,255,17,30,96,1,58,0,30,128,92,0,80,137,140,6,22,144,
+92,80,32,153,140,112,15,0,9,16,22,48,92,0,80,162,144,84,
+32,169,144,5,0,141,89,145,96,5,90,0,80,138,146,38,0,0,
+21,31,88,236,89,145,64,151,112,0,16,129,140,2,30,152,92,
+80,212,255,9,0,80,242,146,36,32,241,146,40,0,0,8,32,32,
+161,140,0,16,133,176,8,32,53,146,5,65,172,89,0,80,141,
+140,6,22,144,92,0,16,133,146,4,32,173,146,12,32,157,146,
+44,254,1,60,0,0,0,10,0,0,0,0,0,48,160,144,40,86,0,0,
+135,16,139,112,0,0,128,140,7,0,165,89,0,48,160,146,40,86,
+0,0,164,17,0,8,0,48,160,144,40,86,0,0,135,16,139,112,0,
+0,128,140,7,0,165,89,0,48,160,146,40,86,0,0,140,17,0,11,
+20,32,137,144,4,22,128,92,0,0,144,140,2,30,152,92,192,
+211,255,9,20,32,241,146,0,0,0,10,0,30,32,92,0,16,36,146,
+132,73,140,89,16,16,129,89,244,127,36,62,0,0,0,10,0,0,0,
+0,0,0,0,0,0,0,0,0,80,96,8,140,112,224,199,178,128,224,
+231,146,21,22,216,92,3,160,172,140,21,210,232,88,0,144,
+101,140,31,88,115,89,51,96,183,140,142,133,181,112,0,48,
+168,144,68,86,0,0,0,48,184,144,48,86,0,0,21,80,168,89,0,
+48,168,146,68,86,0,0,22,192,181,89,0,48,176,146,48,86,0,
+0,4,32,171,144,19,22,200,92,0,16,213,140,0,30,24,92,8,
+96,127,140,2,222,63,89,64,224,151,146,130,77,95,89,0,16,
+83,144,130,77,77,89,137,224,1,90,0,80,138,140,10,0,0,22,
+7,22,136,92,0,48,80,146,48,0,0,32,0,48,136,146,96,0,0,
+32,0,48,176,144,96,81,0,0,0,48,184,144,176,81,0,0,0,48,
+40,140,48,0,0,32,0,48,168,144,180,81,0,0,17,22,64,92,0,
+48,104,140,4,0,0,32,255,0,32,140,0,48,224,140,0,0,255,
+255,255,3,192,140,48,96,49,140,15,160,3,90,17,157,82,140,
+17,193,90,89,1,160,181,140,17,65,74,89,0,48,176,146,96,
+81,0,0,150,0,177,88,1,224,189,140,149,0,175,88,0,48,224,
+140,252,255,255,0,149,67,175,88,0,48,176,146,4,0,0,32,
+151,0,190,88,0,116,173,140,0,0,1,0,149,0,183,88,0,48,
+192,140,0,0,255,0,149,0,198,88,0,48,184,146,176,81,0,0,
+80,224,199,146,0,48,176,146,180,81,0,0,38,1,0,17,139,48,
+0,90,17,192,66,89,70,0,0,22,12,16,98,89,4,32,163,144,0,
+16,83,144,0,48,168,144,96,81,0,0,0,80,81,146,130,13,77,
+89,1,96,173,140,9,193,90,89,0,144,73,146,149,0,161,88,0,
+48,168,146,96,81,0,0,139,48,0,90,0,80,163,146,196,255,
+255,17,8,206,180,89,0,180,133,146,64,8,0,64,0,48,160,144,
+176,81,0,0,0,48,168,144,180,81,0,0,14,222,224,89,148,3,
+167,88,0,48,160,146,76,12,0,80,0,180,173,146,192,0,0,64,
+0,48,160,128,8,0,0,16,148,32,2,90,0,48,176,140,8,0,0,
+16,255,0,168,140,18,0,0,18,0,144,165,128,148,64,165,88,
+248,31,162,61,30,32,2,58,8,206,164,89,0,52,173,140,192,0,
+0,112,129,11,66,90,0,80,165,144,248,255,255,21,80,224,231,
+144,31,88,195,89,15,1,166,89,0,0,168,140,2,12,181,89,8,
+206,164,89,21,160,5,90,0,52,165,140,192,0,0,64,156,131,
+148,88,0,16,149,146,22,0,0,19,21,80,168,89,21,160,5,90,
+0,16,245,146,244,255,255,20,8,206,156,89,26,206,163,89,0,
+244,164,146,192,0,0,64,112,224,199,176,128,224,231,144,0,
+0,0,10,102,96,76,60,12,16,98,89,4,32,171,144,8,193,177,
+89,0,16,83,144,130,77,77,89,137,160,5,90,0,80,138,140,10,
+0,0,22,22,22,136,92,0,80,81,146,0,48,176,144,96,81,0,0,
+8,64,68,89,136,112,2,90,17,157,82,140,17,65,74,89,0,144,
+137,146,17,193,90,89,1,160,181,140,150,0,169,88,0,48,176,
+146,96,81,0,0,0,80,171,146,164,255,255,22,0,48,184,144,
+176,81,0,0,8,206,180,89,0,180,133,146,64,8,0,64,0,48,
+168,144,180,81,0,0,143,201,109,88,0,48,104,146,76,12,0,
+80,0,180,173,146,192,0,0,64,0,48,168,128,8,0,0,16,21,
+112,2,90,0,48,184,140,8,0,0,16,255,0,176,140,18,0,0,17,
+0,208,173,128,149,128,173,88,248,127,77,51,8,206,180,89,0,
+0,184,140,23,188,173,140,192,0,0,112,0,80,37,176,0,80,37,
+176,0,80,133,152,0,48,168,140,0,0,0,176,23,188,181,140,
+192,0,0,64,148,48,0,90,0,144,173,146,10,9,66,89,0,244,
+123,140,212,255,255,255,38,1,0,21,14,224,2,61,31,88,195,
+89,106,2,126,54,118,224,2,59,22,96,2,61,12,16,98,89,4,
+32,163,144,0,16,83,144,130,13,77,89,2,222,231,89,8,1,167,
+89,137,32,5,90,0,80,138,140,10,0,0,22,20,22,136,92,0,48,
+80,146,48,0,0,32,0,48,160,144,96,81,0,0,255,0,168,140,
+17,65,74,89,0,48,136,146,96,0,0,32,8,64,68,89,17,157,82,
+140,17,193,90,89,1,32,165,140,148,64,173,88,0,48,160,146,
+96,81,0,0,0,48,168,146,4,0,0,32,146,32,82,59,8,206,236,
+89,0,48,32,140,8,0,0,16,255,0,144,140,0,48,56,140,255,
+63,0,0,0,48,48,140,76,12,0,80,0,116,47,140,192,0,0,112,
+0,16,161,128,148,128,164,88,90,32,85,51,0,30,160,92,5,0,
+133,89,20,124,143,140,192,0,0,64,0,100,163,140,148,192,
+105,88,0,144,105,146,0,16,164,176,0,16,164,176,0,16,164,
+152,0,16,164,144,0,48,176,140,0,0,0,176,0,80,180,146,0,
+16,161,128,148,128,164,88,20,176,2,90,11,9,66,89,0,244,
+123,140,212,255,255,255,192,255,255,17,160,63,82,60,254,
+254,2,61,31,88,195,89,246,30,126,49,84,1,0,8,14,224,2,
+61,31,88,227,89,50,1,127,54,118,224,2,59,22,96,2,61,12,
+16,98,89,4,32,163,144,0,16,83,144,130,13,77,89,2,222,199,
+89,8,1,166,89,137,32,5,90,0,80,138,140,10,0,0,22,20,22,
+136,92,0,48,80,146,48,0,0,32,0,48,160,144,96,81,0,0,255,
+0,168,140,17,65,74,89,0,48,136,146,96,0,0,32,8,64,68,89,
+17,157,82,140,17,193,90,89,1,32,165,140,148,64,173,88,0,
+48,160,146,96,81,0,0,0,48,168,146,4,0,0,32,174,32,82,59,
+8,78,62,89,0,48,112,140,8,0,0,16,255,0,48,140,0,244,233,
+140,192,0,0,112,0,144,163,128,148,128,161,88,134,32,85,51,
+0,30,160,92,29,0,37,89,20,252,41,140,192,0,0,64,3,80,24,
+89,30,128,30,61,27,22,128,92,0,80,142,140,96,224,239,146,
+116,238,255,11,0,30,24,92,96,224,239,144,0,48,224,140,255,
+63,0,0,0,100,163,140,148,0,111,88,0,48,104,146,76,12,0,
+80,0,16,161,176,0,16,161,176,0,16,161,152,0,16,161,144,0,
+48,176,140,0,0,0,176,0,80,177,146,0,144,163,128,148,128,
+161,88,20,176,2,90,11,9,66,89,0,244,123,140,212,255,255,
+255,148,255,255,17,116,63,82,60,226,254,2,61,31,88,195,89,
+216,30,126,49,3,80,24,89,22,128,30,61,27,22,128,92,0,80,
+142,140,248,237,255,11,0,30,24,92,0,48,224,140,255,63,0,
+0,0,100,163,140,148,0,167,88,142,9,165,88,0,48,160,146,
+76,12,0,80,0,48,160,128,8,0,0,16,148,32,2,90,0,48,176,
+140,8,0,0,16,255,0,168,140,18,0,0,19,0,144,165,128,148,
+64,165,88,248,31,162,60,0,48,160,128,8,0,0,16,148,48,0,
+90,0,48,184,140,8,0,0,16,255,0,176,140,34,0,0,18,8,78,
+166,89,0,52,173,140,192,0,0,112,0,80,165,144,0,208,165,
+128,148,128,165,88,244,63,5,61,31,88,195,89,80,224,231,
+144,15,1,166,89,64,224,199,144,2,12,181,89,1,25,184,89,1,
+137,181,89,8,78,166,89,150,224,5,90,0,52,165,140,192,0,0,
+64,156,3,174,88,0,16,173,146,22,0,0,18,1,137,181,89,150,
+224,5,90,0,16,245,146,244,255,255,21,8,78,174,89,154,48,
+0,90,26,206,163,89,0,116,165,146,192,0,0,64,26,0,0,18,3,
+80,24,89,18,128,30,61,27,22,128,92,0,80,142,140,4,237,
+255,11,112,224,199,176,128,224,231,144,0,0,0,10,0,0,0,0,
+0,0,0,0,0,0,0,0,0,48,160,144,188,81,0,0,16,16,140,89,
+14,222,144,89,17,0,173,89,149,160,4,90,0,48,32,144,184,
+81,0,0,18,0,0,22,0,30,128,92,0,0,0,10,0,0,0,0,17,0,
+161,89,0,16,129,140,0,48,160,146,184,81,0,0,0,48,168,146,
+188,81,0,0,8,34,0,9,4,208,131,89,16,210,131,88,0,0,0,
+10,0,0,0,0,0,0,0,0,0,30,144,92,0,0,136,140,135,25,152,
+88,0,48,128,140,0,4,0,136,0,48,184,140,192,81,0,0,0,208,
+165,128,34,32,5,58,0,16,164,144,3,224,173,128,4,224,181,
+128,8,12,165,89,148,64,165,88,10,128,165,58,18,80,144,89,
+1,96,140,140,145,224,4,90,4,32,132,140,23,16,186,89,204,
+255,255,22,14,160,4,58,0,30,128,92,0,0,0,10,0,0,80,140,
+0,30,32,92,120,1,0,9,0,30,72,92,0,0,40,140,0,30,48,92,
+0,0,64,140,0,48,160,140,240,85,0,0,0,48,240,146,248,85,
+0,0,0,30,56,92,0,16,69,154,1,30,128,92,0,48,32,178,224,
+85,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,48,240,
+140,144,159,0,0,30,22,152,92,0,0,240,140,135,25,184,88,
+14,192,133,54,0,30,128,92,0,208,4,132,144,57,168,140,192,
+81,0,0,0,80,165,128,14,32,5,61,0,30,128,92,0,208,4,132,
+2,96,165,128,16,57,176,140,0,4,0,136,145,0,141,88,146,48,
+0,90,8,78,164,89,0,144,165,146,38,0,0,18,0,144,165,144,
+1,96,173,128,8,12,165,89,148,64,165,88,18,64,164,58,0,30,
+128,92,0,208,4,132,0,0,0,0,1,30,128,92,0,208,4,132,0,0,
+0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,48,240,140,248,159,0,
+0,30,22,144,92,0,0,240,140,135,25,176,88,14,128,133,54,0,
+30,128,92,0,144,4,132,144,57,168,140,192,81,0,0,0,80,165,
+128,14,32,5,61,0,30,128,92,0,144,4,132,16,57,160,144,0,
+4,0,136,1,96,173,128,8,12,165,89,148,64,165,88,1,0,128,
+140,0,80,164,130,0,144,4,132,0,0,0,10,0,0,0,0,0,30,160,
+92,0,0,176,140,0,30,184,92,0,0,168,140,0,30,136,92,0,0,
+144,140,0,48,160,178,224,85,0,0,0,30,128,92,0,48,160,140,
+240,85,0,0,0,48,240,146,248,85,0,0,0,16,133,154,0,0,0,
+10,0,0,0,0,16,72,8,89,0,48,160,128,128,83,0,0,1,30,144,
+92,148,48,0,90,64,224,151,130,30,0,0,18,0,48,160,128,130,
+83,0,0,148,80,160,88,8,14,165,89,0,48,160,146,224,4,0,
+136,232,3,128,140,240,4,0,11,0,48,160,128,80,82,0,0,148,
+48,0,90,64,224,143,140,34,0,0,18,0,48,160,144,72,4,0,
+136,0,48,168,128,81,82,0,0,8,12,165,89,148,64,165,88,0,
+80,164,130,0,48,160,128,88,82,0,0,34,32,5,58,0,48,160,
+144,76,4,0,136,0,48,168,128,89,82,0,0,8,12,165,89,148,
+64,165,88,65,224,167,130,65,224,167,128,0,48,168,128,144,
+82,0,0,64,224,183,128,0,48,184,144,224,85,0,0,149,48,0,
+90,8,14,165,89,148,131,165,88,20,192,165,89,0,48,160,146,
+224,85,0,0,34,0,0,18,0,48,160,144,104,4,0,136,0,48,168,
+128,145,82,0,0,8,12,165,89,148,64,165,88,0,80,164,130,0,
+48,160,128,152,82,0,0,34,32,5,58,0,48,160,144,108,4,0,
+136,0,48,168,128,153,82,0,0,8,12,165,89,148,64,165,88,66,
+224,167,130,0,48,160,128,160,82,0,0,34,32,5,58,0,48,160,
+144,112,4,0,136,0,48,168,128,161,82,0,0,8,12,165,89,148,
+64,165,88,65,224,167,130,65,224,167,128,66,224,175,128,0,
+48,176,128,168,82,0,0,64,224,191,128,0,48,128,144,232,85,
+0,0,16,14,165,89,150,48,0,90,8,78,173,89,148,67,165,88,
+148,195,165,88,20,0,164,89,0,48,160,146,232,85,0,0,34,0,
+0,18,0,48,160,144,116,4,0,136,0,48,168,128,169,82,0,0,8,
+12,165,89,148,64,165,88,0,80,164,130,0,48,160,128,176,82,
+0,0,34,32,5,58,0,48,160,144,120,4,0,136,0,48,168,128,
+177,82,0,0,8,12,165,89,148,64,165,88,66,224,167,130,0,48,
+160,128,184,82,0,0,34,32,5,58,0,48,160,144,124,4,0,136,
+0,48,168,128,185,82,0,0,8,12,165,89,148,64,165,88,65,224,
+167,130,65,224,167,128,66,224,175,128,0,48,176,128,128,83,
+0,0,64,224,191,128,0,48,128,144,236,85,0,0,16,14,165,89,
+150,48,0,90,8,78,173,89,148,67,165,88,148,195,165,88,20,
+0,164,89,0,48,160,146,236,85,0,0,34,0,0,18,0,48,160,144,
+224,4,0,136,0,48,168,128,129,83,0,0,8,12,165,89,148,64,
+165,88,0,80,164,130,0,48,160,128,136,83,0,0,34,32,5,58,
+0,48,160,144,228,4,0,136,0,48,168,128,137,83,0,0,8,12,
+165,89,148,64,165,88,65,224,167,130,65,224,167,128,0,48,
+168,128,144,83,0,0,64,224,183,128,0,48,184,144,228,85,0,
+0,149,48,0,90,8,14,165,89,148,131,165,88,20,192,165,89,0,
+48,160,146,228,85,0,0,34,0,0,18,0,48,160,144,232,4,0,
+136,0,48,168,128,145,83,0,0,8,12,165,89,148,64,165,88,0,
+80,164,130,0,48,160,128,152,83,0,0,34,32,5,58,0,48,160,
+144,236,4,0,136,0,48,168,128,153,83,0,0,8,12,165,89,148,
+64,165,88,65,224,167,130,65,224,167,128,0,48,168,128,104,
+84,0,0,0,80,188,128,0,48,176,144,240,85,0,0,149,48,0,90,
+8,14,165,89,148,195,165,88,20,128,165,89,0,48,160,146,240,
+85,0,0,30,0,0,18,0,48,160,128,106,84,0,0,151,0,165,88,
+8,14,165,89,0,48,160,146,84,5,0,136,232,3,128,140,252,1,
+0,11,0,48,160,128,96,84,0,0,34,32,5,58,0,48,160,144,80,
+5,0,136,0,48,168,128,97,84,0,0,8,12,165,89,148,64,165,
+88,65,224,167,130,65,224,167,128,0,48,168,128,104,84,0,0,
+0,48,176,144,244,85,0,0,149,48,0,90,20,128,165,89,0,48,
+160,146,244,85,0,0,34,0,0,18,0,48,160,144,84,5,0,136,0,
+48,168,128,105,84,0,0,8,12,165,89,148,64,165,88,65,224,
+167,130,65,224,167,128,0,48,168,144,248,85,0,0,20,64,165,
+89,0,48,160,146,248,85,0,0,0,0,0,10,0,0,0,0,0,0,0,0,
+0,48,240,140,228,164,0,0,30,22,152,92,0,0,240,140,16,72,
+8,89,0,16,164,144,0,16,172,144,0,48,144,140,0,255,0,0,0,
+16,180,144,148,128,164,88,8,12,133,89,135,25,144,88,16,
+160,4,90,16,76,189,89,24,140,181,89,142,0,0,17,144,57,
+168,140,192,81,0,0,0,80,165,128,126,32,5,58,16,57,160,
+144,0,4,0,136,1,96,173,128,8,12,165,89,148,64,165,88,135,
+25,144,88,16,160,4,90,22,1,165,88,151,128,173,88,148,67,
+165,88,255,0,168,140,148,64,141,88,0,116,160,130,240,255,
+255,255,66,0,0,17,144,57,184,140,192,81,0,0,0,208,165,
+128,50,32,5,58,2,224,173,128,16,57,176,140,0,4,0,136,145,
+64,173,88,8,78,165,89,0,144,165,146,0,144,165,144,1,224,
+181,128,8,12,165,89,148,128,165,88,18,64,165,58,0,30,128,
+92,16,73,8,89,0,208,4,132,1,30,128,92,16,73,8,89,0,208,
+4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,30,128,92,135,25,152,
+88,1,25,144,89,255,0,136,140,0,48,168,140,176,228,0,0,0,
+48,184,140,0,4,0,136,0,48,176,140,192,81,0,0,0,144,165,
+128,14,32,5,61,0,80,149,146,20,0,0,8,0,208,165,144,8,12,
+165,89,148,64,164,88,0,80,165,146,16,80,128,89,144,224,4,
+90,4,96,173,140,23,16,185,89,8,160,181,140,204,255,255,22,
+0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,48,240,140,144,
+165,0,0,30,22,136,92,0,0,240,140,144,48,0,90,1,9,132,89,
+22,0,0,20,16,22,160,92,148,48,0,90,1,9,132,89,244,255,
+255,19,0,80,4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,
+0,208,172,144,4,78,164,89,17,57,136,140,0,0,0,0,17,1,
+237,89,127,0,64,140,29,32,2,90,0,48,168,146,64,20,0,80,
+198,0,0,22,8,14,164,89,0,48,48,140,84,0,0,32,0,48,40,
+140,96,0,0,32,135,25,32,88,0,52,141,140,112,4,0,96,0,
+144,145,146,0,80,33,146,0,80,164,176,0,80,164,176,0,80,
+164,176,0,80,164,176,0,80,164,176,0,80,164,176,0,80,164,
+176,0,80,164,176,0,80,164,176,0,80,164,176,0,80,164,176,
+0,80,164,176,0,80,164,176,0,80,164,176,0,80,164,176,0,80,
+164,176,0,80,164,176,0,80,164,176,0,80,164,176,0,80,164,
+176,0,80,164,176,0,80,164,176,0,80,164,176,0,80,164,176,
+0,80,164,176,0,80,164,176,0,80,164,176,0,80,164,176,0,80,
+164,176,0,162,148,140,127,0,64,140,0,116,239,140,128,255,
+255,255,29,32,2,90,0,80,164,176,0,80,164,176,0,80,164,
+176,100,255,255,17,63,0,64,140,110,0,234,54,0,48,144,146,
+84,0,0,32,134,25,168,88,0,48,168,146,96,0,0,32,8,14,164,
+89,0,52,165,140,112,4,0,96,0,16,37,176,0,16,37,176,0,16,
+37,176,0,16,37,176,0,16,37,176,0,16,37,176,0,16,37,176,
+0,16,37,176,0,16,37,176,0,16,37,176,0,16,37,176,0,16,37,
+176,0,16,37,176,0,161,148,140,21,65,239,89,0,16,37,176,0,
+16,37,176,0,16,165,176,78,96,255,51,0,48,144,146,84,0,0,
+32,31,88,168,89,0,48,168,146,96,0,0,32,8,14,164,89,0,52,
+165,140,112,4,0,96,0,16,37,176,0,16,37,176,0,16,37,176,
+0,16,37,176,0,16,37,176,128,160,148,140,21,65,239,89,0,
+16,37,176,0,16,37,176,0,16,165,176,58,96,191,51,0,48,144,
+146,80,0,0,32,8,14,164,89,0,52,165,140,112,4,0,96,0,16,
+37,176,0,16,37,176,0,16,37,176,96,160,148,140,24,73,239,
+89,0,16,37,176,0,16,37,176,0,16,165,176,46,96,95,51,8,
+14,164,89,0,48,144,146,72,0,0,32,0,52,165,140,112,4,0,
+96,48,160,148,140,12,73,239,89,0,16,37,176,0,16,37,176,0,
+16,165,176,50,96,31,51,8,14,164,89,0,48,32,140,64,0,0,
+32,0,52,141,140,112,4,0,96,4,73,239,89,0,16,145,146,29,
+240,0,90,16,160,148,140,0,80,164,176,236,255,255,17,50,96,
+7,58,8,14,132,89,0,48,168,140,56,0,0,32,0,52,132,140,
+124,4,0,96,1,73,239,89,0,80,149,146,157,48,0,90,4,160,
+148,140,0,16,164,144,236,255,255,21,0,48,160,144,64,20,0,
+80,18,22,128,92,0,208,164,146,0,0,0,10,0,0,0,0,0,48,
+240,140,92,168,0,0,30,22,144,92,0,0,240,140,8,78,140,89,
+0,116,140,140,64,4,0,64,0,80,164,176,0,16,164,178,0,80,
+164,176,16,16,132,89,0,16,164,178,0,80,164,176,16,32,164,
+178,0,144,4,132,0,0,0,10,0,48,240,140,148,168,0,0,30,22,
+144,92,0,0,240,140,34,96,4,58,8,14,132,89,0,52,132,140,
+64,4,0,64,4,73,140,89,145,48,0,90,0,16,164,176,244,255,
+255,21,0,144,4,132,0,0,0,10,0,0,0,0,0,0,0,0,8,0,188,
+9,221,4,62,3,110,2,242,1,158,1,99,1,55,1,20,1,248,0,
+226,0,207,0,191,0,177,0,165,0,155,0,146,0,137,0,130,0,
+124,0,118,0,112,0,107,0,103,0,99,0,95,0,91,0,88,0,85,0,
+82,0,79,0,77,0,75,0,72,0,70,0,68,0,66,0,65,0,63,0,61,
+0,60,0,58,0,57,0,56,0,54,0,53,0,52,0,51,0,50,0,49,0,
+48,0,47,0,46,0,45,0,44,0,44,0,43,0,42,0,41,0,41,0,40,
+0,39,0,39,0,38,0,37,0,37,0,36,0,36,0,35,0,35,0,34,0,
+34,0,33,0,33,0,32,0,32,0,31,0,31,0,31,0,30,0,30,0,29,
+0,29,0,29,0,28,0,28,0,28,0,27,0,27,0,27,0,26,0,26,0,
+26,0,26,0,25,0,25,0,25,0,24,0,24,0,24,0,24,0,23,0,23,
+0,23,0,23,0,23,0,22,0,22,0,22,0,22,0,21,0,21,0,21,0,
+21,0,21,0,20,0,20,0,20,0,20,0,20,0,20,0,19,0,19,0,19,
+0,19,0,19,0,19,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,
+17,0,17,0,17,0,17,0,17,0,17,0,17,0,17,0,16,0,16,0,16,
+0,16,0,16,0,16,0,16,0,16,0,16,0,15,0,15,0,15,0,15,0,
+15,0,15,0,15,0,15,0,15,0,14,0,14,0,14,0,14,0,14,0,14,
+0,14,0,14,0,14,0,14,0,14,0,13,0,13,0,13,0,13,0,13,0,
+13,0,13,0,13,0,13,0,13,0,13,0,13,0,13,0,12,0,12,0,12,
+0,12,0,12,0,12,0,12,0,12,0,12,0,12,0,12,0,12,0,12,0,
+12,0,12,0,11,0,11,0,11,0,11,0,11,0,11,0,11,0,11,0,11,
+0,11,0,11,0,11,0,11,0,11,0,11,0,11,0,11,0,10,0,10,0,
+10,0,10,0,10,0,10,0,10,0,10,0,10,0,10,0,10,0,10,0,10,
+0,10,0,10,0,10,0,10,0,10,0,10,0,10,0,10,0,9,0,9,0,9,
+0,9,0,9,0,9,0,9,0,9,0,9,0,9,0,9,0,9,0,9,0,9,0,9,0,
+9,0,9,0,8,0,0,48,32,140,0,178,0,0,0,48,40,140,32,12,0,
+80,0,30,48,92,0,30,56,92,80,0,32,178,0,48,40,140,36,12,
+0,80,144,0,32,178,0,48,40,140,40,12,0,80,208,0,32,178,0,
+48,40,140,44,12,0,80,16,1,32,178,0,30,32,92,0,30,40,92,
+80,0,64,140,144,0,72,140,208,0,80,140,16,1,88,140,16,32,
+34,178,16,96,34,178,16,160,34,178,16,224,34,178,0,48,32,
+140,64,8,0,64,0,48,40,140,64,0,0,112,32,32,34,178,0,48,
+32,140,64,9,0,64,0,48,40,140,64,1,0,112,32,96,34,178,0,
+48,32,140,64,10,0,64,0,48,40,140,64,2,0,112,32,160,34,
+178,0,48,32,140,64,11,0,64,0,48,40,140,64,3,0,112,32,
+224,34,178,0,30,40,92,0,48,32,140,0,16,0,80,48,32,34,
+178,0,48,32,140,4,16,0,80,48,96,34,178,0,48,32,140,8,16,
+0,80,48,160,34,178,0,48,32,140,12,16,0,80,48,224,34,178,
+80,1,64,140,0,48,32,140,176,174,0,0,8,32,42,140,0,32,34,
+154,5,22,56,92,0,48,32,140,80,3,0,0,16,32,49,144,18,160,
+1,50,8,200,41,89,0,208,33,154,8,200,57,89,0,48,32,140,
+128,3,0,0,16,32,49,144,18,160,1,50,8,200,41,89,0,208,33,
+154,8,200,57,89,0,48,32,140,176,3,0,0,16,32,49,144,18,
+160,1,50,8,200,41,89,0,208,33,154,8,200,57,89,0,48,32,
+140,32,228,0,0,16,32,49,144,14,160,1,50,0,208,33,146,8,
+200,57,89,4,201,57,89,8,32,42,140,0,208,41,146,0,30,32,
+92,0,30,40,92,0,30,48,92,0,30,56,92,48,32,34,178,208,1,
+64,140,0,48,32,140,16,176,0,0,0,48,40,144,32,3,0,0,0,
+48,48,144,36,3,0,0,0,32,34,178,0,30,32,92,0,30,48,92,
+16,32,34,178,32,32,34,178,16,2,64,140,0,48,32,140,224,
+183,0,0,0,32,34,178,0,0,32,140,60,32,34,146,144,1,64,
+140,0,48,32,140,0,185,0,0,0,32,34,178,80,2,64,140,80,0,
+32,140,0,16,34,146,4,8,66,89,0,16,34,146,4,8,66,89,0,
+16,34,146,4,8,66,89,0,16,34,146,4,8,66,89,80,1,32,140,
+0,16,34,146,4,8,66,89,208,1,32,140,0,16,34,146,4,8,66,
+89,16,2,32,140,0,16,34,146,4,8,66,89,144,1,32,140,0,16,
+34,146,0,48,32,140,0,192,255,15,0,48,32,146,240,2,0,0,0,
+48,32,140,240,63,0,0,0,48,32,146,244,2,0,0,0,0,32,140,
+0,48,32,146,248,2,0,0,6,0,32,140,0,48,32,146,252,2,0,0,
+0,2,40,140,32,0,48,140,0,48,56,140,80,237,2,0,132,2,56,
+146,7,22,32,92,4,64,57,89,240,33,57,146,1,137,181,89,240,
+191,5,53,0,30,56,92,240,33,57,146,0,48,64,140,192,2,0,0,
+12,0,56,140,0,48,64,146,44,3,0,0,144,0,48,140,8,32,50,
+146,7,0,42,89,0,16,42,146,5,22,64,92,208,0,48,140,8,32,
+50,146,7,0,42,89,0,16,42,146,5,22,64,92,16,1,48,140,8,
+32,50,146,0,32,242,146,0,48,240,146,40,3,0,0,0,0,0,10,
+0,0,0,0,0,0,0,0,0,48,32,144,40,3,0,0,0,48,40,140,40,
+3,0,0,94,32,1,50,4,32,49,144,16,128,129,50,4,22,40,92,
+0,16,33,144,236,255,255,8,0,16,49,144,0,48,56,144,44,3,
+0,0,0,80,49,146,0,16,57,146,0,48,32,146,44,3,0,0,80,2,
+24,140,8,32,41,144,208,0,48,140,4,200,24,89,16,128,41,52,
+4,200,24,89,8,128,41,50,4,200,24,89,80,0,48,140,0,208,
+48,146,0,0,0,10,80,2,24,140,0,48,40,140,64,4,0,64,0,48,
+48,140,128,8,0,64,0,48,56,140,64,20,0,80,0,48,64,140,64,
+4,0,96,255,15,72,140,0,48,80,144,96,86,0,0,0,48,88,140,
+8,0,0,16,0,48,96,140,64,12,0,80,0,48,104,140,0,0,0,48,
+0,48,112,140,96,0,0,48,0,0,120,140,0,30,240,92,0,48,136,
+144,112,228,0,0,0,48,128,140,255,255,15,0,0,80,132,146,0,
+80,132,144,144,0,132,88,1,9,132,89,0,80,132,146,242,63,4,
+53,0,208,130,144,12,32,4,50,0,80,131,144,244,255,255,8,0,
+208,32,144,0,16,161,176,0,16,5,132,0,0,0,0,44,224,3,50,
+0,0,120,140,0,48,136,144,120,228,0,0,0,0,120,140,0,80,
+148,144,136,25,128,88,1,160,148,140,0,80,148,146,0,48,128,
+146,4,0,0,32,0,80,197,152,4,200,24,89,28,32,150,144,4,
+32,201,146,0,144,132,144,64,32,4,53,174,95,174,50,0,80,
+198,152,28,32,150,144,0,144,132,144,44,32,4,53,154,95,174,
+50,0,80,198,152,28,32,150,144,0,144,132,144,24,32,4,53,
+134,95,174,50,0,80,198,152,28,32,150,144,0,144,132,144,
+116,63,4,50,0,16,230,152,110,95,231,49,0,0,208,140,12,32,
+222,144,120,0,200,140,28,65,231,89,90,31,223,49,155,56,
+232,140,0,0,0,0,0,48,128,146,48,0,0,32,29,128,214,89,0,
+48,232,146,96,0,0,32,29,65,206,89,4,160,164,144,2,30,168,
+92,32,32,182,152,0,144,244,146,8,136,148,89,0,48,160,146,
+56,0,0,32,8,192,149,54,22,22,144,92,0,144,132,144,0,144,
+171,146,27,1,231,89,12,32,4,50,10,0,223,49,176,95,238,54,
+28,32,150,146,0,16,222,144,24,32,206,144,1,140,142,89,155,
+93,230,140,27,64,140,89,0,16,142,146,0,208,130,128,254,63,
+68,49,8,137,214,89,0,80,131,176,0,168,6,90,0,80,163,176,
+8,8,239,89,0,16,135,154,8,72,231,89,0,80,151,154,8,8,
+239,89,0,16,167,154,8,72,231,89,0,80,183,154,200,255,255,
+21,160,254,255,8,0,0,0,0,0,0,0,0,0,0,0,0,0,80,213,152,
+132,2,200,144,4,200,24,89,120,0,192,140,128,190,6,50,126,
+126,6,50,0,30,184,92,154,208,155,88,26,210,151,88,3,206,
+212,89,70,0,214,49,26,192,189,89,0,80,245,146,0,48,144,
+146,48,0,0,32,0,48,208,146,96,0,0,32,21,16,170,89,244,
+97,222,146,26,1,198,89,0,80,214,146,8,128,173,54,20,32,
+169,144,0,80,213,152,240,97,206,144,8,160,6,50,180,127,6,
+53,0,48,160,140,16,176,0,0,0,16,161,178,132,2,160,144,0,
+16,181,144,0,208,130,128,250,159,133,52,22,193,189,89,0,
+16,229,140,0,80,131,176,16,8,239,89,0,80,195,176,8,137,
+181,89,0,16,135,178,16,72,231,89,0,80,199,178,230,191,5,
+53,10,32,141,136,255,0,144,140,7,32,133,128,145,128,140,
+88,1,78,140,89,240,33,213,144,0,116,140,140,160,168,0,0,
+143,8,132,88,0,80,140,136,240,33,245,146,10,32,141,138,
+238,32,44,53,80,0,144,140,124,96,68,50,0,48,176,144,40,3,
+0,0,0,16,173,144,4,160,157,144,18,160,5,50,96,192,172,50,
+0,144,181,144,240,255,255,8,0,48,176,144,44,3,0,0,82,160,
+5,50,4,160,173,146,0,144,133,144,0,48,136,144,40,3,0,0,
+0,48,128,146,44,3,0,0,0,144,141,146,0,48,176,146,40,3,0,
+0,80,2,144,140,8,160,141,144,208,0,128,140,4,136,148,89,
+16,0,140,52,4,136,148,89,8,0,140,50,4,136,148,89,0,144,
+140,146,8,160,149,144,0,48,192,144,112,86,0,0,24,160,132,
+144,1,8,198,89,0,48,192,146,112,86,0,0,44,32,4,53,0,48,
+128,140,200,191,0,0,24,160,164,146,80,0,136,140,0,160,132,
+146,42,128,140,53,0,48,128,140,192,191,0,0,0,160,132,146,
+24,0,0,8,240,33,140,144,12,96,4,50,17,22,128,92,244,255,
+255,8,240,33,164,146,26,22,160,92,188,254,5,53,132,2,160,
+146,200,252,255,8,16,2,144,140,60,160,132,144,214,63,4,53,
+0,48,152,140,240,183,0,0,60,160,164,146,0,160,156,146,212,
+255,255,8,0,0,0,0,0,48,136,144,12,0,0,16,48,0,144,140,
+214,133,140,52,0,144,193,152,0,48,160,176,240,2,0,0,137,
+64,206,88,194,101,6,50,152,0,133,88,152,128,149,88,238,36,
+4,53,152,64,141,88,23,132,148,89,145,131,140,88,145,57,
+216,140,80,237,0,0,16,224,150,152,42,37,30,55,0,144,4,
+132,0,48,128,176,0,3,0,0,17,29,132,140,0,16,148,144,28,
+160,4,53,0,48,128,144,160,86,0,0,1,8,132,89,0,48,128,
+146,160,86,0,0,40,4,0,8,60,224,230,144,1,72,140,89,0,
+144,196,146,145,192,140,88,0,16,244,146,8,160,156,140,0,
+16,135,144,0,48,136,146,4,3,0,0,24,224,150,146,28,224,
+158,146,40,32,4,53,28,22,128,92,160,6,0,9,0,16,135,144,
+24,32,4,53,40,32,135,144,0,16,140,144,1,72,140,89,0,16,
+140,146,180,3,0,8,24,32,143,144,1,9,132,89,144,93,140,
+140,28,224,158,144,0,80,148,144,4,96,180,144,52,224,190,
+144,0,16,135,146,0,208,148,146,8,200,156,89,28,224,158,
+146,2,204,165,89,0,48,144,140,112,186,0,0,25,22,152,92,0,
+48,184,140,255,255,255,255,4,78,134,89,16,224,150,154,2,
+78,142,89,104,7,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,208,
+129,144,0,48,136,140,123,221,4,199,150,1,140,53,0,48,128,
+144,100,86,0,0,20,224,166,144,0,48,152,140,255,255,0,0,0,
+48,136,144,116,86,0,0,20,0,132,89,1,96,140,140,0,48,128,
+146,100,86,0,0,0,48,136,146,116,86,0,0,146,192,148,88,24,
+224,134,152,48,224,166,160,16,65,140,89,8,73,140,89,3,76,
+140,89,4,32,140,146,12,8,164,89,0,80,156,140,40,64,149,
+54,0,16,173,146,21,129,148,89,8,32,165,140,24,128,149,54,
+0,16,181,146,22,129,148,89,8,32,165,140,1,201,156,89,236,
+255,255,8,0,16,149,146,20,224,12,50,8,32,165,140,0,16,
+245,146,8,0,0,8,0,0,0,0,0,48,152,144,16,3,0,0,2,72,
+140,89,0,208,148,144,36,160,4,53,0,48,160,144,120,228,0,
+0,1,0,168,140,136,25,176,88,0,16,173,146,0,48,176,146,4,
+0,0,32,220,255,255,8,1,76,140,89,0,48,160,176,0,3,0,0,
+1,136,173,89,22,29,133,146,151,64,181,88,0,48,176,146,8,
+3,0,0,1,75,140,90,0,16,164,176,16,8,132,89,0,48,144,146,
+64,0,0,32,16,136,148,89,0,144,163,178,228,255,255,21,0,
+48,136,144,20,3,0,0,4,224,132,144,0,208,244,146,8,200,
+156,89,17,224,4,90,12,0,0,19,0,48,152,144,24,3,0,0,2,
+30,136,92,0,48,128,146,56,0,0,32,0,144,139,146,0,48,152,
+146,16,3,0,0,1,224,123,140,40,224,35,49,0,0,120,140,0,
+48,136,144,120,228,0,0,136,25,128,88,0,80,148,144,1,160,
+148,140,0,48,128,146,4,0,0,32,0,80,148,146,0,104,6,90,
+20,224,246,146,0,48,128,140,80,178,0,0,16,224,134,146,138,
+253,255,21,52,253,255,8,0,48,152,140,255,255,0,0,146,192,
+148,88,20,224,142,144,2,140,148,89,17,58,128,140,0,0,0,0,
+2,78,140,89,17,1,132,89,12,0,148,51,12,9,132,89,26,0,
+148,49,0,48,128,144,108,86,0,0,1,8,132,89,0,48,128,146,
+108,86,0,0,0,48,136,144,120,86,0,0,0,30,232,92,1,72,140,
+89,0,48,136,146,120,86,0,0,4,0,0,8,0,48,128,144,104,86,
+0,0,20,224,166,144,20,0,132,89,0,48,128,146,104,86,0,0,
+24,224,134,152,16,65,140,89,8,73,140,89,3,76,140,89,4,32,
+140,146,4,72,140,89,0,48,152,144,16,3,0,0,1,76,140,89,0,
+48,160,176,0,3,0,0,1,136,173,89,22,29,133,146,151,64,181,
+88,0,48,176,146,8,3,0,0,0,208,148,144,36,160,4,53,0,48,
+160,144,120,228,0,0,1,0,168,140,136,25,176,88,0,16,173,
+146,0,48,176,146,4,0,0,32,220,255,255,8,1,75,140,90,0,
+48,144,146,64,0,0,32,16,136,148,89,0,16,164,176,16,8,132,
+89,0,144,163,178,228,255,255,21,0,48,136,144,20,3,0,0,4,
+224,132,144,0,208,244,146,8,200,156,89,17,224,4,90,12,0,
+0,19,0,48,152,144,24,3,0,0,10,30,136,92,0,48,152,146,16,
+3,0,0,0,48,128,146,56,0,0,32,0,144,139,146,1,224,123,
+140,40,224,35,49,0,0,120,140,0,48,136,144,120,228,0,0,
+136,25,128,88,0,80,148,144,1,160,148,140,0,80,148,146,0,
+48,128,146,4,0,0,32,20,224,246,146,0,48,128,140,192,182,
+0,0,12,32,14,48,0,48,128,140,80,178,0,0,16,224,134,146,
+18,96,39,49,4,73,239,89,0,80,129,176,248,127,39,54,16,96,
+7,50,0,80,129,144,1,73,239,89,244,255,255,8,140,123,6,50,
+216,251,255,8,0,0,0,0,0,48,128,176,0,3,0,0,24,224,142,
+144,18,29,140,146,1,136,148,89,146,192,148,88,0,48,144,
+146,8,3,0,0,0,48,128,140,192,182,0,0,20,224,246,146,16,
+224,134,146,16,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,48,
+128,144,104,86,0,0,25,0,132,89,0,48,128,146,104,86,0,0,
+124,32,14,48,0,48,136,140,80,178,0,0,0,48,128,144,128,86,
+0,0,16,224,142,146,1,32,132,140,0,48,128,146,128,86,0,0,
+88,0,0,8,0,0,0,0,0,48,128,144,20,86,0,0,25,0,132,89,0,
+48,128,146,20,86,0,0,60,0,0,8,0,48,160,140,0,0,240,15,
+152,0,165,88,0,48,128,140,8,86,0,0,20,129,165,88,12,32,
+5,53,0,48,128,140,16,86,0,0,0,16,140,144,25,64,140,89,0,
+16,140,146,8,0,0,8,0,0,0,0,0,80,129,176,1,104,6,90,0,
+80,129,176,1,73,206,89,0,80,129,176,236,255,255,20,152,
+250,255,8,0,0,0,0,224,255,255,8,0,0,0,0,0,0,0,0,0,0,
+0,0,27,22,128,92,24,22,136,92,25,22,144,92,32,0,160,178,
+48,0,192,178,64,0,224,178,248,214,255,9,64,0,224,176,48,
+0,192,176,32,0,160,176,88,250,255,8,0,0,0,0,27,22,128,
+92,24,22,136,92,25,22,144,92,32,0,160,178,48,0,192,178,
+64,0,224,178,168,220,255,9,64,0,224,176,48,0,192,176,32,
+0,160,176,40,250,255,8,0,0,0,0,4,200,24,89,188,246,255,
+8,0,0,0,0,0,0,0,0,0,48,160,144,0,16,0,80,32,0,160,178,
+48,0,192,178,64,0,224,178,0,48,128,140,32,12,0,80,0,16,
+140,144,254,127,4,53,60,32,129,144,7,32,188,128,143,200,
+189,88,6,32,140,128,4,32,148,136,0,30,152,92,10,32,196,
+136,0,0,160,140,0,30,168,92,0,48,192,146,0,16,0,80,16,
+32,180,140,0,32,132,144,12,224,37,50,4,211,255,9,8,0,0,
+8,204,222,255,9,0,48,128,140,32,12,0,80,0,16,140,144,254,
+127,4,53,64,0,224,176,48,0,192,176,32,0,160,176,0,48,160,
+146,0,16,0,80,60,32,153,144,244,225,140,144,2,30,144,92,
+0,48,136,146,56,0,0,32,0,144,147,146,4,224,132,144,0,48,
+136,140,0,0,0,240,145,0,132,88,50,32,4,50,1,224,123,140,
+40,224,35,49,0,0,120,140,0,48,136,144,120,228,0,0,136,25,
+128,88,0,80,148,144,1,160,148,140,0,80,148,146,0,48,128,
+146,4,0,0,32,240,225,148,144,132,2,136,144,0,168,4,90,
+240,225,140,146,132,2,152,146,60,32,145,146,246,254,255,21,
+0,48,128,140,224,183,0,0,0,32,129,146,228,254,255,8,0,48,
+144,144,112,228,0,0,0,48,128,144,8,228,0,0,0,144,156,144,
+80,2,24,140,1,200,156,89,0,16,140,144,0,144,156,146,124,
+117,4,50,0,48,80,146,96,86,0,0,48,0,192,178,64,0,224,
+178,184,159,255,9,64,0,224,176,48,0,192,176,92,245,255,8,
+0,0,0,0,0,0,0,0,28,32,36,144,16,22,24,92,0,16,129,144,
+204,32,4,50,120,0,96,140,0,224,48,152,0,0,64,140,12,224,
+40,144,6,193,49,89,182,128,41,49,133,56,56,140,0,0,0,0,
+0,48,128,146,48,0,0,32,7,0,66,89,0,48,56,146,96,0,0,32,
+7,1,99,89,4,32,73,144,32,224,80,152,0,16,241,146,8,8,33,
+89,0,48,72,146,56,0,0,32,8,192,34,54,10,22,32,92,2,30,
+88,92,0,16,129,144,0,48,88,146,96,0,0,48,5,129,49,89,14,
+64,49,52,10,32,4,50,172,31,59,54,28,224,32,146,0,208,40,
+144,24,224,48,144,1,12,58,89,133,157,49,140,5,192,57,89,
+0,208,56,146,0,48,32,140,8,0,0,16,0,48,72,140,0,0,0,48,
+0,16,129,128,254,63,36,49,4,9,66,89,0,80,98,176,0,144,
+97,154,8,136,49,89,0,144,113,154,8,136,49,89,224,63,2,53,
+0,0,0,10,0,0,0,0,0,48,24,144,116,228,0,0,0,48,40,144,
+120,228,0,0,1,0,48,140,136,25,56,88,0,208,32,144,26,32,
+1,48,0,48,240,146,4,0,0,32,0,80,49,146,0,48,56,146,4,0,
+0,32,0,0,0,10,0,0,0,0,0,0,0,0,0,208,166,176,25,192,
+156,89,25,57,136,140,0,0,0,0,20,224,158,146,4,78,134,89,
+0,208,185,146,17,1,228,89,50,33,14,55,0,30,200,92,0,0,
+232,140,16,0,229,54,20,1,239,89,142,32,5,50,0,16,229,140,
+48,0,168,140,28,1,165,89,21,22,184,92,8,64,229,51,28,22,
+184,92,2,206,149,89,0,48,176,146,84,0,0,32,23,65,141,89,
+0,48,184,146,96,0,0,32,0,48,128,140,232,186,0,0,18,128,
+181,89,17,28,4,132,0,16,130,176,0,16,130,176,0,16,130,
+176,0,16,130,176,0,16,130,176,0,16,130,176,0,16,130,176,
+0,16,130,176,0,16,130,176,0,16,130,176,0,16,130,176,23,1,
+231,89,0,16,130,176,152,63,7,52,18,96,7,53,0,208,185,144,
+0,224,166,178,212,246,255,8,28,224,150,144,24,224,142,144,
+0,48,128,144,140,228,0,0,17,129,148,89,236,25,148,51,64,
+224,230,144,20,224,142,144,85,5,144,140,0,16,135,144,218,
+153,140,49,40,32,4,53,28,22,128,92,236,253,255,9,0,16,
+135,144,24,32,4,53,40,32,135,144,0,16,140,144,1,72,140,
+89,0,16,140,146,176,249,255,8,24,32,143,144,1,9,132,89,
+144,93,140,140,28,224,158,144,0,80,148,144,4,96,180,144,
+56,224,190,144,0,16,135,146,0,208,148,146,8,200,156,89,28,
+224,158,146,2,204,165,89,0,80,231,140,224,254,255,8,0,0,
+0,0,1,73,206,89,0,0,232,140,8,33,101,54,10,30,184,92,0,
+0,232,140,16,0,189,54,20,193,237,89,0,16,189,140,82,32,5,
+50,151,56,144,140,0,0,0,0,0,48,176,146,84,0,0,32,22,128,
+180,89,0,48,184,146,96,0,0,32,22,224,37,49,8,232,5,90,0,
+16,130,176,4,201,189,89,244,255,255,22,26,224,13,49,0,16,
+130,144,18,224,21,49,0,16,130,144,10,224,29,49,0,16,130,
+144,144,96,7,50,28,224,150,144,24,224,142,144,0,48,128,
+144,140,228,0,0,17,129,148,89,12,0,148,52,2,72,239,89,
+228,248,255,8,64,224,230,144,0,16,135,144,44,32,4,53,28,
+22,128,92,240,252,255,9,0,16,135,144,28,32,4,53,40,32,
+135,144,0,16,140,144,1,72,140,89,0,16,140,146,2,72,239,
+89,176,248,255,8,24,32,143,144,1,9,132,89,144,93,140,140,
+28,224,158,144,0,80,148,144,4,96,180,144,56,224,190,144,0,
+16,135,146,0,208,148,146,8,200,156,89,28,224,158,146,2,
+204,165,89,29,22,184,92,24,255,255,8,0,80,145,152,112,246,
+255,8,0,0,0,0,0,0,0,0,0,0,0,0,0,48,176,146,72,0,0,32,
+0,16,130,176,0,16,130,176,0,16,130,176,76,246,255,8,0,0,
+0,0,0,0,0,0,0,80,149,144,74,0,152,140,84,0,128,140,6,
+213,148,51,32,32,193,176,0,0,224,140,18,1,164,89,0,16,
+214,146,20,57,136,140,0,0,0,0,4,14,133,89,0,16,219,146,
+120,0,216,140,17,1,236,89,0,0,160,140,27,65,239,89,48,
+192,190,52,27,57,144,140,0,0,0,0,27,0,231,89,0,48,176,
+146,48,0,0,32,27,193,189,89,0,48,216,146,96,0,0,32,18,
+128,181,89,0,208,162,144,76,0,0,8,30,224,5,50,0,48,176,
+146,48,0,0,32,23,193,222,89,0,48,184,146,96,0,0,32,23,0,
+231,89,16,32,129,152,128,11,132,90,0,208,162,144,108,0,0,
+18,0,80,180,152,8,96,140,140,3,200,189,89,16,32,129,154,
+2,204,189,89,144,255,6,53,0,208,162,144,40,32,101,54,0,
+208,162,144,120,0,216,140,244,63,7,53,116,95,223,54,0,16,
+219,144,8,32,177,154,44,32,217,146,56,244,255,8,0,0,0,0,
+0,80,134,176,12,9,165,89,1,160,82,140,0,80,134,176,12,9,
+231,89,0,80,134,176,232,63,101,54,0,208,162,144,184,255,
+255,8,20,32,101,54,0,208,162,144,248,63,103,54,46,0,167,
+50,240,255,255,8,0,80,134,176,12,9,231,89,0,80,134,176,1,
+160,82,140,12,9,165,89,0,80,134,176,232,63,101,54,0,208,
+162,144,204,255,255,8,130,139,214,88,48,32,87,51,0,80,134,
+176,0,48,160,140,255,255,255,79,0,80,134,176,153,0,165,88,
+0,80,134,160,0,16,245,146,1,160,82,140,10,30,160,92,0,16,
+214,146,56,0,0,8,28,145,162,89,0,16,214,146,26,32,39,49,
+4,9,231,89,0,80,134,176,14,32,39,49,4,9,231,89,0,80,134,
+176,14,32,23,49,2,9,231,89,0,80,134,152,10,32,15,49,0,
+80,134,144,2,14,165,89,0,48,144,140,255,255,255,79,2,158,
+138,89,0,48,128,140,192,190,0,0,20,65,140,89,146,64,166,
+88,16,64,132,89,24,32,153,144,0,16,4,132,0,16,245,146,0,
+16,245,146,0,16,245,146,0,16,245,146,0,16,245,146,0,16,
+245,146,0,16,245,146,0,16,245,146,0,16,245,146,0,16,245,
+146,4,224,132,136,1,136,82,89,0,16,133,146,0,16,131,144,
+244,225,140,144,16,21,128,88,0,48,136,146,56,0,0,32,2,30,
+144,92,0,16,133,146,0,144,147,146,4,224,132,144,0,48,136,
+140,0,0,0,240,145,0,132,88,50,32,4,50,1,224,123,140,40,
+224,35,49,0,0,120,140,0,48,136,144,120,228,0,0,0,80,148,
+144,1,160,148,140,136,25,128,88,0,80,148,146,0,48,128,146,
+4,0,0,32,240,225,148,144,132,2,136,144,0,168,4,90,132,2,
+152,146,240,225,140,146,52,0,0,21,0,48,144,144,112,228,0,
+0,0,48,128,140,32,192,0,0,0,144,156,144,0,48,136,140,0,
+0,32,0,24,32,241,146,19,64,140,89,0,32,129,146,8,32,137,
+146,104,242,255,8,10,160,140,136,10,224,132,136,52,64,132,
+50,0,48,128,140,192,191,0,0,24,32,145,146,0,32,129,146,
+72,242,255,8,0,0,0,0,0,80,133,144,60,50,4,53,0,48,128,
+140,240,188,0,0,24,32,145,144,0,32,129,146,48,32,153,144,
+10,160,140,136,0,160,228,144,1,25,232,89,0,208,140,146,24,
+0,136,140,6,160,132,128,18,64,140,89,16,160,180,152,1,9,
+132,89,3,224,189,140,24,32,145,146,2,204,189,89,16,32,129,
+154,40,32,225,154,8,32,177,154,216,252,255,8,0,0,0,0,0,
+48,144,144,112,228,0,0,0,144,156,144,212,145,157,53,40,32,
+129,144,108,237,255,9,0,48,128,140,0,178,0,0,0,32,129,
+146,188,241,255,8,0,0,0,0,0,0,0,0,17,22,144,92,0,0,136,
+140,8,3,0,8,0,0,0,0,160,22,0,9,12,0,0,9,232,17,0,8,0,
+0,0,0,128,5,0,9,84,1,0,11,120,3,0,9,196,3,0,8,128,192,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,215,0,0,16,216,0,0,
+48,216,0,0,80,216,0,0,112,216,0,0,144,216,0,0,176,216,0,
+0,208,216,0,0,240,216,0,0,16,217,0,0,48,217,0,0,240,215,
+0,0,145,48,0,90,0,20,32,140,0,0,0,0,0,48,160,140,204,
+192,0,0,20,1,33,89,10,0,0,18,38,32,92,51,0,52,241,133,
+216,215,0,0,31,152,153,89,0,208,236,140,0,16,236,146,0,
+52,129,140,96,194,0,0,0,0,0,10,0,52,161,140,160,194,0,0,
+145,32,5,90,16,57,144,140,192,230,0,0,0,144,188,144,16,
+22,128,92,22,0,0,21,0,52,137,140,240,215,0,0,52,0,0,8,
+0,0,0,0,0,52,161,140,128,194,0,0,38,0,141,61,0,52,161,
+140,128,192,0,0,16,61,169,144,144,192,0,0,0,16,181,144,
+21,0,173,89,22,65,173,89,0,96,141,140,0,52,161,140,240,
+215,0,0,151,32,5,90,0,144,140,146,16,22,128,92,18,0,0,
+21,0,52,185,140,160,194,0,0,48,0,0,8,0,52,161,140,128,
+192,0,0,16,61,169,144,144,192,0,0,0,16,181,144,21,0,173,
+89,22,65,173,89,0,96,173,140,14,64,189,61,0,52,185,140,
+128,194,0,0,23,22,128,92,0,0,0,10,0,0,0,0,0,0,0,0,0,
+0,0,0,0,20,240,140,148,0,0,0,30,22,152,92,0,0,240,140,
+0,30,136,92,0,20,160,140,0,0,0,0,0,48,168,140,220,193,0,
+0,21,1,165,89,0,52,189,140,128,192,0,0,0,0,144,140,0,48,
+176,140,192,230,0,0,0,52,165,140,144,192,0,0,0,208,133,
+144,0,16,173,144,21,192,173,89,16,65,173,89,21,128,172,89,
+0,144,173,146,4,32,173,144,21,192,173,89,16,65,173,89,21,
+128,172,89,4,160,173,146,8,32,173,144,17,208,136,89,145,
+240,2,90,21,192,173,89,16,65,173,89,21,128,172,89,8,160,
+173,146,22,16,179,89,12,32,165,140,180,255,255,22,0,208,4,
+132,0,0,0,10,0,20,240,140,12,0,0,0,30,22,128,92,0,0,
+240,140,0,16,4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,20,240,
+140,12,0,0,0,30,22,128,92,0,0,240,140,0,16,4,132,0,0,0,
+10,0,0,0,0,0,0,0,0,0,20,240,140,12,0,0,0,30,22,128,92,
+0,0,240,140,0,16,4,132,0,0,0,10,0,0,0,0,0,0,0,0,16,
+22,56,92,180,20,0,11,16,22,40,92,236,20,0,11,5,16,50,89,
+28,0,0,8,12,96,33,146,6,22,128,92,248,22,0,11,4,125,129,
+144,16,0,0,0,0,16,4,134,6,22,128,92,164,22,0,11,12,96,
+161,144,1,9,37,89,216,63,1,62,5,16,130,89,208,22,0,11,5,
+22,128,92,136,22,0,11,4,96,33,144,34,32,1,58,32,32,161,
+144,5,22,128,92,4,96,161,146,176,22,0,11,4,22,128,92,144,
+3,0,9,216,255,255,8,7,22,128,92,44,23,0,11,7,22,128,92,
+140,17,0,8,0,0,0,0,0,0,0,0,10,30,128,92,236,2,0,9,1,
+30,128,92,116,17,0,8,0,20,240,140,132,0,0,0,7,168,4,90,
+0,144,239,140,16,210,153,88,0,0,240,140,24,78,164,89,0,
+16,180,140,8,12,173,89,78,0,0,19,19,32,4,90,7,224,156,
+140,148,67,165,88,28,0,0,18,22,224,4,90,0,144,141,130,1,
+137,148,89,1,160,181,140,240,255,255,21,42,160,60,51,16,
+12,173,89,148,67,165,88,20,22,168,92,15,168,4,90,0,144,
+165,154,8,137,148,89,8,160,181,140,240,255,255,20,24,160,
+4,51,1,168,4,90,0,144,141,130,1,137,148,89,1,160,181,140,
+240,255,255,21,0,80,7,132,0,0,0,10,144,0,128,140,36,19,
+0,11,144,48,0,90,0,16,36,140,16,22,128,92,22,0,0,21,208,
+19,0,11,0,16,132,144,0,0,0,10,0,0,0,0,4,32,244,146,92,
+21,0,11,12,32,241,146,4,16,130,89,80,21,0,11,0,30,128,
+92,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,24,22,40,92,64,
+96,8,140,112,224,207,146,120,224,215,154,4,222,129,89,228,
+18,0,11,144,48,0,90,0,16,36,140,16,22,128,92,30,0,0,21,
+112,19,0,11,0,16,132,144,5,22,192,92,112,224,207,144,120,
+224,215,152,0,0,0,10,138,25,128,88,76,4,0,9,0,16,129,
+146,16,22,64,92,0,0,72,140,8,32,129,146,16,22,80,92,0,0,
+128,140,1,30,88,92,4,32,73,146,12,32,89,146,76,21,0,11,
+4,16,164,89,0,16,133,146,16,22,192,92,28,32,129,140,24,
+32,241,146,138,25,200,88,0,0,208,140,68,224,247,146,4,32,
+205,146,72,224,247,146,160,20,0,11,76,224,247,146,72,224,
+55,152,1,30,128,92,36,32,241,146,40,32,49,154,8,21,0,11,
+52,32,129,146,24,17,0,9,48,32,161,140,144,48,0,90,0,16,
+101,152,16,22,128,92,14,0,0,18,66,0,96,140,8,0,0,8,2,
+30,96,92,138,25,112,88,0,0,120,140,64,32,129,140,96,224,
+247,146,0,16,101,178,80,224,247,146,100,224,247,146,64,20,
+0,11,6,30,144,92,0,144,60,140,84,224,63,146,96,224,55,
+152,80,224,151,152,2,30,128,92,72,32,49,154,80,32,145,154,
+156,20,0,11,88,32,129,146,172,16,0,9,144,48,0,90,0,0,
+160,140,16,22,128,92,10,0,0,21,138,25,160,88,92,32,161,
+146,96,32,241,146,100,32,129,140,240,19,0,11,236,17,0,11,
+8,20,0,11,228,17,0,11,4,32,164,144,32,32,161,146,248,17,
+0,11,68,32,129,146,240,17,0,11,36,32,132,140,104,32,129,
+146,196,17,0,11,16,22,32,92,220,17,0,11,72,32,132,140,4,
+32,129,146,176,17,0,11,12,20,0,11,0,30,128,92,5,22,192,
+92,112,224,207,144,120,224,215,152,0,0,0,10,0,0,0,0,0,0,
+0,0,0,0,0,0,5,222,128,89,100,17,0,11,18,32,4,61,3,30,
+128,92,0,0,0,10,0,0,0,0,48,32,164,140,1,30,136,92,0,80,
+148,140,0,16,244,146,8,32,164,146,16,32,148,146,0,30,128,
+92,0,0,0,10,0,0,0,0,0,0,0,0,48,198,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,16,72,8,89,16,48,3,90,0,20,136,140,0,0,0,
+0,0,48,160,140,80,198,0,0,20,65,140,89,70,0,0,17,2,14,
+172,89,0,116,180,140,48,198,0,0,0,144,189,144,21,124,164,
+144,144,192,0,0,0,116,173,140,192,230,0,0,0,80,141,144,
+20,128,165,89,23,1,165,89,0,32,165,140,0,80,165,146,0,80,
+4,134,0,30,128,92,0,0,0,10,0,116,244,133,216,215,0,0,31,
+152,145,89,0,144,156,140,0,16,156,146,1,25,128,89,0,0,0,
+10,0,0,0,0,16,22,32,92,188,0,0,9,16,32,129,144,20,15,0,
+9,24,32,129,144,10,32,4,58,152,1,0,9,8,32,129,144,18,32,
+4,58,12,32,161,144,10,32,29,55,68,7,0,9,28,32,161,144,
+14,32,5,58,4,16,135,89,188,18,0,11,152,16,0,11,110,0,36,
+58,144,16,0,11,36,32,132,140,98,0,36,58,132,16,0,11,72,
+32,132,140,30,0,36,61,80,0,0,8,0,0,0,0,32,32,161,144,0,
+80,165,146,0,7,0,9,52,0,0,8,64,16,0,11,92,18,0,11,56,
+16,0,11,16,16,169,89,24,0,0,8,0,0,0,0,0,80,133,144,214,
+31,129,58,0,80,165,144,32,32,173,140,0,80,165,144,236,63,
+5,61,16,16,0,11,108,18,0,11,0,30,128,92,0,0,0,10,0,0,
+0,0,0,0,0,0,144,48,0,90,0,16,36,140,0,30,48,92,22,0,0,
+21,240,0,0,9,0,30,128,92,0,0,0,10,0,0,0,0,12,32,164,
+144,131,0,232,140,148,64,167,88,30,32,5,61,40,16,0,11,9,
+30,232,92,0,80,159,140,0,16,156,146,1,25,128,89,0,0,0,
+10,16,16,135,89,204,17,0,11,4,16,42,89,0,80,137,144,134,
+96,4,58,0,16,145,144,126,64,148,58,12,32,161,144,9,47,5,
+90,16,32,129,144,16,22,128,92,54,0,0,16,17,129,148,89,
+196,14,0,9,0,16,161,144,0,80,169,144,21,1,165,89,58,0,
+133,58,12,32,161,144,1,25,48,89,133,9,165,88,12,32,161,
+146,36,0,0,8,0,0,0,0,0,16,137,144,4,32,145,144,40,17,0,
+11,16,17,136,89,16,32,129,144,1,30,144,92,16,14,0,9,12,
+32,161,144,8,32,169,144,4,32,241,146,9,14,165,88,12,32,
+161,146,0,16,169,146,4,16,135,89,120,17,0,11,6,22,128,92,
+0,0,0,10,0,0,0,0,32,14,0,8,0,0,0,0,0,0,0,0,0,0,0,0,
+248,14,0,11,20,17,0,11,240,14,0,11,4,32,36,144,132,48,0,
+90,0,0,40,140,16,22,128,92,34,0,0,18,4,22,128,92,220,
+254,255,9,1,25,144,89,10,128,132,58,5,80,40,89,32,32,33,
+144,232,63,1,61,188,14,0,11,24,17,0,11,5,22,128,92,0,0,
+0,10,0,0,0,0,18,32,4,61,0,30,128,92,0,0,0,10,0,0,0,0,
+0,48,32,144,240,230,0,0,16,208,131,89,132,48,0,90,16,210,
+43,88,0,0,168,140,30,0,0,18,16,0,0,8,4,22,168,92,4,32,
+33,144,14,32,1,58,12,32,161,144,240,95,161,52,94,32,1,58,
+149,48,0,90,4,32,161,144,16,22,128,92,22,0,0,21,0,48,
+160,146,240,230,0,0,12,0,0,8,0,0,0,0,4,96,165,146,12,
+32,169,144,32,96,161,140,0,48,144,140,255,255,255,255,18,
+22,152,92,20,96,5,90,4,32,153,146,16,22,128,92,14,0,0,
+17,4,22,128,93,76,4,0,9,4,16,132,89,0,0,0,10,0,48,168,
+144,244,230,0,0,218,96,5,58,4,96,165,144,0,48,152,140,
+255,255,255,255,202,192,164,58,12,96,165,144,20,65,41,89,
+0,80,129,140,120,11,0,9,6,33,4,58,0,48,144,140,255,255,
+255,255,250,128,132,58,0,48,32,144,240,230,0,0,0,48,160,
+144,244,230,0,0,132,48,0,90,0,0,168,140,16,22,128,92,26,
+0,0,18,22,0,37,58,4,22,168,92,4,32,33,144,10,32,1,58,
+244,31,37,61,34,32,1,61,244,13,0,11,31,216,145,89,0,144,
+156,140,0,16,156,146,4,30,128,92,72,252,255,9,216,254,255,
+8,149,48,0,90,4,32,161,144,16,22,128,92,22,0,0,21,0,48,
+160,146,240,230,0,0,12,0,0,8,0,0,0,0,4,96,165,146,0,16,
+161,176,4,16,132,89,0,48,168,140,255,255,255,255,5,192,
+141,89,0,80,188,140,0,16,161,154,8,32,177,146,12,32,137,
+146,0,0,0,10,0,0,0,0,5,16,132,89,188,10,0,9,74,32,4,
+58,0,48,152,140,255,255,255,255,62,192,132,58,12,32,44,
+146,0,48,160,144,244,230,0,0,0,48,64,140,203,173,219,172,
+0,48,72,140,255,255,255,255,5,22,88,92,0,16,68,154,0,48,
+128,146,244,230,0,0,8,32,164,146,16,16,132,89,0,0,0,10,
+52,254,255,8,0,0,0,0,0,0,0,0,0,0,0,0,144,48,0,90,0,
+16,68,140,16,22,128,92,14,0,0,21,17,22,128,92,12,254,255,
+8,18,96,4,61,100,3,0,9,0,30,128,92,0,0,0,10,16,9,44,
+89,0,80,161,144,0,48,152,140,203,173,219,172,26,192,164,
+61,0,52,164,144,244,255,255,255,0,48,232,140,255,255,255,
+255,30,64,167,58,216,12,0,11,31,216,233,89,0,80,159,140,
+0,16,156,146,180,1,0,8,0,0,0,0,12,96,161,144,17,208,139,
+89,17,210,51,88,20,129,57,89,26,224,1,60,5,22,128,92,0,
+144,137,140,124,2,0,9,8,22,128,92,0,0,0,10,0,48,160,144,
+244,230,0,0,50,0,45,61,7,22,128,92,192,9,0,9,6,33,4,58,
+0,48,152,140,255,255,255,255,250,192,132,58,12,96,161,144,
+8,22,128,92,7,0,165,89,12,96,161,146,0,0,0,10,12,96,161,
+144,5,60,37,140,16,0,0,0,4,32,161,144,0,48,232,140,255,
+255,255,255,202,64,167,58,0,48,184,144,240,230,0,0,151,32,
+1,90,0,0,128,140,16,22,128,92,26,0,0,18,22,224,5,58,23,
+22,128,92,4,224,189,144,10,0,185,58,244,255,5,61,26,224,
+5,61,20,12,0,11,31,216,233,89,0,80,159,140,0,16,156,146,
+240,0,0,8,144,48,0,90,4,224,165,144,16,22,128,92,22,0,0,
+21,0,48,160,146,240,230,0,0,12,0,0,8,0,0,0,0,4,32,164,
+146,12,96,161,144,0,48,168,144,244,230,0,0,12,224,181,144,
+149,224,5,90,22,60,165,140,16,0,0,0,12,96,161,146,16,22,
+128,92,18,0,0,21,0,48,40,146,244,230,0,0,16,0,0,8,12,
+96,161,144,5,60,45,146,24,0,0,0,12,96,161,144,26,128,161,
+52,5,22,128,92,0,144,137,140,100,1,0,9,5,16,132,89,0,0,
+0,10,8,96,33,144,14,33,1,58,4,32,161,144,0,48,152,140,
+255,255,255,255,254,192,164,58,12,96,161,144,12,32,169,144,
+21,60,165,140,16,0,0,0,234,128,161,52,0,48,184,144,240,
+230,0,0,151,32,1,90,0,0,128,140,16,22,128,92,26,0,0,18,
+22,224,5,58,23,22,128,92,4,224,189,144,10,0,185,58,244,
+255,5,61,34,224,5,61,36,11,0,11,31,216,153,89,0,208,236,
+140,0,16,236,146,4,30,128,92,120,249,255,9,4,254,255,8,
+12,32,161,144,0,48,168,144,244,230,0,0,12,96,177,144,149,
+96,1,90,22,60,165,140,16,0,0,0,12,32,161,146,16,22,128,
+92,22,0,0,21,0,48,32,146,244,230,0,0,20,0,0,8,0,0,0,0,
+12,96,161,144,5,60,37,146,24,0,0,0,144,48,0,90,4,32,161,
+144,16,22,128,92,18,0,0,21,0,48,160,146,240,230,0,0,8,0,
+0,8,4,32,164,146,0,48,152,140,255,255,255,255,19,22,232,
+92,16,32,129,140,4,32,233,146,7,129,145,89,0,16,138,140,
+212,2,0,11,4,22,128,92,0,144,137,140,84,0,0,9,4,16,132,
+89,0,0,0,10,6,22,128,92,100,251,255,9,144,48,0,90,0,16,
+36,140,16,22,128,92,38,0,0,18,7,129,145,89,0,16,138,140,
+156,2,0,11,8,22,128,92,160,0,0,9,4,22,128,92,0,0,0,10,
+0,0,0,0,48,253,255,8,0,0,0,0,0,0,0,0,0,0,0,0,12,32,
+172,144,32,96,164,140,118,64,165,49,16,124,180,140,16,0,0,
+0,8,160,133,146,0,48,168,144,244,230,0,0,0,48,144,140,
+203,173,219,172,18,22,152,92,0,144,157,146,0,48,144,140,
+255,255,255,255,18,22,152,92,12,32,164,144,4,160,157,146,
+149,32,4,90,16,9,165,89,17,1,165,89,12,160,165,146,12,32,
+140,146,18,0,0,21,0,48,176,146,244,230,0,0,16,0,0,8,12,
+160,165,144,22,60,181,146,24,0,0,0,22,16,132,89,8,0,0,8,
+0,0,0,10,10,32,4,61,0,0,0,10,16,9,140,89,0,80,164,144,
+0,48,232,140,203,173,219,172,34,65,167,61,0,52,164,144,
+244,255,255,255,0,48,152,140,255,255,255,255,14,193,164,61,
+0,48,168,144,240,230,0,0,98,97,5,58,0,48,160,144,244,230,
+0,0,190,0,141,58,12,96,164,144,17,60,133,140,16,0,0,0,4,
+32,164,144,0,48,232,140,255,255,255,255,162,64,167,58,149,
+32,4,90,0,0,144,140,21,22,184,92,26,0,0,18,22,96,5,58,
+23,22,144,92,4,224,189,144,10,0,188,58,244,255,5,61,26,
+224,5,61,28,9,0,11,31,216,233,89,0,80,159,140,0,16,156,
+146,172,0,0,8,12,96,164,144,0,48,168,144,244,230,0,0,12,
+32,180,144,149,32,4,90,22,60,165,140,16,0,0,0,12,96,164,
+146,16,22,128,92,22,0,0,21,0,48,136,146,244,230,0,0,20,
+0,0,8,0,0,0,0,12,96,164,144,17,60,141,146,24,0,0,0,22,
+160,4,61,4,224,165,144,0,48,160,146,240,230,0,0,12,0,0,
+8,4,224,165,144,4,160,164,146,8,96,132,144,150,32,4,58,4,
+32,164,144,0,48,152,140,255,255,255,255,134,192,164,58,0,
+48,184,144,240,230,0,0,22,0,188,58,18,224,5,58,4,224,189,
+144,10,0,188,58,248,255,5,61,30,224,5,61,112,8,0,11,31,
+216,153,89,0,208,236,140,0,16,236,146,4,30,128,92,196,246,
+255,8,12,32,164,144,0,48,168,144,244,230,0,0,12,96,180,
+144,149,96,4,90,22,60,165,140,16,0,0,0,12,32,164,146,16,
+22,128,92,22,0,0,21,0,48,128,146,244,230,0,0,0,0,0,10,
+0,0,0,0,12,32,164,144,16,60,133,146,24,0,0,0,0,0,0,10,
+0,48,160,144,240,230,0,0,0,48,136,146,240,230,0,0,4,96,
+164,146,0,0,0,10,144,64,132,112,14,32,4,61,0,30,128,92,
+0,0,0,10,224,248,255,9,144,48,0,90,0,16,36,140,16,22,
+128,92,22,0,0,18,0,52,148,144,252,255,255,255,0,30,136,
+92,88,243,255,11,4,22,128,92,0,0,0,10,0,20,240,140,220,
+0,0,0,34,162,4,59,16,96,4,90,0,144,239,140,18,64,172,89,
+198,0,0,18,21,33,4,90,17,210,152,88,2,1,0,17,19,96,4,
+90,0,208,180,144,16,210,160,88,4,224,156,140,178,0,0,21,
+16,32,5,90,4,9,165,89,108,0,0,18,8,8,165,89,0,16,140,
+140,22,22,184,92,32,0,240,140,128,139,148,90,130,0,0,18,
+8,136,247,89,0,80,188,130,8,204,189,89,1,96,140,140,232,
+31,141,53,0,208,188,144,146,48,1,90,4,224,156,140,30,132,
+141,93,62,0,0,20,23,22,176,92,0,208,188,144,132,137,148,
+89,0,16,141,146,4,8,165,89,220,255,255,8,132,137,148,89,
+0,208,180,144,4,200,156,89,0,16,141,146,146,48,1,90,4,8,
+165,89,0,144,141,140,228,255,255,19,30,160,4,58,129,137,
+148,89,0,16,141,130,128,168,4,90,1,32,165,140,8,76,140,
+89,236,255,255,21,0,30,240,92,0,80,7,132,0,0,0,10,16,32,
+5,90,0,208,188,144,131,72,140,88,3,78,244,89,128,255,255,
+18,148,67,140,88,16,96,4,90,4,32,165,140,30,132,173,93,0,
+16,140,140,14,0,0,17,23,22,176,92,4,224,156,140,21,22,
+184,92,56,255,255,8,21,210,184,88,23,96,5,90,4,201,157,
+89,18,0,140,89,17,210,160,88,182,0,0,21,17,32,5,90,0,
+208,188,144,4,201,156,89,112,0,0,18,23,22,176,92,32,0,
+240,140,128,139,148,90,134,255,255,18,136,142,181,89,0,116,
+140,140,255,255,255,255,17,32,5,90,0,80,180,130,8,137,247,
+89,224,255,255,21,0,208,180,144,146,48,1,90,0,244,156,140,
+252,255,255,255,30,132,141,93,0,144,189,140,4,9,165,89,54,
+0,0,20,0,208,180,144,132,137,148,89,0,16,141,146,216,255,
+255,8,132,137,148,89,0,208,188,144,4,201,156,89,0,16,141,
+146,146,48,1,90,4,9,165,89,0,208,141,140,228,255,255,19,
+22,191,4,58,136,78,140,89,4,32,165,140,129,9,165,89,148,
+32,4,90,0,16,141,130,136,78,140,89,240,255,255,21,244,254,
+255,8,17,32,5,90,0,208,189,144,131,72,173,88,0,208,180,
+144,3,78,245,89,124,255,255,18,148,67,173,88,17,96,5,90,
+30,132,173,93,16,0,0,20,22,22,184,92,8,0,0,18,4,201,156,
+89,21,22,176,92,48,255,255,8,30,22,232,92,176,254,255,8,
+0,0,0,0,16,72,8,89,0,48,128,140,0,231,0,0,138,25,136,
+88,242,0,232,140,29,48,0,102,36,32,4,62,0,30,32,92,0,48,
+40,140,176,86,0,0,64,224,167,140,0,30,48,92,72,224,247,
+146,0,16,37,154,20,0,0,8,64,224,135,140,0,48,136,140,0,
+231,0,0,24,0,0,9,64,224,135,176,0,48,144,146,208,235,0,
+0,0,0,0,10,0,0,0,0,16,22,64,92,17,22,32,92,4,22,128,
+92,220,1,0,11,0,30,48,92,0,48,56,140,0,235,0,0,0,16,50,
+154,0,16,161,192,128,40,5,90,1,8,44,89,4,64,41,89,164,0,
+0,18,152,0,0,8,0,16,161,192,31,88,152,89,16,192,164,58,
+12,32,77,58,8,32,109,58,16,32,85,61,4,80,32,89,0,16,161,
+192,224,63,5,61,0,16,161,192,100,32,5,58,0,16,170,144,21,
+80,160,89,21,57,32,146,0,235,0,0,0,16,162,146,0,16,161,
+192,56,32,5,58,31,88,144,89,48,128,164,58,44,32,77,58,40,
+32,109,58,36,32,85,58,4,80,32,89,0,16,161,192,24,32,5,
+58,31,88,152,89,16,192,164,58,12,32,77,58,8,32,109,58,
+228,63,85,61,0,16,161,192,12,32,5,58,0,16,241,130,4,80,
+32,89,0,16,161,192,12,32,5,58,0,16,162,144,132,63,149,59,
+0,16,162,144,20,57,240,146,0,235,0,0,0,80,161,192,128,40,
+5,90,0,30,32,92,204,0,0,18,0,48,40,146,80,235,0,0,5,22,
+128,92,236,0,0,11,1,72,41,89,16,64,41,89,0,80,161,192,
+128,40,5,90,1,30,32,92,164,0,0,18,0,48,48,140,84,235,0,
+0,0,144,41,146,5,22,128,92,192,0,0,11,1,72,41,89,16,64,
+41,89,0,80,161,192,128,40,5,90,4,80,32,89,120,0,0,18,4,
+160,41,146,5,22,128,92,156,0,0,11,1,72,41,89,16,64,41,
+89,0,80,161,192,128,40,5,90,4,80,32,89,84,0,0,18,8,160,
+41,146,5,22,128,92,120,0,0,11,1,72,41,89,16,64,41,89,0,
+80,161,192,128,40,5,90,4,80,32,89,48,0,0,18,12,160,41,
+146,5,22,128,92,84,0,0,11,1,72,41,89,16,64,41,89,0,80,
+161,192,6,16,52,89,148,48,0,90,4,80,32,89,8,0,0,18,108,
+63,225,59,0,48,152,140,80,235,0,0,4,57,240,146,80,235,0,
+0,19,22,144,92,8,32,146,146,0,0,0,10,0,0,0,0,0,0,0,0,
+0,0,0,0,0,20,240,140,48,0,0,0,30,22,136,92,0,30,240,92,
+0,16,164,192,0,30,168,92,12,0,0,8,0,16,164,192,21,80,
+168,89,148,48,0,90,16,80,128,89,240,255,255,21,21,22,128,
+92,0,80,4,132,0,0,0,10,0,0,0,0,1,1,232,140,29,48,0,
+102,0,0,0,10,0,0,0,0,0,20,240,140,32,0,0,0,30,22,128,
+92,0,30,240,92,0,48,160,140,80,205,3,0,20,22,168,92,0,
+48,168,146,224,235,0,0,0,16,4,132,0,0,0,10,0,0,0,0,144,
+48,0,90,0,48,168,144,224,235,0,0,12,0,0,21,21,22,128,92,
+0,0,0,10,21,208,163,89,20,210,163,88,0,32,165,140,21,32,
+5,90,20,60,180,140,255,255,255,255,20,0,0,17,16,0,181,52,
+0,48,144,140,79,221,3,0,32,128,180,54,132,2,0,11,12,30,
+144,92,18,22,136,92,0,16,140,146,0,48,128,140,255,255,255,
+255,0,0,0,10,22,80,168,89,12,128,173,51,0,48,168,140,79,
+221,3,0,20,22,128,92,0,48,168,146,224,235,0,0,0,0,0,10,
+0,48,136,140,80,205,3,0,20,64,132,52,1,9,164,89,0,48,
+144,140,79,221,3,0,28,128,164,54,44,2,0,11,12,30,144,92,
+18,22,136,92,0,16,140,146,1,25,128,89,0,0,0,10,0,48,128,
+146,224,235,0,0,0,30,128,92,0,0,0,10,0,0,0,0,0,0,0,0,
+0,0,0,0,234,0,232,140,29,48,0,102,144,48,0,90,16,22,32,
+92,20,0,0,18,228,1,0,11,0,16,36,146,1,25,128,89,0,0,0,
+10,0,30,128,92,0,0,0,10,0,0,0,0,16,72,8,89,64,224,143,
+140,235,0,232,140,29,48,0,102,144,48,0,90,16,22,32,92,20,
+0,0,18,172,1,0,11,0,16,36,146,1,25,128,89,0,0,0,10,64,
+224,135,144,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,16,72,8,
+89,64,224,159,140,233,0,232,140,29,48,0,102,144,48,0,90,
+16,22,32,92,20,0,0,18,108,1,0,11,0,16,36,146,1,25,128,
+89,0,0,0,10,64,224,135,144,0,0,0,10,0,0,0,0,0,0,0,0,
+0,0,0,0,239,0,232,140,29,48,0,102,144,48,0,90,16,22,32,
+92,20,0,0,18,52,1,0,11,0,16,36,146,1,25,128,89,0,0,0,
+10,0,30,128,92,0,0,0,10,0,0,0,0,16,72,8,89,64,224,159,
+140,232,0,232,140,29,48,0,102,144,48,0,90,16,22,32,92,20,
+0,0,18,252,0,0,11,0,16,36,146,1,25,128,89,0,0,0,10,64,
+224,135,144,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,224,253,
+255,8,0,0,0,0,0,0,0,0,0,0,0,0,0,20,240,140,20,0,0,0,
+30,22,136,92,0,30,240,92,0,48,128,140,240,235,0,0,0,80,
+4,132,0,0,0,10,0,20,240,140,20,0,0,0,30,22,136,92,0,30,
+240,92,0,48,128,140,128,236,0,0,0,80,4,132,0,0,0,10,0,
+20,240,140,20,0,0,0,30,22,136,92,0,30,240,92,0,48,128,
+140,240,236,0,0,0,80,4,132,0,0,0,10,0,20,240,140,20,0,
+0,0,30,22,136,92,0,30,240,92,0,48,128,140,240,235,0,0,0,
+80,4,132,0,0,0,10,0,20,240,140,20,0,0,0,30,22,136,92,0,
+30,240,92,0,48,128,140,128,236,0,0,0,80,4,132,0,0,0,10,
+0,20,240,140,20,0,0,0,30,22,136,92,0,30,240,92,0,48,128,
+140,240,236,0,0,0,80,4,132,0,0,0,10,0,20,240,140,20,0,
+0,0,30,22,136,92,0,30,240,92,0,48,128,140,240,236,0,0,0,
+80,4,132,0,0,0,10,0,20,240,140,12,0,0,0,30,22,128,92,0,
+30,240,92,0,16,4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,20,
+240,140,12,0,0,0,30,22,128,92,0,30,240,92,0,16,4,132,0,
+0,0,10,0,0,0,0,0,0,0,0,0,20,240,140,12,0,0,0,30,22,
+128,92,0,30,240,92,0,16,4,132,0,0,0,10,0,0,0,0,0,0,0,
+0,0,20,240,140,12,0,0,0,30,22,128,92,0,30,240,92,0,16,
+4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,20,240,140,12,0,0,0,
+30,22,128,92,0,30,240,92,0,16,4,132,0,0,0,10,0,0,0,0,
+0,0,0,0,0,20,240,140,12,0,0,0,30,22,128,92,0,30,240,92,
+0,16,4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,20,240,140,12,
+0,0,0,30,22,128,92,0,30,240,92,0,16,4,132,0,0,0,10,0,
+0,0,0,0,0,0,0,0,20,240,140,12,0,0,0,30,22,128,92,0,30,
+240,92,0,16,4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,20,240,
+140,12,0,0,0,30,22,128,92,0,30,240,92,0,16,4,132,0,0,0,
+10,0,0,0,0,0,0,0,0,0,20,240,140,12,0,0,0,30,22,128,92,
+0,30,240,92,0,16,4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,20,
+240,140,12,0,0,0,30,22,128,92,0,30,240,92,0,16,4,132,0,
+0,0,10,0,0,0,0,0,0,0,0,0,20,240,140,16,0,0,0,30,22,
+136,92,0,30,240,92,18,22,128,92,0,80,4,132,0,0,0,10,0,
+0,0,0,0,20,240,140,12,0,0,0,30,22,128,92,0,30,240,92,0,
+16,4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,20,240,140,12,0,
+0,0,30,22,128,92,0,30,240,92,0,16,4,132,0,0,0,10,0,0,
+0,0,0,0,0,0,0,20,240,140,12,0,0,0,30,22,128,92,0,30,
+240,92,0,16,4,132,0,0,0,10,0,0,0,0,0,0,0,0,0,20,240,
+140,12,0,0,0,30,22,128,92,0,30,240,92,0,16,4,132,0,0,0,
+10,0,0,0,0,0,0,0,0,0,20,240,140,12,0,0,0,30,22,136,92,
+0,30,240,92,0,80,4,132,0,0,0,10,0,0,0,0,0,0,0,0,240,
+252,255,8,0,0,0,0,0,0,0,0,0,0,0,0,0,20,240,140,12,0,
+0,0,30,22,128,92,0,30,240,92,0,16,4,132,0,0,0,10,0,0,
+0,0,0,0,0,0,0,20,240,140,20,0,0,0,30,22,136,92,0,30,
+240,92,1,25,168,89,149,2,132,100,0,80,4,132,0,0,0,10,0,
+20,240,140,12,0,0,0,30,22,128,92,0,30,240,92,0,16,4,132,
+0,0,0,10,0,0,0,0,0,0,0,0,144,48,0,90,19,4,32,140,86,
+0,0,20,16,22,40,92,133,16,96,100,66,0,0,16,12,17,101,89,
+140,48,0,90,18,0,0,20,12,70,137,89,0,30,128,92,20,0,0,
+8,12,17,104,89,13,68,137,89,32,32,107,140,13,70,129,89,
+20,78,140,88,12,1,33,89,20,14,33,89,132,67,140,88,0,0,0,
+10,0,30,128,93,248,255,255,8,16,17,40,89,133,16,96,100,
+12,17,101,89,140,48,0,90,18,0,0,20,12,70,137,89,0,30,
+128,92,20,0,0,8,12,17,104,89,13,68,137,89,32,32,107,140,
+13,70,129,89,20,78,140,88,12,1,33,89,20,14,33,89,132,67,
+140,88,159,73,140,88,176,255,255,8,1,78,36,89,0,48,96,
+140,0,0,224,255,4,32,3,90,106,0,0,17,21,12,33,89,10,78,
+44,89,158,73,41,88,31,78,41,88,22,12,100,89,133,3,43,88,
+145,48,0,90,29,4,96,140,38,0,0,20,4,32,3,90,4,1,35,89,
+14,0,0,17,4,68,129,89,0,0,0,10,1,25,128,89,31,14,132,
+88,244,255,255,8,4,32,3,90,4,1,35,89,18,0,0,17,4,68,41,
+89,5,17,128,89,220,255,255,8,31,94,128,89,212,255,255,8,
+0,30,128,92,204,255,255,8,1,78,36,89,0,48,96,140,0,0,
+224,255,4,32,3,90,66,0,0,17,21,12,33,89,11,78,44,89,159,
+73,41,88,21,12,100,89,133,3,43,88,145,48,0,90,38,0,0,20,
+30,4,96,140,4,32,3,90,18,0,0,17,4,1,35,89,4,68,129,89,
+0,0,0,10,1,25,128,89,248,255,255,8,0,30,128,92,240,255,
+255,8,1,78,36,89,0,48,96,140,255,255,223,255,12,32,1,90,
+4,49,0,90,82,0,0,22,21,12,33,89,128,3,96,140,12,1,33,
+89,255,0,96,140,4,32,3,90,46,0,0,19,23,14,33,89,12,78,
+44,89,9,76,41,89,132,67,33,88,29,12,44,89,28,47,4,90,5,
+0,129,91,31,111,4,90,159,15,132,88,0,0,0,10,132,48,0,90,
+48,0,0,20,24,0,0,8,42,0,0,18,0,48,96,140,0,0,224,255,
+4,32,3,90,18,0,0,17,0,48,128,140,0,0,128,127,204,255,
+255,8,1,25,128,89,204,255,255,8,0,30,128,92,188,255,255,
+8,31,12,84,89,31,142,82,89,1,14,36,89,0,48,96,140,255,
+255,255,254,12,32,1,90,4,49,0,90,58,0,0,22,24,12,33,89,
+0,48,96,140,128,252,255,255,12,1,33,89,20,14,33,89,9,14,
+44,89,12,76,41,89,132,67,137,88,29,14,132,89,145,131,138,
+88,0,0,0,10,0,30,128,93,244,255,255,8,250,255,255,18,0,
+48,96,140,0,0,0,255,12,32,1,90,22,0,0,20,0,48,136,140,
+0,0,240,127,0,30,128,92,208,255,255,8,1,25,128,89,1,25,
+136,89,200,255,255,8,15,140,84,89,31,142,82,89,17,142,36,
+89,17,12,33,89,31,111,4,90,90,0,0,16,0,48,96,140,0,60,
+0,0,12,1,33,89,255,7,96,140,4,32,3,90,46,0,0,19,31,78,
+140,88,11,76,60,89,21,78,52,89,20,14,33,89,135,3,137,88,
+10,47,4,90,11,12,132,89,6,0,132,91,138,67,140,88,0,0,0,
+10,34,0,0,17,0,48,136,140,0,0,240,127,0,30,128,92,232,
+255,255,8,144,67,140,88,17,48,0,90,220,255,255,18,1,25,
+128,89,1,25,136,89,212,255,255,8,31,72,84,88,8,0,0,8,31,
+78,84,88,1,78,36,89,0,48,96,140,0,0,224,255,4,32,3,90,
+10,0,0,17,10,22,136,92,0,0,0,10,31,200,156,88,1,78,36,
+89,1,206,44,89,21,94,96,89,4,0,107,89,13,32,3,90,154,1,
+0,22,5,0,107,89,13,32,3,90,142,1,0,22,11,78,60,89,21,
+12,100,89,11,14,52,89,140,195,57,88,159,201,57,88,11,206,
+76,89,21,140,100,89,11,142,68,89,140,67,74,88,159,73,74,
+88,21,12,33,89,21,76,41,89,31,76,84,89,31,142,82,89,4,
+96,1,90,32,0,104,140,52,0,0,20,5,1,97,89,12,96,3,90,16,
+0,0,20,9,22,64,92,0,30,72,92,13,1,99,89,12,4,66,89,12,
+65,107,89,13,70,106,89,12,68,74,89,141,3,66,88,52,0,0,8,
+4,65,97,89,12,96,3,90,16,0,0,20,7,22,48,92,0,30,56,92,
+13,1,99,89,12,132,49,89,12,65,107,89,13,198,105,89,12,
+196,57,89,141,131,49,88,5,22,32,92,17,195,100,88,31,47,3,
+90,62,0,0,18,8,128,49,91,9,192,57,91,150,0,0,16,1,140,
+49,89,31,206,97,89,1,204,57,89,140,131,49,88,1,8,33,89,
+255,7,96,140,4,32,3,90,116,0,0,20,12,22,32,92,0,30,48,
+93,104,0,0,8,8,129,49,91,9,193,57,91,22,0,0,18,0,56,0,
+90,6,17,48,91,7,17,56,91,31,136,82,88,135,16,96,100,32,
+0,0,18,134,16,96,100,102,0,0,16,12,209,103,89,12,134,57,
+89,0,30,48,92,32,32,99,140,28,0,0,8,12,132,105,89,1,76,
+107,89,12,209,103,89,12,198,57,89,12,134,49,89,141,195,57,
+88,12,1,33,89,132,48,0,90,54,0,0,20,10,175,1,90,31,206,
+57,88,11,140,129,89,21,206,105,89,11,204,137,89,20,14,33,
+89,145,3,137,88,13,0,132,91,10,64,140,91,0,0,0,10,0,30,
+128,93,248,255,255,8,0,30,128,92,10,22,136,92,236,255,255,
+8,0,48,96,140,0,0,224,255,12,32,1,90,70,0,0,18,90,0,0,
+20,12,96,1,90,50,0,0,18,78,0,0,20,132,3,108,88,13,48,0,
+90,22,0,0,18,133,131,108,88,13,48,0,90,70,254,255,21,176,
+255,255,8,133,131,108,88,13,48,0,90,50,0,0,18,18,22,128,
+93,156,255,255,8,12,96,1,90,22,0,0,20,146,255,255,17,17,
+195,100,88,140,48,0,90,134,255,255,19,1,25,128,89,1,25,
+136,89,120,255,255,8,145,192,140,88,0,30,128,92,108,255,
+255,8,17,195,28,88,31,204,24,89,31,206,24,89,1,78,36,89,
+1,206,44,89,21,94,96,89,4,0,107,89,13,32,3,90,238,0,0,
+22,5,0,107,89,13,32,3,90,226,0,0,22,11,78,60,89,1,204,
+57,89,10,14,52,89,22,12,108,89,141,195,57,88,158,201,57,
+88,11,206,76,89,11,142,68,89,21,140,108,89,141,67,74,88,
+159,73,74,88,21,12,33,89,21,76,41,89,137,128,81,103,10,
+22,104,92,0,30,96,92,137,0,99,103,13,22,80,92,1,12,106,
+89,0,30,96,92,137,0,99,103,11,64,99,103,0,56,0,90,13,
+129,82,91,0,201,90,91,13,129,82,91,0,201,90,91,31,239,2,
+90,16,0,0,18,10,128,82,91,11,192,90,91,1,9,33,89,255,35,
+33,140,5,1,33,89,254,7,96,140,12,32,1,90,46,0,0,20,10,
+175,2,90,11,140,130,89,21,206,106,89,11,204,138,89,20,78,
+140,88,20,14,33,89,145,3,137,88,13,0,132,91,3,64,140,91,
+0,0,0,10,132,48,0,90,16,0,0,20,1,8,35,89,0,30,80,93,
+200,255,255,8,0,30,32,92,0,30,80,93,188,255,255,8,0,48,
+96,140,0,0,224,255,12,32,1,90,90,0,0,18,94,0,0,20,12,
+96,1,90,66,0,0,18,82,0,0,20,132,3,108,88,13,48,0,90,38,
+0,0,18,133,131,108,88,13,48,0,90,242,254,255,21,0,48,136,
+140,0,0,240,127,0,30,128,92,131,67,140,88,148,255,255,8,
+133,131,108,88,13,48,0,90,26,0,0,18,0,30,128,93,131,67,
+140,88,124,255,255,8,12,96,1,90,210,255,255,17,1,25,128,
+89,1,25,136,89,104,255,255,8,0,0,0,0,0,0,0,0,0,0,0,0,
+17,195,28,88,31,204,24,89,31,206,24,89,1,78,36,89,1,206,
+44,89,21,94,96,89,4,0,107,89,13,32,3,90,214,0,0,22,5,0,
+107,89,13,32,3,90,202,0,0,22,11,78,60,89,11,14,52,89,21,
+12,108,89,141,195,57,88,11,206,76,89,11,142,68,89,21,140,
+108,89,141,67,74,88,159,201,57,88,159,73,74,88,21,12,33,
+89,21,76,41,89,7,64,98,103,6,64,82,103,0,120,0,90,12,
+192,98,91,13,16,104,91,7,0,114,103,10,128,115,91,12,192,
+99,91,13,16,104,91,31,111,3,90,16,0,0,18,12,0,99,91,13,
+64,107,91,1,9,33,89,4,124,33,140,2,252,255,255,254,7,80,
+140,10,32,1,90,46,0,0,20,10,47,3,90,11,12,131,89,21,78,
+83,89,11,76,139,89,20,78,140,88,20,14,33,89,145,3,137,88,
+10,0,132,91,3,64,140,91,0,0,0,10,132,48,0,90,16,0,0,20,
+1,136,34,89,0,30,96,93,200,255,255,8,0,30,32,92,0,30,96,
+93,188,255,255,8,31,204,24,89,31,206,24,89,0,48,96,140,0,
+0,224,255,12,32,1,90,58,0,0,18,70,0,0,20,12,96,1,90,62,
+0,0,20,70,0,0,18,132,3,108,88,13,48,0,90,18,0,0,18,133,
+131,108,88,13,48,0,90,2,255,255,21,0,30,128,93,131,67,
+140,88,148,255,255,8,12,96,1,90,14,0,0,20,5,48,0,90,30,
+0,0,21,1,25,128,89,1,25,136,89,120,255,255,8,132,3,108,
+88,13,48,0,90,238,255,255,18,0,48,136,140,0,0,240,127,
+131,67,140,88,0,30,128,92,88,255,255,8,0,0,0,0,1,78,36,
+89,1,206,52,89,0,48,96,140,0,0,224,255,12,32,1,90,126,0,
+0,22,12,160,1,90,198,0,0,22,132,131,49,88,6,48,0,90,98,
+0,0,18,159,77,100,89,31,78,60,88,16,3,51,88,7,3,59,88,
+0,56,0,90,12,129,49,91,0,201,57,91,159,205,100,89,31,206,
+76,88,18,3,67,88,9,3,75,88,0,56,0,90,12,1,66,91,0,73,
+74,91,135,96,2,90,1,25,128,89,34,0,0,20,1,30,128,92,26,
+0,0,17,6,32,2,90,18,0,0,17,1,25,128,89,10,0,0,20,0,30,
+128,92,0,0,0,10,12,32,1,90,30,0,0,18,3,30,128,92,240,
+255,255,8,1,30,128,92,232,255,255,8,1,25,128,89,224,255,
+255,8,12,160,1,90,230,255,255,20,30,0,0,17,17,195,100,88,
+140,48,0,90,198,255,255,19,31,111,4,90,214,255,255,16,216,
+255,255,8,145,48,0,90,208,255,255,20,196,255,255,8,12,160,
+1,90,182,255,255,20,31,239,4,90,182,255,255,18,184,255,
+255,8,0,0,0,10
+};
+
+int pca200e_microcode_size_4 = sizeof(pca200e_microcode_4);
diff --git a/sbin/atm/ilmid/Makefile b/sbin/atm/ilmid/Makefile
new file mode 100644
index 0000000..67d9bab
--- /dev/null
+++ b/sbin/atm/ilmid/Makefile
@@ -0,0 +1,40 @@
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $Id: Makefile,v 1.5 1998/07/10 16:01:58 jpt Exp $
+# $FreeBSD$
+
+PROG= ilmid
+MAN= ilmid.8
+
+.if ${MACHINE_ARCH} == "arm"
+WARNS?= 3
+.else
+WARNS?= 6
+.endif
+
+CFLAGS+= -I${.CURDIR}/../../../sys
+
+DPADD= ${LIBATM}
+LDADD= -latm
+
+.include <bsd.prog.mk>
diff --git a/sbin/atm/ilmid/ilmid.8 b/sbin/atm/ilmid/ilmid.8
new file mode 100644
index 0000000..8ffa9fe
--- /dev/null
+++ b/sbin/atm/ilmid/ilmid.8
@@ -0,0 +1,110 @@
+.\"
+.\" ===================================
+.\" HARP | Host ATM Research Platform
+.\" ===================================
+.\"
+.\"
+.\" This Host ATM Research Platform ("HARP") file (the "Software") is
+.\" made available by Network Computing Services, Inc. ("NetworkCS")
+.\" "AS IS". NetworkCS does not provide maintenance, improvements or
+.\" support of any kind.
+.\"
+.\" NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+.\" INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+.\" SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+.\" In no event shall NetworkCS be responsible for any damages, including
+.\" but not limited to consequential damages, arising from or relating to
+.\" any use of the Software or related support.
+.\"
+.\" Copyright 1994-1998 Network Computing Services, Inc.
+.\"
+.\" Copies of this Software may be made, however, the above copyright
+.\" notice must be reproduced on all copies.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 14, 1998
+.Dt ILMID 8
+.Os
+.Sh NAME
+.Nm ilmid
+.Nd "simple ILMI ATM address registration daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar level
+.Op Fl f
+.Op Fl r
+.Sh DESCRIPTION
+The
+.Nm
+utility is a HARP ATM daemon that performs the ILMI ATM address registration
+procedures with an ATM network switch.
+It is normally invoked at boot time
+from the ATM startup script.
+.Pp
+For each ATM interface with a UNI signalling manager attached,
+.Nm
+will open an ILMI PVC (VPI = 0, VCI = 16) and register the interface's
+ATM address with the switch.
+As part of the address registration procedure,
+the ATM switch will notify the endsystem (local host) of the
+.Dq "network prefix"
+portion of the endsystem's ATM address and
+.Nm
+will notify the switch of the endsystem's
+.Dq "user part"
+of its address
+(typically the interface card MAC address).
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl d Ar level
+Specify the debug level for optional protocol tracing.
+Messages are
+written to
+.Pa /var/log/ilmid .
+.It Fl f
+Causes
+.Nm
+to run in the foreground.
+.It Fl r
+Causes
+.Nm
+to issue a coldStart TRAP on all ATM interfaces it is able to open and exit.
+.El
+.Sh NOTES
+This daemon does not fully conform to the ATM Forum ILMI specifications.
+In particular, it
+does not make any queries of the network side to ensure
+that the ATM Address table is empty.
+It also does not implement any
+of the ATM Forum MIB that is specified as part of ILMI.
+.Pp
+The
+.Nm
+utility will increment the debug level when it receives a
+.Dv SIGUSR1
+signal and will
+decrement the debug level when it receives a
+.Dv SIGUSR2
+signal.
+.Sh SEE ALSO
+The ATM Forum,
+.%T "ATM User-Network Interface, Version 3.1 (UNI 3.1) Specification"
+for details on the ILMI protocols and address registration
+procedures.
+.Sh COPYRIGHT
+Copyright (c) 1994-1998, Network Computing Services, Inc.
+.Sh AUTHORS
+.An John Cavanaugh ,
+Network Computing Services, Inc.
+.An Mike Spengler ,
+Network Computing Services, Inc.
+.An Joseph Thomas ,
+Network Computing Services, Inc.
+.Sh ACKNOWLEDGMENTS
+This software was developed with the support of the
+Defense Advanced Research Projects Agency (DARPA).
+.Sh BUGS
+Please report any bugs to
+.Aq harp\-bugs@magic.net .
diff --git a/sbin/atm/ilmid/ilmid.c b/sbin/atm/ilmid/ilmid.c
new file mode 100644
index 0000000..4a1e5ec
--- /dev/null
+++ b/sbin/atm/ilmid/ilmid.c
@@ -0,0 +1,2725 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ */
+
+/*
+ * User utilities
+ * --------------
+ *
+ * Implement very minimal ILMI address registration.
+ *
+ * Implement very crude and basic support for "cracking" and
+ * "encoding" SNMP PDU's to support ILMI prefix and NSAP address
+ * registration. Code is not robust nor is it meant to provide any
+ * "real" SNMP support. Much of the code expects predetermined values
+ * and will fail if anything else is found. Much of the "encoding" is
+ * done with pre-computed PDU's.
+ *
+ * See "The Simple Book", Marshall T. Rose, particularly chapter 5,
+ * for ASN and BER information.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sigmgr.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+#include <dev/hfa/fore_aali.h>
+#include <dev/hfa/fore_slave.h>
+#include <dev/hfa/fore_stats.h>
+
+#include <err.h>
+#include <errno.h>
+#include <libatm.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+#define MAX_LEN 9180
+
+#define MAX_UNITS 8
+
+/*
+ * Define some ASN types
+ */
+#define ASN_INTEGER 0x02
+#define ASN_OCTET 0x04
+#define ASN_NULL 0x05
+#define ASN_OBJID 0x06
+#define ASN_SEQUENCE 0x30
+#define ASN_IPADDR 0x40
+#define ASN_TIMESTAMP 0x43
+
+static const char *Var_Types[] = {
+ "",
+ "",
+ "ASN_INTEGER",
+ "",
+ "ASN_OCTET",
+ "ASN_NULL",
+ "ASN_OBJID"
+};
+
+/*
+ * Define SNMP PDU types
+ */
+#define PDU_TYPE_GET 0xA0
+#define PDU_TYPE_GETNEXT 0xA1
+#define PDU_TYPE_GETRESP 0xA2
+#define PDU_TYPE_SET 0xA3
+#define PDU_TYPE_TRAP 0xA4
+
+static const char *const PDU_Types[] = {
+ "GET REQUEST",
+ "GETNEXT REQUEST",
+ "GET RESPONSE",
+ "SET REQUEST",
+ "TRAP",
+ " ?? ",
+ " ??? "
+};
+
+/*
+ * Define TRAP codes
+ */
+#define TRAP_COLDSTART 0
+#define TRAP_WARMSTART 1
+#define TRAP_LINKDOWN 2
+#define TRAP_LINKUP 3
+#define TRAP_AUTHFAIL 4
+#define TRAP_EGPLOSS 5
+#define TRAP_ENTERPRISE 6
+
+/*
+ * Define SNMP Version numbers
+ */
+#define SNMP_VERSION_1 1
+#define SNMP_VERSION_2 2
+
+/*
+ * SNMP Error-status values
+ */
+#define SNMP_ERR_NOERROR 0
+#define SNMP_ERR_TOOBIG 1
+#define SNMP_ERR_NOSUCHNAME 2
+#define SNMP_ERR_BADVALUE 3
+#define SNMP_ERR_READONLY 4
+#define SNMP_ERR_GENERR 5
+
+/*
+ * Max string length for Variable
+ */
+#define STRLEN 128
+
+/*
+ * Unknown variable
+ */
+#define VAR_UNKNOWN -1
+
+/*
+ * Define our internal representation of an OBJECT IDENTIFIER
+ */
+struct objid {
+ int oid[128];
+};
+typedef struct objid Objid;
+
+/*
+ * Define a Veriable classso that we can handle multiple GET/SET's
+ * per PDU.
+ */
+typedef struct variable Variable;
+struct variable {
+ Objid oid;
+ int type;
+ union {
+ int ival; /* INTEGER/TIMESTAMP */
+ Objid oval; /* OBJID */
+ long aval; /* IPADDR */
+ char sval[STRLEN]; /* OCTET */
+ } var;
+ Variable *next;
+};
+
+/*
+ * Every SNMP PDU has the first four fields of this header. The only type
+ * which doesn't have the last three fields is the TRAP type.
+ */
+struct snmp_header {
+ int pdulen;
+ int version;
+ char community[64];
+ int pdutype;
+
+ /* GET/GETNEXT/GETRESP/SET */
+ int reqid;
+ int error;
+ int erridx;
+
+ /* TRAP */
+ Objid enterprise;
+ int ipaddr;
+ int generic_trap;
+ int specific_trap;
+
+ int varlen;
+ Variable *head,
+ *tail;
+};
+typedef struct snmp_header Snmp_Header;
+
+Snmp_Header *ColdStart_Header;
+Snmp_Header *PDU_Header;
+
+/*
+ * Define some OBJET IDENTIFIERS that we'll try to reply to:
+ *
+ * sysUpTime: number of time ticks since this deamon came up
+ * netpfx_oid: network prefix table
+ * unitype: is this a PRIVATE or PUBLIC network link
+ * univer: which version of UNI are we running
+ * devtype: is this a USER or NODE ATM device
+ * setprefix: used when the switch wants to tell us its NSAP prefix
+ * foresiggrp: FORE specific Objid we see alot of (being connected to FORE
+ * switches...)
+ */
+Objid Objids[] = {
+#define SYS_OBJID 0
+ {{ 8, 43, 6, 1, 2, 1, 1, 2, 0 }},
+#define UPTIME_OBJID 1
+ {{ 8, 43, 6, 1, 2, 1, 1, 3, 0 }},
+#define PORT_OBJID 2
+ {{ 12, 43, 6, 1, 4, 1, 353, 2, 1, 1, 1, 1, 0 }},
+#define IPNM_OBJID 3
+ {{ 10, 43, 6, 1, 4, 1, 353, 2, 1, 2, 0 }},
+#define LAYER_OBJID 4
+ {{ 12, 43, 6, 1, 4, 1, 353, 2, 2, 1, 1, 1, 0 }},
+#define MAXVCC_OBJID 5
+ {{ 12, 43, 6, 1, 4, 1, 353, 2, 2, 1, 1, 3, 0 }},
+#define UNITYPE_OBJID 6
+ {{ 12, 43, 6, 1, 4, 1, 353, 2, 2, 1, 1, 8, 0 }},
+#define UNIVER_OBJID 7
+ {{ 12, 43, 6, 1, 4, 1, 353, 2, 2, 1, 1, 9, 0 }},
+#define DEVTYPE_OBJID 8
+ {{ 12, 43, 6, 1, 4, 1, 353, 2, 2, 1, 1, 10, 0 }},
+#define ADDRESS_OBJID 9
+ {{ 8, 43, 6, 1, 4, 1, 353, 2, 6 }},
+#define NETPFX_OBJID 10
+ {{ 9, 43, 6, 1, 4, 1, 353, 2, 7, 1 }},
+#define MY_OBJID 11
+ {{ 7, 43, 6, 1, 4, 1, 9999, 1 }},
+#define SETPFX_OBJID 12 /* ATM Forum says 1=valid, 2=invalid, not 0! */
+ {{ 12, 43, 6, 1, 4, 1, 353, 2, 7, 1, 1, 3, 0 }},
+#define ENTERPRISE_OBJID 13
+ {{ 8, 43, 6, 1, 4, 1, 3, 1, 1 }},
+#define ATMF_PORTID 14
+ {{ 10, 43, 6, 1, 4, 1, 353, 2, 1, 4, 0 }},
+#define ATMF_SYSID 15
+ {{ 12, 43, 6, 1, 4, 1, 353, 2, 1, 1, 1, 8, 0 }},
+#define MADGE_OBJECT1 16 /* I don't have a clue ... -RH */
+ {{ 9, 43, 6, 1, 4, 1, 353, 2, 7, 99 }},
+};
+
+#define NUM_OIDS (sizeof(Objids)/sizeof(Objid))
+
+#define UNIVER_UNI20 1
+#define UNIVER_UNI30 2
+#define UNIVER_UNI31 3
+#define UNIVER_UNI40 4
+#define UNIVER_UNKNOWN 5
+
+#define UNITYPE_PUBLIC 1
+#define UNITYPE_PRIVATE 2
+
+#define DEVTYPE_USER 1
+#define DEVTYPE_NODE 2
+
+/* For print_pdu() */
+#define PDU_SEND 1
+#define PDU_RECV 2
+
+/*
+ * ILMI protocol states
+ */
+enum ilmi_states {
+ ILMI_UNKNOWN, /* Uninitialized */
+ ILMI_COLDSTART, /* We need to send a COLD_START trap */
+ ILMI_INIT, /* Ensure that switch has reset */
+ ILMI_REG, /* Looking for SET message */
+ ILMI_RUNNING /* Normal processing */
+};
+
+static const char *ILMI_State[] = {
+ "UNKNOWN",
+ "COLDSTART",
+ "INIT",
+ "REG",
+ "RUNNING"
+};
+
+/*
+ * Our (incrementing) Request ID
+ */
+int Req_ID;
+
+/*
+ * Temporary buffer for building response packets. Should help ensure
+ * that we aren't accidently overwriting some other memory.
+ */
+u_char Resp_Buf[1024];
+
+/*
+ * Copy the reponse into a buffer we can modify without
+ * changing the original...
+ */
+#define COPY_RESP(resp) \
+ bcopy ( (resp), Resp_Buf, (resp)[0] + 1 )
+
+int NUnits;
+
+/*
+ * fd for units which have seen a coldStart TRAP and are now exchaning SNMP requests
+ */
+int ilmi_fd[MAX_UNITS + 1];
+/*
+ * enum ilmi_states for this unit
+ */
+int ilmi_state[MAX_UNITS + 1];
+/*
+ * Local copy for HARP physical configuration information
+ */
+struct air_cfg_rsp Cfg[MAX_UNITS + 1];
+/*
+ * Local copy for HARP interface configuration information
+ */
+struct air_int_rsp Intf[MAX_UNITS + 1];
+
+/*
+ * addressEntry table
+ */
+Objid addressEntry[MAX_UNITS + 1];
+
+/*
+ * When this daemon started
+ */
+struct timeval starttime;
+
+int Debug_Level = 0;
+int foregnd = 0; /* run in the foreground? */
+
+char *progname;
+char hostname[80];
+
+ /* File to write debug messages to */
+#define LOG_FILE "/var/log/ilmid"
+FILE *Log; /* File descriptor for log messages */
+
+static const char *Months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+/*
+ * function declarations
+ */
+static void write_timestamp (void);
+static void hexdump (FILE *, u_int8_t *, int, char *);
+static int asn_get_pdu_len (u_char **, int *);
+static int asn_get_encoded (u_char **, int *);
+static int asn_get_int (u_char **, int *);
+static void asn_set_int (u_char **, int);
+static void print_objid (Objid *);
+static void asn_get_objid (u_char **, Objid *, int *);
+static int asn_put_objid (u_char **, Objid *);
+static void asn_get_octet (u_char **, char *, int *);
+static void print_header (Snmp_Header *);
+static void parse_oids (Snmp_Header *, u_char **);
+static int oid_cmp (Objid *, Objid *);
+static int oid_ncmp (Objid *, Objid *, int);
+static int find_var (Variable *);
+static int get_ticks (void);
+static void build_pdu (Snmp_Header *, int);
+static void free_pdu (Snmp_Header *);
+static void print_pdu (int, int, Snmp_Header *, int, u_char *);
+static void send_resp (int, Snmp_Header *, u_char *);
+static void init_ilmi (void);
+static void ilmi_open (void);
+static void get_local_ip (int, long *);
+static void set_prefix (Objid *, Snmp_Header *, int);
+static void set_address (Snmp_Header *, int);
+static void process_get (Snmp_Header *, int);
+static int lmi_object_find (Variable *);
+static int lmi_rcvcmd_getnext(Snmp_Header *, int);
+static int lmi_rcvcmd_trap (Snmp_Header *, int);
+static void ilmi_do_state (void);
+static void Increment_DL (int);
+static void Decrement_DL (int);
+
+static Snmp_Header * asn_get_header (u_char **);
+static Snmp_Header * build_cold_start (void);
+static Snmp_Header * build_generic_header (void);
+
+/*
+ * Write a syslog() style timestamp
+ *
+ * Write a syslog() style timestamp with month, day, time and hostname
+ * to the log file.
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+write_timestamp (void)
+{
+ time_t clk;
+ struct tm *tm;
+
+ clk = time ( (time_t)NULL );
+ tm = localtime ( &clk );
+
+ if ( Log && Debug_Level > 1 )
+ if ( Log != stderr )
+ fprintf ( Log, "%.3s %2d %.2d:%.2d:%.2d %s: ",
+ Months[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min,
+ tm->tm_sec, hostname );
+
+ return;
+
+}
+
+/*
+ * Utility to pretty print buffer as hex dumps
+ *
+ * Arguments:
+ * out - file handle
+ * ptr - buffer pointer
+ * len - length to pretty print
+ * desc - output header
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+hexdump (out, ptr, len, desc)
+ FILE * out;
+ u_int8_t * ptr;
+ int len;
+ char * desc;
+{
+ char line[17];
+ int i, j;
+
+ if (out == NULL)
+ out = stdout;
+
+ if (desc != NULL)
+ fprintf(out, "[ %s (%d bytes)]\n", desc, len);
+
+ bzero(line, sizeof(line));
+
+ for (i = 0, j = 0; i < len; i++) {
+
+ if (j == 0) fprintf(out, "%04x: ", i);
+ if (j == 8) fprintf(out, "| ");
+
+ fprintf(out, "%02x ", ptr[i]);
+ line[j] = isalnum(ptr[i]) ? ptr[i] : '.' ;
+ if (j == 15) {
+ fprintf(out, " |%16s|\n", line);
+ bzero(line, sizeof(line));
+ j = 0;
+ } else
+ j++;
+ }
+
+ if (j != 0) {
+ if (j <= 8) fprintf(out, " ");
+ for (; j < 16; j++) fprintf(out, " ");
+ fprintf(out, " |%-16s|\n", line);
+ }
+ fflush(out);
+
+ return;
+}
+
+/*
+ * Get lengths from PDU encodings
+ *
+ * Lengths are sometimes encoded as a single byte if the length
+ * is less the 127 but are more commonly encoded as one byte with
+ * the high bit set and the lower seven bits indicating the nuber
+ * of bytes which make up the length value. Trailing data is (to my
+ * knowledge) not 7-bit encoded.
+ *
+ * Arguments:
+ * bufp - pointer to buffer pointer
+ * plen - pointer to PDU length or NULL if not a concern
+ *
+ * Returns:
+ * bufp - updated buffer pointer
+ * plen - (possibly) adjusted pdu length
+ * <len> - decoded length
+ *
+ */
+static int
+asn_get_pdu_len (u_char **bufp, int *plen)
+{
+ u_char *bp = *bufp;
+ int len = 0;
+ int i, b;
+
+ b = *bp++;
+ if ( plen )
+ (*plen)--;
+ if ( b & 0x80 ) {
+ for ( i = 0; i < (b & ~0x80); i++ ) {
+ len = len * 256 + *bp++;
+ if ( plen )
+ (*plen)--;
+ }
+ } else
+ len = b;
+
+ *bufp = bp;
+ return ( len );
+}
+
+/*
+ * Get an 7-bit encoded value.
+ *
+ * Get a value which is represented using a 7-bit encoding. The last
+ * byte in the stream has the high-bit clear.
+ *
+ * Arguments:
+ * bufp - pointer to the buffer pointer
+ * len - pointer to the buffer length
+ *
+ * Returns:
+ * bufp - updated buffer pointer
+ * len - updated buffer length
+ * <val> - value encoding represented
+ *
+ */
+static int
+asn_get_encoded (u_char **bufp, int *len)
+{
+ u_char *bp = *bufp;
+ int val = 0;
+ int l = *len;
+
+ /*
+ * Keep going while high bit is set
+ */
+ do {
+ /*
+ * Each byte can represent 7 bits
+ */
+ val = ( val << 7 ) + ( *bp & ~0x80 );
+ l--;
+ } while ( *bp++ & 0x80 );
+
+ *bufp = bp; /* update buffer pointer */
+ *len = l; /* update buffer length */
+
+ return ( val );
+}
+
+/*
+ * Get a BER encoded integer
+ *
+ * Intergers are encoded as one byte length followed by <length> data bytes
+ *
+ * Arguments:
+ * bufp - pointer to the buffer pointer
+ * plen - pointer to PDU length or NULL if not a concern
+ *
+ * Returns:
+ * bufp - updated buffer pointer
+ * plen - (possibly) updated PDU length
+ * <val> - value of encoded integer
+ *
+ */
+static int
+asn_get_int (u_char **bufp, int *plen)
+{
+ int i;
+ int len;
+ int v = 0;
+ u_char *bp = *bufp;
+
+ len = *bp++;
+ if ( plen )
+ (*plen)--;
+ for ( i = 0; i < len; i++ ) {
+ v = (v * 256) + *bp++;
+ if ( plen )
+ (*plen)--;
+ }
+ *bufp = bp;
+ return ( v );
+}
+
+/*
+ * Set a BER encoded integer
+ *
+ * Arguments:
+ * bufp - pointer to buffer pointer where we are to set int in
+ * val - integer value to set
+ *
+ * Returns:
+ * none
+ * <bufp> - updated buffer pointer
+ *
+ */
+static void
+asn_set_int (u_char **bufp, int val)
+{
+ union {
+ int i;
+ u_char c[4];
+ } u;
+ int len = sizeof(int);
+ size_t i = 0;
+ u_char *bp = *bufp;
+
+ /* Check for special case where val == 0 */
+ if ( val == 0 ) {
+ *bp++ = 1;
+ *bp++ = 0;
+ *bufp = bp;
+ return;
+ }
+
+ u.i = htonl ( val );
+
+ while ( u.c[i] == 0 && i++ < sizeof(int) )
+ len--;
+
+ if ( u.c[i] > 0x7f ) {
+ i--;
+ len++;
+ }
+
+ *bp++ = len;
+ bcopy ( (caddr_t)&u.c[sizeof(int)-len], bp, len );
+ bp += len;
+ *bufp = bp;
+
+ return;
+}
+
+/*
+ * Utility to print an object identifier.
+ *
+ * Arguments:
+ * objid - pointer to objid representation
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_objid (Objid *objid)
+{
+ int i;
+
+ /*
+ * First oid coded as 40 * X + Y
+ */
+ if ( Log ) {
+ write_timestamp();
+ fprintf ( Log, ".%d.%d", objid->oid[1] / 40,
+ objid->oid[1] % 40 );
+ }
+ for ( i = 2; i <= objid->oid[0]; i++ )
+ if ( Log )
+ fprintf ( Log, ".%d", objid->oid[i] );
+ if ( Log )
+ fprintf ( Log, "\n" );
+
+ return;
+}
+
+/*
+ * Get Object Identifier
+ *
+ * Arguments:
+ * bufp - pointer to buffer pointer
+ * objid - pointer to objid buffer
+ * plen - pointer to PDU length or NULL of not a concern
+ *
+ * Returns:
+ * bufp - updated buffer pointer
+ * objid - internal representation of encoded objid
+ * plen - (possibly) adjusted PDU length
+ *
+ */
+static void
+asn_get_objid (u_char **bufp, Objid *objid, int *plen)
+{
+ int len;
+ u_char *bp = *bufp;
+ int *ip = (int *)objid + 1; /* First byte will contain length */
+ int oidlen = 0;
+
+ len = *bp++;
+ if ( plen )
+ (*plen)--;
+ while ( len ) {
+ *ip++ = asn_get_encoded ( &bp, &len );
+ if ( plen )
+ (*plen)--;
+ oidlen++;
+ }
+ objid->oid[0] = oidlen;
+ *bufp = bp;
+
+ return;
+}
+
+/*
+ * Put OBJID - assumes elements <= 16383 for two byte coding
+ *
+ */
+static int
+asn_put_objid (u_char **bufp, Objid *objid)
+{
+ int len = 0;
+ u_char *bp = *bufp;
+ u_char *cpp;
+ int i;
+
+ cpp = bp;
+ *bp++ = objid->oid[0];
+ len++;
+ for ( i = 1; i <= objid->oid[0]; i++ ) {
+ u_int c = objid->oid[i];
+
+ while ( c > 127 ) {
+ *bp++ = ( ( c >> 7 ) & 0x7f ) | 0x80;
+ len++;
+ c &= 0x7f; /* XXX - assumption of two bytes */
+ (*cpp)++;
+ }
+ *bp++ = c;
+ len++;
+ }
+
+ *bufp = bp;
+ return ( len );
+
+}
+
+/*
+ * Get OCTET STRING
+ *
+ * Octet strings are encoded as a 7-bit encoded length followed by <len>
+ * data bytes;
+ *
+ * Arguments:
+ * bufp - pointer to buffer pointer
+ * octet - pointer to octet buffer
+ * plen - pointer to PDU length
+ *
+ * Returns:
+ * bufp - updated buffer pointer
+ * octet - encoded Octet String
+ * plen - (possibly) adjusted PDU length
+ *
+ */
+static void
+asn_get_octet (u_char **bufp, char *octet, int *plen)
+{
+ u_char *bp = *bufp;
+ int i = 0;
+ int len = 0;
+
+ /*
+ * &i is really a dummy value here as we don't keep track
+ * of the ongoing buffer length
+ */
+ len = asn_get_encoded ( &bp, &i );
+
+ for ( i = 0; i < len; i++ ) {
+ *octet++ = *bp++;
+ if ( plen )
+ (*plen)--;
+ }
+
+ *bufp = bp;
+
+ return;
+
+}
+
+/*
+ * Utility to print SNMP PDU header information
+ *
+ * Arguments:
+ * Hdr - pointer to internal SNMP header structure
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_header (Snmp_Header *Hdr)
+{
+ Variable *var;
+
+ if ( Log ) {
+ write_timestamp();
+ fprintf(Log,
+ " PDU Type: 0x%x (%s)\n"
+ " PDU len: %d\n"
+ " Version: %d\n"
+ " Community: \"%s\"\n",
+ Hdr->pdutype, PDU_Types[Hdr->pdutype & 7],
+ Hdr->pdulen,
+ Hdr->version + 1,
+ Hdr->community);
+
+ if (Hdr->pdutype != PDU_TYPE_TRAP) {
+ write_timestamp();
+ fprintf(Log,
+ " Req Id: 0x%x\n"
+ " Error: %d\n"
+ " Error Index: %d\n",
+ Hdr->reqid,
+ Hdr->error,
+ Hdr->erridx);
+ }
+ }
+
+ var = Hdr->head;
+ while ( var ) {
+ if ( Log ) {
+ write_timestamp();
+ fprintf ( Log, "Variable Type: %d", var->type );
+ if ( Var_Types[var->type] )
+ fprintf ( Log, " (%s)", Var_Types[var->type] );
+ fprintf ( Log, "\n Object: ");
+ print_objid ( &var->oid );
+ fprintf ( Log, " Value: ");
+ switch ( var->type ) {
+ case ASN_INTEGER:
+ fprintf ( Log, "%d (0x%x)\n", var->var.ival, var->var.ival );
+ break;
+ case ASN_NULL:
+ fprintf ( Log, "NULL" );
+ break;
+ default:
+ fprintf ( Log, "[0x%x]", var->type );
+ break;
+ }
+ fprintf ( Log, "\n" );
+ }
+ var = var->next;
+ }
+
+ return;
+
+}
+
+/*
+ * Pull OID's from GET/SET message
+ *
+ * Arguments:
+ * h - pointer to Snmp_Header
+ * bp - pointer to input PDU
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+parse_oids (Snmp_Header *h, u_char **bp)
+{
+ int len = h->varlen;
+ int sublen;
+ Variable *var;
+ u_char *bufp = *bp;
+
+ while ( len > 0 ) {
+ if ( *bufp++ == ASN_SEQUENCE ) {
+ len--;
+
+ /* Create new Variable instance */
+ if ( ( var = (Variable *)malloc(sizeof(Variable)) ) == NULL )
+ {
+ *bp = bufp;
+ return;
+ }
+ bzero(var, sizeof(Variable));
+ /* Link to tail */
+ if ( h->tail )
+ h->tail->next = var;
+ /* Set head iff NULL */
+ if ( h->head == NULL ) {
+ h->head = var;
+ }
+ /* Adjust tail */
+ h->tail = var;
+
+ /* Get length of variable sequence */
+ sublen = asn_get_pdu_len ( &bufp, &len );
+ /* Should be OBJID type */
+ if ( *bufp++ != ASN_OBJID ) {
+ *bp = bufp;
+ return;
+ }
+ asn_get_objid ( (u_char **)&bufp, &var->oid, &len );
+ var->type = *bufp++;
+ len--;
+ switch ( var->type ) {
+ case ASN_INTEGER:
+ var->var.ival = asn_get_int ( &bufp, &len );
+ break;
+ case ASN_NULL:
+ bufp++;
+ len--;
+ break;
+ case ASN_OBJID:
+ asn_get_objid ( &bufp, &var->var.oval, &len );
+ break;
+ case ASN_OCTET:
+ asn_get_octet ( &bufp, var->var.sval, &len );
+ break;
+ default:
+ if ( Log ) {
+ write_timestamp();
+ fprintf ( Log, "Unknown variable type: %d\n",
+ var->type );
+ }
+ break;
+ }
+ var->next = NULL;
+ } else
+ break;
+ }
+
+ *bp = bufp;
+ return;
+}
+
+/*
+ * Crack the SNMP header
+ *
+ * Pull the PDU length, SNMP version, SNMP community and PDU type.
+ * If present, also pull out the Request ID, Error status, and Error
+ * index values.
+ *
+ * Arguments:
+ * bufp - pointer to buffer pointer
+ *
+ * Returns:
+ * bufp - updated buffer pointer
+ * - generated SNMP header
+ *
+ */
+static Snmp_Header *
+asn_get_header (u_char **bufp)
+{
+ Snmp_Header *h;
+ u_char *bp = *bufp;
+ int len = 0;
+ int dummy = 0;
+
+ /*
+ * Allocate memory to hold the SNMP header
+ */
+ if ( ( h = (Snmp_Header *)malloc(sizeof(Snmp_Header)) ) == NULL )
+ return ( (Snmp_Header *)NULL );
+
+ /*
+ * Ensure that we wipe the slate clean
+ */
+ bzero(h, sizeof(Snmp_Header));
+
+ /*
+ * PDU has to start as SEQUENCE OF
+ */
+ if ( *bp++ != ASN_SEQUENCE ) /* Class == Universial, f == 1, tag == SEQUENCE */
+ return ( (Snmp_Header *)NULL );
+
+ /*
+ * Get the length of remaining PDU data
+ */
+ h->pdulen = asn_get_pdu_len ( &bp, NULL );
+
+ /*
+ * We expect to find an integer encoding Version-1
+ */
+ if ( *bp++ != ASN_INTEGER ) {
+ return ( (Snmp_Header *)NULL );
+ }
+ h->version = asn_get_int ( &bp, NULL );
+
+ /*
+ * After the version, we need the community name
+ */
+ if ( *bp++ != ASN_OCTET ) {
+ return ( (Snmp_Header *)NULL );
+ }
+ asn_get_octet ( &bp, h->community, NULL );
+
+ /*
+ * Single byte PDU type
+ */
+ h->pdutype = *bp++;
+
+ /*
+ * If this isn't a TRAP PDU, then look for the rest of the header
+ */
+ if ( h->pdutype != PDU_TYPE_TRAP ) { /* TRAP uses different format */
+
+ (void) asn_get_pdu_len ( &bp, &dummy );
+
+ /* Request ID */
+ if ( *bp++ != ASN_INTEGER ) {
+ free( h );
+ return ( (Snmp_Header *)NULL );
+ }
+ h->reqid = asn_get_int ( &bp, NULL );
+
+ /* Error Status */
+ if ( *bp++ != ASN_INTEGER ) {
+ free ( h );
+ return ( (Snmp_Header *)NULL );
+ }
+ h->error = asn_get_int ( &bp, NULL );
+
+ /* Error Index */
+ if ( *bp++ != ASN_INTEGER ) {
+ free ( h );
+ return ( (Snmp_Header *)NULL );
+ }
+ h->erridx = asn_get_int ( &bp, NULL );
+
+ /* Sequence of... */
+ if ( *bp++ != ASN_SEQUENCE ) {
+ free ( h );
+ return ( (Snmp_Header *)NULL );
+ }
+ h->varlen = ( asn_get_pdu_len ( &bp, &len ) - 1 );
+ h->varlen += ( len - 1 );
+
+ parse_oids ( h, &bp );
+ }
+
+ *bufp = bp;
+
+ return ( h );
+
+}
+
+/*
+ * Compare two internal OID representations
+ *
+ * Arguments:
+ * oid1 - Internal Object Identifier
+ * oid2 - Internal Object Identifier
+ *
+ * Returns:
+ * 0 - Objid's match
+ * 1 - Objid's don't match
+ *
+ */
+static int
+oid_cmp (Objid *oid1, Objid *oid2)
+{
+ int i;
+ int len;
+
+ /*
+ * Compare lengths
+ */
+ if ( !(oid1->oid[0] == oid2->oid[0] ) )
+ /* Different lengths */
+ return ( 1 );
+
+ len = oid1->oid[0];
+
+ /*
+ * value by value compare
+ */
+ for ( i = 1; i <= len; i++ ) {
+ if ( !(oid1->oid[i] == oid2->oid[i]) )
+ /* values don't match */
+ return ( 1 );
+ }
+
+ /* Objid's are identical */
+ return ( 0 );
+}
+
+/*
+ * Compare two internal OID representations
+ *
+ * Arguments:
+ * oid1 - Internal Object Identifier
+ * oid2 - Internal Object Identifier
+ * len - Length of OID to compare
+ *
+ * Returns:
+ * 0 - Objid's match
+ * 1 - Objid's don't match
+ *
+ */
+static int
+oid_ncmp (Objid *oid1, Objid *oid2, int len)
+{
+ int i;
+
+ /*
+ * value by value compare
+ */
+ for ( i = 1; i <= len; i++ ) {
+ if ( !(oid1->oid[i] == oid2->oid[i]) )
+ /* values don't match */
+ return ( 1 );
+ }
+
+ /* Objid's are identical */
+ return ( 0 );
+}
+
+/*
+ * Find the index of an OBJID which matches this Variable instance.
+ *
+ * Arguments:
+ * var - pointer to Variable instance
+ *
+ * Returns:
+ * idx - index of matched Variable instance
+ * -1 - no matching Variable found
+ *
+ */
+static int
+find_var (Variable *var)
+{
+ size_t i;
+
+ for ( i = 0; i < NUM_OIDS; i++ )
+ if ( oid_cmp ( &var->oid, &Objids[i] ) == 0 ) {
+ return ( i );
+ }
+
+ return ( -1 );
+
+}
+
+/*
+ * Return the time process has been running as a number of ticks
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * number of ticks
+ *
+ */
+static int
+get_ticks (void)
+{
+ struct timeval timenow;
+ struct timeval timediff;
+
+ (void) gettimeofday ( &timenow, NULL );
+ /*
+ * Adjust for subtraction
+ */
+ timenow.tv_sec--;
+ timenow.tv_usec += 1000000;
+
+ /*
+ * Compute time since 'starttime'
+ */
+ timediff.tv_sec = timenow.tv_sec - starttime.tv_sec;
+ timediff.tv_usec = timenow.tv_usec - starttime.tv_usec;
+
+ /*
+ * Adjust difference timeval
+ */
+ if ( timediff.tv_usec >= 1000000 ) {
+ timediff.tv_usec -= 1000000;
+ timediff.tv_sec++;
+ }
+
+ /*
+ * Compute number of ticks
+ */
+ return ( ( timediff.tv_sec * 100 ) + ( timediff.tv_usec / 10000 ) );
+
+}
+
+/*
+ * Build a response PDU
+ *
+ * Arguments:
+ * hdr - pointer to PDU Header with completed Variable list
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+build_pdu (Snmp_Header *hdr, int type)
+{
+ u_char *bp = Resp_Buf;
+ u_char *vpp;
+ u_char *ppp;
+ int erridx = 0;
+ int varidx = 1;
+ int varlen = 0;
+ int pdulen = 0;
+ int traplen = 0;
+ Variable *var;
+
+ /*
+ * Clear out the reply
+ */
+ bzero ( Resp_Buf, sizeof(Resp_Buf) );
+
+ /* [0] is reserved for overall length */
+ bp++;
+
+ /* Start with SEQUENCE OF */
+ *bp++ = ASN_SEQUENCE;
+ /* - assume we can code length in two octets */
+ *bp++ = 0x82;
+ bp++;
+ bp++;
+ /* Version */
+ *bp++ = ASN_INTEGER;
+ asn_set_int ( &bp, hdr->version );
+ /* Community name */
+ *bp++ = ASN_OCTET;
+ *bp++ = strlen ( hdr->community );
+ bcopy ( hdr->community, bp, strlen ( hdr->community ) );
+ bp += strlen ( hdr->community );
+ /* PDU Type */
+ *bp++ = type;
+ ppp = bp;
+ /* Length of OID data - assume it'll fit in one octet */
+ bp++;
+
+ if ( type != PDU_TYPE_TRAP ) {
+ /* Sequence ID */
+ *bp++ = ASN_INTEGER;
+ asn_set_int ( &bp, hdr->reqid );
+ /*
+ * Check to see if all the vaiables were resolved - we do this
+ * by looking for something which still has an ASN_NULL value.
+ */
+ var = hdr->head;
+ if ( type == PDU_TYPE_GETRESP ) {
+ while ( var && erridx == 0 ) {
+ if ( var->type != ASN_NULL ) {
+ varidx++;
+ var = var->next;
+ } else
+ erridx = varidx;
+ }
+ }
+
+ /* Error status */
+ *bp++ = ASN_INTEGER;
+ *bp++ = 0x01; /* length = 1 */
+ if ( erridx )
+ *bp++ = SNMP_ERR_NOSUCHNAME;
+ else
+ *bp++ = SNMP_ERR_NOERROR;
+ /* Error Index */
+ *bp++ = ASN_INTEGER;
+ *bp++ = 0x01; /* length = 1 */
+ *bp++ = erridx; /* index == 0 if no error */
+ } else {
+ /* type == PDU_TYPE_TRAP */
+
+ /* Fill in ENTERPRISE OBJID */
+ *bp++ = ASN_OBJID;
+ (void) asn_put_objid ( &bp, &hdr->enterprise );
+
+ /* Fill in IP address */
+ *bp++ = ASN_IPADDR;
+ *bp++ = sizeof ( hdr->ipaddr );
+ bcopy ( (caddr_t)&hdr->ipaddr, bp, sizeof(hdr->ipaddr) );
+ bp += sizeof(hdr->ipaddr);
+
+ /* Fill in generic and specific trap types */
+ *bp++ = ASN_INTEGER;
+ asn_set_int ( &bp, hdr->generic_trap );
+ *bp++ = ASN_INTEGER;
+ asn_set_int ( &bp, hdr->specific_trap );
+
+ /* Fill in time-stamp - assume 0 for now */
+ *bp++ = ASN_TIMESTAMP;
+ asn_set_int ( &bp, 0 );
+
+ /* encoded length */
+ traplen = ( bp - ppp - 1 );
+
+ /* Continue with variable processing */
+ }
+
+ /* SEQUENCE OF */
+ *bp++ = ASN_SEQUENCE;
+ *bp++ = 0x82;
+ /* - assume we can code length in two octets */
+ vpp = bp;
+ varlen = 0;
+ bp++;
+ bp++;
+
+ /* Install Variables */
+ var = hdr->head;
+ varidx = 1;
+ while ( var ) {
+ u_char *bpp;
+ int len = 0;
+
+ /* SEQUENCE OF */
+ *bp++ = ASN_SEQUENCE;
+ *bp++ = 0x82;
+ /* - assume we can code length in two octets */
+ bpp = bp;
+ bp++;
+ bp++;
+ /* OBJID */
+ *bp++ = ASN_OBJID;
+ len++;
+
+ len += asn_put_objid ( &bp, &var->oid );
+
+ if ( erridx && varidx >= erridx ) {
+ /* Code this variable as NULL */
+ *bp++ = ASN_NULL;
+ len++;
+ bp++;
+ len++;
+ } else {
+ u_char *lpp;
+ /* Variable type */
+ *bp++ = var->type;
+ len++;
+ lpp = bp;
+ switch ( var->type ) {
+ case ASN_INTEGER:
+ asn_set_int ( &bp, var->var.ival );
+ len += ( *lpp + 1 );
+ break;
+ case ASN_OCTET:
+ *bp++ = var->var.sval[0];
+ len++;
+ bcopy ( (caddr_t)&var->var.sval[1],
+ bp, var->var.sval[0] );
+ len += var->var.sval[0];
+ bp += var->var.sval[0];
+ break;
+ case ASN_NULL:
+ *bp++ = 0x00;
+ len++;
+ break;
+ case ASN_OBJID:
+ len += asn_put_objid ( &bp, &var->var.oval );
+ break;
+ case ASN_SEQUENCE:
+ break;
+ case ASN_IPADDR:
+ *bp++ = 4;
+ len++;
+ bcopy ( (caddr_t)&var->var.aval, bp, 4 );
+ len += 4;
+ bp += 4;
+ break;
+ case ASN_TIMESTAMP:
+ asn_set_int ( &bp, var->var.ival );
+ len += ( *lpp + 1 );
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Accumulate total Variable sequence length */
+ varlen += (len + 4);
+
+ /* Fill in length of this sequence */
+ bpp[1] = len & 0xff;
+ bpp[0] = len >> 8;
+
+ var = var->next;
+ }
+
+
+ /* Fill in length of Variable sequence */
+ vpp[1] = varlen & 0xff;
+ vpp[0] = varlen >> 8;
+
+ if ( type != PDU_TYPE_TRAP ) {
+ /* Fill in length of data AFTER PDU type */
+ *ppp = varlen + 12 + ppp[2]; /* + length of reqid */
+ } else {
+ /* Fill in length of data AFTER PDU type */
+ *ppp = varlen + traplen + 4; /* + length of initial sequence of */
+ }
+
+ /* Fill in overall sequence length */
+ pdulen = *ppp + 7 + strlen ( hdr->community );
+ Resp_Buf[4] = pdulen & 0x7f;
+ Resp_Buf[3] = pdulen >> 8;
+
+ pdulen = bp - Resp_Buf - 1;
+
+ Resp_Buf[0] = pdulen;
+
+ hdr->pdutype = type;
+ hdr->pdulen = pdulen;
+
+ return;
+}
+
+static void
+free_pdu (Snmp_Header *hdr)
+{
+ Variable *var;
+
+ while ( hdr->head ) {
+ var = hdr->head->next; /* Save next link */
+ free ( hdr->head ); /* Free current var */
+ hdr->head = var; /* Set head to next link */
+ }
+
+ free ( hdr ); /* Free fixed portion */
+}
+
+static void
+print_pdu (int dir, int intf, Snmp_Header *Hdr, int len, u_char *buf)
+{
+ const char * pdu_dir;
+ const char * pdu_type;
+ int pdu_num;
+
+ write_timestamp();
+
+ switch (dir) {
+ case PDU_SEND:
+ pdu_dir = "SEND";
+ break;
+ case PDU_RECV:
+ pdu_dir = "RECV";
+ break;
+ default:
+ pdu_dir = "undefined";
+ break;
+ }
+
+ if (Hdr == NULL) {
+ pdu_type = "unknown";
+ pdu_num = 0;
+ } else {
+ pdu_type = PDU_Types[Hdr->pdutype & 7];
+ pdu_num = Hdr->pdutype;
+ }
+
+ fprintf(Log,
+ "%s: %s(%d), ILMI %s(%d), PDU Type %s(0x%x) %d/%d bytes.\n",
+ pdu_dir,
+ Intf[intf].anp_intf, ilmi_fd[intf],
+ ILMI_State[intf], ilmi_state[intf],
+ pdu_type, pdu_num,
+ len, buf[0]);
+
+ if (Hdr == NULL)
+ fprintf(Log, "Header seems to be invalid.\n");
+ else
+ print_header(Hdr);
+
+ hexdump(Log, (u_char *)&buf[1], len, NULL);
+
+ return;
+}
+
+/*
+ * Send a generic response packet
+ *
+ * Arguments:
+ * sd - socket to send the reply on
+ * reqid - original request ID from GET PDU
+ * resp - pointer to the response to send
+ *
+ * Returns:
+ * none - response sent
+ *
+ */
+static void
+send_resp (int intf, Snmp_Header *Hdr, u_char *resp)
+{
+ int n;
+
+ if ( ilmi_fd[intf] > 0 ) {
+ n = write ( ilmi_fd[intf], (caddr_t)&resp[1], resp[0] );
+ if ( Log && Debug_Level > 1 ) {
+ print_pdu(PDU_SEND, intf, Hdr, n, resp);
+ }
+ }
+
+ free_pdu ( Hdr );
+ return;
+}
+
+/*
+ * Build a COLD_START TRAP PDU
+ *
+ */
+static Snmp_Header *
+build_cold_start (void)
+{
+ Snmp_Header *hdr;
+ Variable *var;
+
+ hdr = (Snmp_Header *)malloc(sizeof(Snmp_Header));
+ if (hdr == NULL) {
+ fprintf(stderr, "malloc() failed in %s()\n", __func__);
+ exit(1);
+ }
+ bzero(hdr, sizeof(Snmp_Header));
+
+ hdr->pdulen = 0;
+ hdr->version = SNMP_VERSION_1 - 1;
+ snprintf ( hdr->community, sizeof(hdr->community), "ILMI" );
+
+ hdr->ipaddr = 0x0; /* 0.0.0.0 */
+ hdr->generic_trap = TRAP_COLDSTART;
+ hdr->specific_trap = 0;
+ bcopy ( (caddr_t)&Objids[ENTERPRISE_OBJID], (caddr_t)&hdr->enterprise,
+ sizeof(Objid) );
+
+ hdr->head = (Variable *)malloc(sizeof(Variable));
+ if (hdr == NULL) {
+ fprintf(stderr, "malloc() failed in %s()\n", __func__);
+ exit(1);
+ }
+ bzero(hdr->head, sizeof(Variable));
+
+ var = hdr->head;
+ bcopy ( (caddr_t)&Objids[UPTIME_OBJID], (caddr_t)&var->oid,
+ sizeof(Objid) );
+ var->type = ASN_NULL;
+
+ return ( hdr );
+}
+
+/*
+ * Build a Generic PDU Header
+ *
+ */
+static Snmp_Header *
+build_generic_header (void)
+{
+ Snmp_Header *hdr;
+
+ hdr = (Snmp_Header *)malloc(sizeof(Snmp_Header));
+ if (hdr == NULL) {
+ fprintf(stderr, "malloc() failed in %s()\n", __func__);
+ exit(1);
+ }
+ bzero(hdr, sizeof(Snmp_Header));
+
+ hdr->pdulen = 0;
+ hdr->version = SNMP_VERSION_1 - 1;
+ snprintf ( hdr->community, sizeof(hdr->community), "ILMI" );
+
+ return ( hdr );
+}
+
+/*
+ * Initialize information on what physical adapters HARP knows about
+ *
+ * Query the HARP subsystem about configuration and physical interface
+ * information for any currently registered ATM adapters. Store the information
+ * as arrays for easier indexing by SNMP port/index numbers.
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none Information from HARP available
+ *
+ */
+static void
+init_ilmi (void)
+{
+ struct air_cfg_rsp *cfg_info = NULL;
+ struct air_int_rsp *intf_info = NULL;
+ int buf_len;
+
+ /*
+ * Get configuration info - what's available with 'atm sh config'
+ */
+ buf_len = get_cfg_info ( NULL, &cfg_info );
+ /*
+ * If error occurred, clear out everything
+ */
+ if ( buf_len <= 0 ) {
+ bzero ( Cfg, sizeof(Cfg) );
+ bzero( Intf, sizeof(Intf) );
+ NUnits = 0;
+ return;
+ }
+
+ /*
+ * Move to local storage
+ */
+ bcopy ( cfg_info, (caddr_t)Cfg, buf_len );
+ /*
+ * Compute how many units information was returned for
+ */
+ NUnits = buf_len / sizeof(struct air_cfg_rsp);
+ /* Housecleaning */
+ free ( cfg_info );
+ cfg_info = NULL;
+ /*
+ * Get the per interface information
+ */
+ buf_len = get_intf_info ( NULL, &intf_info );
+ /*
+ * If error occurred, clear out Intf info
+ */
+ if ( buf_len <= 0 ) {
+ bzero ( Intf, sizeof(Intf) );
+ return;
+ }
+
+ /*
+ * Move to local storage
+ */
+ bcopy ( intf_info, (caddr_t)Intf, buf_len );
+ /* Housecleaning */
+ free ( intf_info );
+ intf_info = NULL;
+
+ return;
+
+}
+
+/*
+ * Open a new SNMP session for ILMI
+ *
+ * Start by updating interface information, in particular, how many
+ * interfaces are in the system. While we'll try to open sessons on
+ * all interfaces, this deamon currently can only handle the first
+ * interface.
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+ilmi_open (void)
+{
+ struct sockaddr_atm satm;
+ struct t_atm_aal5 aal5;
+ struct t_atm_traffic traffic;
+ struct t_atm_bearer bearer;
+ struct t_atm_qos qos;
+ struct t_atm_app_name appname;
+ Atm_addr subaddr;
+ char nifname[IFNAMSIZ];
+ int optlen;
+ int unit = 0;
+ u_char sig_proto;
+
+ init_ilmi();
+
+ for ( unit = 0; unit < NUnits; unit++ ) {
+
+ /*
+ * ILMI only makes sense for UNI signalling protocols
+ */
+ sig_proto = Intf[unit].anp_sig_proto;
+ if ( sig_proto != ATM_SIG_UNI30 && sig_proto != ATM_SIG_UNI31 &&
+ sig_proto != ATM_SIG_UNI40 )
+ continue;
+
+ if ( ilmi_fd[unit] == -1 ) {
+
+ ilmi_fd[unit] = socket ( AF_ATM, SOCK_SEQPACKET, ATM_PROTO_AAL5 );
+
+ if ( ilmi_fd[unit] < 0 ) {
+ perror ( "open" );
+ continue;
+ }
+
+ /*
+ * Set interface name. For now, we must have a netif to go on...
+ */
+ if ( Intf[unit].anp_nif_cnt == 0 ) {
+ if ( Debug_Level > 1 && Log ) {
+ write_timestamp();
+ fprintf ( Log, "No nif on unit %d\n", unit );
+ }
+ close ( ilmi_fd[unit] );
+ ilmi_fd[unit] = -1;
+ continue;
+ }
+ sprintf ( nifname, "%s0", Intf[unit].anp_nif_pref );
+ optlen = sizeof ( nifname );
+ if ( setsockopt ( ilmi_fd[unit], T_ATM_SIGNALING,
+ T_ATM_NET_INTF, (caddr_t)nifname, optlen ) < 0 ) {
+ perror ( "setsockopt" );
+ if ( Log ) {
+ write_timestamp();
+ fprintf ( Log,
+ "Couldn't set interface name \"%s\"\n",
+ nifname );
+ }
+ if ( Debug_Level > 1 && Log ) {
+ write_timestamp();
+ fprintf ( Log, "nifname: closing unit %d\n", unit );
+ }
+ close ( ilmi_fd[unit] );
+ ilmi_fd[unit] = -1;
+ continue;
+ }
+
+ /*
+ * Set up destination SAP
+ */
+ bzero ( (caddr_t) &satm, sizeof(satm) );
+ satm.satm_family = AF_ATM;
+#if (defined(BSD) && (BSD >= 199103))
+ satm.satm_len = sizeof(satm);
+#endif
+
+ satm.satm_addr.t_atm_sap_addr.SVE_tag_addr = T_ATM_PRESENT;
+ satm.satm_addr.t_atm_sap_addr.SVE_tag_selector = T_ATM_ABSENT;
+ satm.satm_addr.t_atm_sap_addr.address_format = T_ATM_PVC_ADDR;
+ satm.satm_addr.t_atm_sap_addr.address_length = sizeof(Atm_addr_pvc);
+ ATM_PVC_SET_VPI((Atm_addr_pvc *)satm.satm_addr.t_atm_sap_addr.address,
+ 0 );
+ ATM_PVC_SET_VCI((Atm_addr_pvc *)satm.satm_addr.t_atm_sap_addr.address,
+ 16 );
+
+ satm.satm_addr.t_atm_sap_layer2.SVE_tag = T_ATM_PRESENT;
+ satm.satm_addr.t_atm_sap_layer2.ID_type = T_ATM_SIMPLE_ID;
+ satm.satm_addr.t_atm_sap_layer2.ID.simple_ID = T_ATM_BLLI2_I8802;
+
+ satm.satm_addr.t_atm_sap_layer3.SVE_tag = T_ATM_ABSENT;
+
+ satm.satm_addr.t_atm_sap_appl.SVE_tag = T_ATM_ABSENT;
+
+ /*
+ * Set up connection parameters
+ */
+ aal5.forward_max_SDU_size = MAX_LEN;
+ aal5.backward_max_SDU_size = MAX_LEN;
+ aal5.SSCS_type = T_ATM_NULL;
+ optlen = sizeof(aal5);
+ if ( setsockopt ( ilmi_fd[unit], T_ATM_SIGNALING, T_ATM_AAL5,
+ (caddr_t) &aal5, optlen ) < 0 ) {
+ perror ( "setsockopt(aal5)" );
+ if ( Debug_Level > 1 && Log ) {
+ write_timestamp();
+ fprintf ( Log, "aal5: closing unit %d\n", unit );
+ }
+ close ( ilmi_fd[unit] );
+ ilmi_fd[unit] = -1;
+ continue;
+ }
+
+ traffic.forward.PCR_high_priority = T_ATM_ABSENT;
+ traffic.forward.PCR_all_traffic = 100000;
+ traffic.forward.SCR_high_priority = T_ATM_ABSENT;
+ traffic.forward.SCR_all_traffic = T_ATM_ABSENT;
+ traffic.forward.MBS_high_priority = T_ATM_ABSENT;
+ traffic.forward.MBS_all_traffic = T_ATM_ABSENT;
+ traffic.forward.tagging = T_NO;
+ traffic.backward.PCR_high_priority = T_ATM_ABSENT;
+ traffic.backward.PCR_all_traffic = 100000;
+ traffic.backward.SCR_high_priority = T_ATM_ABSENT;
+ traffic.backward.SCR_all_traffic = T_ATM_ABSENT;
+ traffic.backward.MBS_high_priority = T_ATM_ABSENT;
+ traffic.backward.MBS_all_traffic = T_ATM_ABSENT;
+ traffic.backward.tagging = T_NO;
+ traffic.best_effort = T_YES;
+ optlen = sizeof(traffic);
+ if (setsockopt(ilmi_fd[unit], T_ATM_SIGNALING, T_ATM_TRAFFIC,
+ (caddr_t)&traffic, optlen) < 0) {
+ perror("setsockopt(traffic)");
+ }
+ bearer.bearer_class = T_ATM_CLASS_X;
+ bearer.traffic_type = T_ATM_NULL;
+ bearer.timing_requirements = T_ATM_NULL;
+ bearer.clipping_susceptibility = T_NO;
+ bearer.connection_configuration = T_ATM_1_TO_1;
+ optlen = sizeof(bearer);
+ if (setsockopt(ilmi_fd[unit], T_ATM_SIGNALING, T_ATM_BEARER_CAP,
+ (caddr_t)&bearer, optlen) < 0) {
+ perror("setsockopt(bearer)");
+ }
+
+ qos.coding_standard = T_ATM_NETWORK_CODING;
+ qos.forward.qos_class = T_ATM_QOS_CLASS_0;
+ qos.backward.qos_class = T_ATM_QOS_CLASS_0;
+ optlen = sizeof(qos);
+ if (setsockopt(ilmi_fd[unit], T_ATM_SIGNALING, T_ATM_QOS, (caddr_t)&qos,
+ optlen) < 0) {
+ perror("setsockopt(qos)");
+ }
+
+ subaddr.address_format = T_ATM_ABSENT;
+ subaddr.address_length = 0;
+ optlen = sizeof(subaddr);
+ if (setsockopt(ilmi_fd[unit], T_ATM_SIGNALING, T_ATM_DEST_SUB,
+ (caddr_t)&subaddr, optlen) < 0) {
+ perror("setsockopt(dest_sub)");
+ }
+
+ strncpy(appname.app_name, "ILMI", T_ATM_APP_NAME_LEN);
+ optlen = sizeof(appname);
+ if (setsockopt(ilmi_fd[unit], T_ATM_SIGNALING, T_ATM_APP_NAME,
+ (caddr_t)&appname, optlen) < 0) {
+ perror("setsockopt(appname)");
+ }
+
+ /*
+ * Now try to connect to destination
+ */
+ if ( connect ( ilmi_fd[unit], (struct sockaddr *) &satm,
+ sizeof(satm)) < 0 ) {
+ perror ( "connect" );
+ if ( Debug_Level > 1 && Log ) {
+ write_timestamp();
+ fprintf ( Log, "connect: closing unit %d\n", unit );
+ }
+ close ( ilmi_fd[unit] );
+ ilmi_fd[unit] = -1;
+ continue;
+ }
+
+ if ( Debug_Level && Log ) {
+ write_timestamp();
+ fprintf ( Log, "***** opened unit %d\n", unit );
+ }
+
+ ilmi_state[unit] = ILMI_COLDSTART;
+
+ }
+
+ }
+
+ return;
+
+}
+
+/*
+ * Get our local IP address for this interface
+ *
+ * Arguments:
+ * s - socket to find address for
+ * aval - pointer to variable to store address in
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+get_local_ip (int s, long *aval)
+{
+ char intf_name[IFNAMSIZ];
+ socklen_t namelen = IFNAMSIZ;
+ struct air_netif_rsp *net_info = NULL;
+ struct sockaddr_in *sain;
+
+ /*
+ * Get physical interface name
+ */
+ if ( getsockopt ( s, T_ATM_SIGNALING, T_ATM_NET_INTF,
+ (caddr_t) intf_name, &namelen ) )
+ return;
+
+ /*
+ * Get network interface information for this physical interface
+ */
+ get_netif_info ( intf_name, &net_info );
+ if ( net_info == NULL )
+ return;
+
+ sain = (struct sockaddr_in *)(void *)&net_info->anp_proto_addr;
+
+ /*
+ * Fill in answer
+ */
+ bcopy ( (caddr_t)&sain->sin_addr.s_addr, aval, 4 );
+
+ free ( net_info );
+
+ return;
+
+}
+
+/*
+ * Set local NSAP prefix and then reply with our full NSAP address.
+ *
+ * Switch will send a SET message with the NSAP prefix after a coldStart.
+ * We'll set that prefix into HARP and then send a SET message of our own
+ * with our full interface NSAP address.
+ *
+ * Arguments:
+ * oid - objid from SET message
+ * hdr - pointer to internal SNMP header
+ * buf - pointer to SET buffer
+ * s - socket to send messages on
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+set_prefix (Objid *oid, Snmp_Header *hdr __unused, int intf)
+{
+ struct atmsetreq asr;
+ Atm_addr *aa;
+ int fd;
+ int i;
+
+ /*
+ * Build IOCTL request to set prefix
+ */
+ asr.asr_opcode = AIOCS_SET_PRF;
+ strncpy ( asr.asr_prf_intf, Intf[intf].anp_intf,
+ sizeof(asr.asr_prf_intf ) );
+ /*
+ * Pull prefix out of received Objid
+ * save in set_prefix IOCTL and addressEntry table
+ */
+ for ( i = 0; i < oid->oid[13]; i++ ) {
+ asr.asr_prf_pref[i] = oid->oid[i + 14];
+ }
+
+ /*
+ * Pass new prefix to the HARP kernel
+ */
+ fd = socket ( AF_ATM, SOCK_DGRAM, 0 );
+ if ( fd < 0 )
+ return;
+ if ( ioctl ( fd, AIOCSET, (caddr_t)&asr ) < 0 ) {
+ if ( errno != EALREADY ) {
+ syslog ( LOG_ERR, "ilmid: error setting prefix: %m" );
+ if ( Log ) {
+ write_timestamp();
+ fprintf ( Log, "errno %d setting prefix\n",
+ errno );
+ }
+ close ( fd );
+ return;
+ }
+ }
+ close ( fd );
+
+ /*
+ * Reload the cfg/intf info with newly set prefix
+ */
+ init_ilmi();
+
+ aa = &Intf[intf].anp_addr;
+
+ /*
+ * Copy our NSAP into addressEntry table
+ */
+
+ addressEntry[intf].oid[0] = 0;
+ for ( i = 0; i < aa->address_length; i++ ) {
+ addressEntry[intf].oid[0]++; /* Increment length */
+ addressEntry[intf].oid[i + 1] = (int)((u_char *)(aa->address))[i];
+
+ }
+
+ return;
+
+}
+
+static void
+set_address (Snmp_Header *hdr __unused, int intf)
+{
+ Variable *var;
+ int i, j;
+
+ PDU_Header = build_generic_header();
+
+ PDU_Header->head = (Variable *)malloc(sizeof(Variable));
+ if (PDU_Header->head == NULL) {
+ fprintf(stderr, "malloc() failed in %s()\n", __func__);
+ exit(1);
+ }
+ bzero(PDU_Header->head, sizeof(Variable));
+
+ var = PDU_Header->head;
+ /* Copy generic addressEntry OBJID */
+ bcopy ( (caddr_t)&Objids[ADDRESS_OBJID], (caddr_t)&var->oid,
+ sizeof(Objid) );
+ /* Set specific instance */
+ i = var->oid.oid[0] + 1; /* Get length */
+ var->oid.oid[i++] = 1;
+ var->oid.oid[i++] = 1;
+ var->oid.oid[i++] = 3;
+ var->oid.oid[i++] = 0;
+
+ /* Copy in address length */
+ var->oid.oid[i++] = addressEntry[intf].oid[0];
+
+ /* Copy in address */
+ for ( j = 0; j < addressEntry[intf].oid[0]; j++ )
+ var->oid.oid[i++] = addressEntry[intf].oid[j + 1];
+ var->oid.oid[0] = i - 1; /* Set new length */
+
+ /* Set == VALID */
+ var->type = ASN_INTEGER;
+ var->var.ival = 1;
+
+ build_pdu ( PDU_Header, PDU_TYPE_SET );
+ send_resp ( intf, PDU_Header, Resp_Buf );
+}
+
+/*
+ * Increment Debug Level
+ *
+ * Catches SIGUSR1 signal and increments value of Debug_Level
+ *
+ * Arguments:
+ * sig - signal number
+ *
+ * Returns:
+ * none - Debug_Level incremented
+ *
+ */
+static void
+Increment_DL (int sig __unused)
+{
+ Debug_Level++;
+ if ( Debug_Level && Log == (FILE *)NULL ) {
+ if ( foregnd ) {
+ Log = stderr;
+ } else {
+ if ( ( Log = fopen ( LOG_FILE, "a" ) ) == NULL )
+ Log = NULL;
+ }
+ if ( Log ) {
+ setbuf ( Log, NULL );
+ write_timestamp();
+ fprintf ( Log, "Raised Debug_Level to %d\n", Debug_Level );
+ }
+ }
+ signal ( SIGUSR1, Increment_DL );
+ return;
+}
+
+/*
+ * Decrement Debug Level
+ *
+ * Catches SIGUSR2 signal and decrements value of Debug_Level
+ *
+ * Arguments:
+ * sig - signal number
+ *
+ * Returns:
+ * none - Debug_Level decremented
+ *
+ */
+static void
+Decrement_DL (int sig __unused)
+{
+ Debug_Level--;
+ if ( Debug_Level <= 0 ) {
+ Debug_Level = 0;
+ if ( Log ) {
+ write_timestamp();
+ fprintf ( Log, "Lowered Debug_Level to %d\n", Debug_Level );
+ if ( !foregnd )
+ fclose ( Log );
+ Log = NULL;
+ }
+ }
+ signal ( SIGUSR2, Decrement_DL );
+ return;
+}
+
+/*
+ * Loop through GET variable list looking for matches
+ *
+ */
+static void
+process_get (Snmp_Header *hdr, int intf)
+{
+ Variable *var;
+ int idx;
+ int x;
+ int oidlen;
+
+ var = hdr->head;
+ while ( var ) {
+
+ /* Handle the 'GET PREFIX' request */
+ oidlen = Objids[SETPFX_OBJID].oid[0];
+ if (oid_ncmp(&var->oid, &Objids[SETPFX_OBJID], oidlen) == 0) {
+ var->var.ival = 2; /* assume not valid */
+ for(x = 0; x < 13; x++)
+ if (var->oid.oid[oidlen + x + 2] !=
+ addressEntry[intf].oid[x + 1])
+ break;
+
+ /* Address Match */
+ if (x == 13)
+ hdr->head->var.ival = 1;
+ var = var->next;
+ continue;
+ }
+
+ idx = find_var ( var );
+ switch ( idx ) {
+ case MADGE_OBJECT1:
+ /* reply with NO SUCH OBJECT */
+ var->type = ASN_NULL;
+ break;
+ case SYS_OBJID:
+ var->type = ASN_OBJID;
+ bcopy ( (caddr_t)&Objids[MY_OBJID],
+ (caddr_t)&var->var.oval,
+ sizeof(Objid) );
+ break;
+ case UPTIME_OBJID:
+ var->type = ASN_TIMESTAMP;
+ var->var.ival = get_ticks();
+ break;
+ case UNITYPE_OBJID:
+ var->type = ASN_INTEGER;
+ var->var.ival = UNITYPE_PRIVATE;
+ break;
+ case UNIVER_OBJID:
+ var->type = ASN_INTEGER;
+ switch ( Intf[intf].anp_sig_proto ) {
+ case ATM_SIG_UNI30:
+ var->var.ival = UNIVER_UNI30;
+ break;
+ case ATM_SIG_UNI31:
+ var->var.ival = UNIVER_UNI31;
+ break;
+ case ATM_SIG_UNI40:
+ var->var.ival = UNIVER_UNI40;
+ break;
+ default:
+ var->var.ival = UNIVER_UNKNOWN;
+ break;
+ }
+ break;
+ case DEVTYPE_OBJID:
+ var->type = ASN_INTEGER;
+ var->var.ival = DEVTYPE_USER;
+ break;
+ case MAXVCC_OBJID:
+ var->type = ASN_INTEGER;
+ var->var.ival = 1024;
+ break;
+ case PORT_OBJID:
+ var->type = ASN_INTEGER;
+ var->var.ival = intf + 1;
+ break;
+ case IPNM_OBJID:
+ var->type = ASN_IPADDR;
+ get_local_ip( ilmi_fd[intf], &var->var.aval );
+ break;
+ case ADDRESS_OBJID:
+ break;
+ case ATMF_PORTID:
+ var->type = ASN_INTEGER;
+ var->var.ival = 0x30 + intf;
+ break;
+ case ATMF_SYSID:
+ var->type = ASN_OCTET;
+ var->var.sval[0] = 6;
+ bcopy ( (caddr_t)&Cfg[intf].acp_macaddr,
+ (caddr_t)&var->var.sval[1], 6 );
+ break;
+ default:
+ /* NO_SUCH */
+ break;
+ }
+ var = var->next;
+ }
+ build_pdu ( hdr, PDU_TYPE_GETRESP );
+ send_resp ( intf, hdr, Resp_Buf );
+
+}
+
+/******************************************************************************
+ *
+ * Find an OBJID from known ones
+ *
+ * in: Variable with valid OID
+ * out: OID number (index), -1 = not found
+ */
+static int
+lmi_object_find (Variable *var)
+{
+ Objid * obj_var;
+ Objid * obj_cur;
+ size_t x;
+ int y;
+
+ obj_var = &var->oid;
+
+ for (x = 0; x < NUM_OIDS; x++) {
+ obj_cur = &Objids[x];
+ for (y = 0; y < 128; y++) {
+ if (obj_var->oid[y] != obj_cur->oid[y])
+ break;
+ if (obj_var->oid[y] == 0) /* object ID endmark */
+ return (x);
+ }
+ }
+
+ return (-1);
+}
+
+#if 0
+/******************************************************************************
+ *
+ * Append instance number to OID
+ *
+ * in: Variable, instance number
+ * out: zero = success
+ *
+ */
+static int
+lmi_object_instance (Variable *var, int instnum)
+{
+ int * oidptr;
+ int curlen;
+
+ oidptr = var->oid.oid;
+ curlen = oidptr[0]; /* current length */
+ if (curlen > 126)
+ return (1);
+ curlen++;
+ oidptr[curlen] = instnum;
+ oidptr[0] = curlen;
+ return (0);
+}
+#endif
+
+/******************************************************************************
+ *
+ * Handle received GETNEXT
+ *
+ * in: Header with valid fields, interface number
+ * out: zero = success
+ *
+ */
+static int
+lmi_rcvcmd_getnext (Snmp_Header *header, int intf)
+{
+ int * oidptr;
+ int oidlen;
+ int oidnum;
+ int x;
+
+ oidnum = lmi_object_find(header->head);
+ oidptr = header->head->oid.oid;
+ oidlen = oidptr[0];
+
+ switch(oidnum) {
+ /* Should be because the remote side is attempting
+ * to verify that our table is empty
+ */
+ case ADDRESS_OBJID:
+ if ( addressEntry[intf].oid[0] ) {
+ /* XXX - FIXME */
+ /* Our table is not empty - return address */
+ }
+ break;
+
+ /* Madge Collage sends GETNEXT for this */
+ case SETPFX_OBJID:
+ if(addressEntry[intf].oid[0]) { /* we have a prefix */
+ oidptr[0] += 14;
+ oidptr += oidlen; /* skip to last number */
+ oidptr++;
+ *oidptr++ = 13; /* length of prefix */
+
+ /* fill in the prefix */
+ for(x = 0; x < 13; x++) {
+ *oidptr++ = addressEntry[intf].oid[x+1];
+ }
+ header->head->type = ASN_INTEGER;
+ /* 1=valid, 2=invalid -- only 2 values */
+ header->head->var.ival = 1;
+ } else { /* no prefix available */
+ header->head->type = ASN_NULL;
+ }
+ break;
+
+ default:
+ return (1); /* unknown object ID */
+ }
+
+ build_pdu(header, PDU_TYPE_GETRESP);
+ send_resp(intf, header, Resp_Buf);
+
+ return (0);
+}
+
+
+/******************************************************************************
+ *
+ * Handle received TRAP
+ *
+ * in: Header with valid fields, interface number
+ * out: zero = success
+ *
+ */
+static int
+lmi_rcvcmd_trap (Snmp_Header *header __unused, int intf)
+{
+
+ bzero((caddr_t)&addressEntry[intf], sizeof(Objid));
+ return (0);
+}
+
+/*
+ * ILMI State Processing Loop
+ *
+ *
+ */
+static void
+ilmi_do_state(void)
+{
+ struct timeval tvp;
+ fd_set rfd;
+ u_char buf[1024];
+ Variable *var;
+ int intf;
+ int maxfd = 0;
+
+ /*
+ * Loop forever
+ */
+ for ( ; ; ) {
+ int count;
+ int n;
+ u_char *bpp;
+ Snmp_Header *Hdr;
+
+ /*
+ * SunOS CC doesn't allow automatic aggregate initialization.
+ * Initialize to zero which effects a poll operation.
+ */
+ tvp.tv_sec = 15;
+ tvp.tv_usec = 0;
+
+ /*
+ * Clear fd_set and initialize to check this interface
+ */
+ FD_ZERO ( &rfd );
+ for ( intf = 0; intf < MAX_UNITS; intf++ )
+ if ( ilmi_fd[intf] > 0 ) {
+ FD_SET ( ilmi_fd[intf], &rfd );
+ maxfd = MAX ( maxfd, ilmi_fd[intf] );
+ }
+
+ /*
+ * Check for new interfaces
+ */
+ ilmi_open();
+
+ for ( intf = 0; intf < MAX_UNITS; intf++ ) {
+ /*
+ * Do any pre-message state processing
+ */
+ switch ( ilmi_state[intf] ) {
+ case ILMI_COLDSTART:
+ /*
+ * Clear addressTable
+ */
+ bzero ( (caddr_t)&addressEntry[intf], sizeof(Objid) );
+
+ /*
+ * Start by sending a COLD_START trap. This should cause the
+ * remote end to clear the associated prefix/address table(s).
+ */
+ /* Build ColdStart TRAP header */
+ ColdStart_Header = build_cold_start();
+ build_pdu ( ColdStart_Header, PDU_TYPE_TRAP );
+ send_resp ( intf, ColdStart_Header, Resp_Buf );
+
+ /*
+ * Start a timeout so that if the next state fails, we re-enter
+ * ILMI_COLDSTART.
+ */
+ /* atm_timeout() */
+
+ /* Enter new state */
+ ilmi_state[intf] = ILMI_INIT;
+ /* fall into ILMI_INIT */
+
+ case ILMI_INIT:
+ /*
+ * After a COLD_START, we need to check that the remote end has
+ * cleared any tables. Send a GET_NEXT request to check for this.
+ * In the event that the table is not empty, or that no reply is
+ * received, return to COLD_START state.
+ */
+ PDU_Header = build_generic_header();
+
+ PDU_Header->head = (Variable *)malloc(sizeof(Variable));
+ if (PDU_Header->head == NULL) {
+ fprintf(stderr, "malloc() failed in %s()\n", __func__);
+ exit(1);
+ }
+ bzero(PDU_Header->head, sizeof(Variable));
+
+ var = PDU_Header->head;
+ bcopy ( (caddr_t)&Objids[ADDRESS_OBJID], (caddr_t)&var->oid,
+ sizeof(Objid) );
+ var->type = ASN_NULL;
+ var->next = NULL;
+
+ /*
+ * Send GETNEXT request looking for empty ATM Address Table
+ */
+ PDU_Header->reqid = Req_ID++;
+ build_pdu ( PDU_Header, PDU_TYPE_GETNEXT );
+ send_resp ( intf, PDU_Header, Resp_Buf );
+
+ /*
+ * Start a timeout while looking for SET message. If we don't receive
+ * a SET, then go back to COLD_START state.
+ */
+ /* atm_timeout() */
+ break;
+
+ case ILMI_RUNNING:
+ /* Normal SNMP processing */
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ count = select ( maxfd + 1, &rfd, NULL, NULL, &tvp );
+
+ for ( intf = 0; intf < MAX_UNITS; intf++ ) {
+ /*
+ * Check for received messages
+ */
+ if ( ilmi_fd[intf] > 0 && FD_ISSET ( ilmi_fd[intf], & rfd ) ) {
+
+ n = read ( ilmi_fd[intf], (caddr_t)&buf[1], sizeof(buf) - 1 );
+ if ( n == -1 && ( errno == ECONNRESET || errno == EBADF ) ) {
+ ilmi_state[intf] = ILMI_COLDSTART;
+ close ( ilmi_fd[intf] );
+ ilmi_fd[intf] = -1;
+ } else {
+ bpp = &buf[1];
+ Hdr = asn_get_header(&bpp);
+
+ if ( Log && Debug_Level > 1 )
+ print_pdu(PDU_RECV, intf, Hdr, n, buf);
+
+ if (Hdr == NULL)
+ continue;
+
+ /* What we do with this messages depends upon the state we're in */
+ switch ( ilmi_state[intf] ) {
+ case ILMI_COLDSTART:
+ /* We should never be in this state here */
+ free_pdu ( Hdr );
+ break;
+ case ILMI_INIT:
+ /* The only messages we care about are GETNEXTs, GETRESPs, and TRAPs */
+ switch ( Hdr->pdutype ) {
+ case PDU_TYPE_GETNEXT:
+ lmi_rcvcmd_getnext(Hdr, intf);
+ break;
+ case PDU_TYPE_GETRESP:
+ /*
+ * This should be in response to our GETNEXT.
+ * Check the OIDs and go onto ILMI_RUNNING if
+ * the address table is empty. We can cheat and
+ * not check sequence numbers because we only send
+ * the one GETNEXT request and ILMI says we shouldn't
+ * have interleaved sessions.
+ */
+ /*
+ * First look for empty table. If found, go to next state.
+ */
+ if ((Hdr->error == SNMP_ERR_NOSUCHNAME) ||
+ ((Hdr->error == SNMP_ERR_NOERROR) &&
+ ( oid_ncmp ( &Objids[ADDRESS_OBJID], &Hdr->head->oid,
+ Objids[ADDRESS_OBJID].oid[0] ) == 1 ))) {
+ ilmi_state[intf] = ILMI_RUNNING; /* ILMI_REG; */
+ } else if (Hdr->error == SNMP_ERR_NOERROR) {
+ /*
+ * Check to see if this matches our address
+ * and if so, that it's a VALID entry.
+ */
+ Atm_addr *aa;
+ int l;
+ int match = 1;
+
+ aa = &Intf[intf].anp_addr;
+ if ( aa->address_length == Hdr->head->oid.oid[13] ) {
+ for ( l = 0; l < aa->address_length; l++ ) {
+ if ( (int)((u_char *)(aa->address))[l] !=
+ Hdr->head->oid.oid[14 + l] ) {
+ match = 0;
+ }
+ }
+ }
+ if ( match ) {
+ if ( Hdr->head->var.ival == 1 ) {
+ ilmi_state[intf] = ILMI_RUNNING;
+ }
+ }
+ }
+ free_pdu ( Hdr );
+ break;
+ case PDU_TYPE_SET:
+ /* Look for SET_PREFIX Objid */
+ if ( oid_ncmp ( &Hdr->head->oid,
+ &Objids[SETPFX_OBJID],
+ Objids[SETPFX_OBJID].oid[0] ) == 0 ) {
+ set_prefix ( &Hdr->head->oid, Hdr, intf );
+ /* Reply to SET before sending our ADDRESS */
+ build_pdu(Hdr, PDU_TYPE_GETRESP);
+ send_resp( intf, Hdr, Resp_Buf );
+ set_address ( Hdr, intf );
+ } else {
+ build_pdu(Hdr, PDU_TYPE_GETRESP);
+ send_resp( intf, Hdr, Resp_Buf );
+ }
+ break;
+ case PDU_TYPE_TRAP:
+ /* Remote side wants us to start fresh */
+ lmi_rcvcmd_trap(Hdr, intf);
+ free_pdu ( Hdr );
+ break;
+ default:
+ /* Ignore */
+ free_pdu ( Hdr );
+ break;
+ }
+ break;
+ case ILMI_REG:
+ break;
+ case ILMI_RUNNING:
+ /* We'll take anything here */
+ switch ( Hdr->pdutype ) {
+ case PDU_TYPE_GET:
+ process_get ( Hdr, intf );
+ break;
+ case PDU_TYPE_GETRESP:
+ /* Ignore GETRESPs */
+ free_pdu ( Hdr );
+ break;
+ case PDU_TYPE_GETNEXT:
+ lmi_rcvcmd_getnext(Hdr, intf);
+ break;
+ case PDU_TYPE_SET:
+ /* Look for SET_PREFIX Objid */
+ if ( oid_ncmp ( &Hdr->head->oid,
+ &Objids[SETPFX_OBJID],
+ Objids[SETPFX_OBJID].oid[0] ) == 0 ) {
+ set_prefix ( &Hdr->head->oid, Hdr, intf );
+ /* Reply to SET before sending our ADDRESS */
+ build_pdu(Hdr, PDU_TYPE_GETRESP);
+ send_resp( intf, Hdr, Resp_Buf );
+ set_address ( Hdr, intf );
+ } else {
+ build_pdu(Hdr, PDU_TYPE_GETRESP);
+ send_resp( intf, Hdr, Resp_Buf );
+ }
+ break;
+ case PDU_TYPE_TRAP:
+ lmi_rcvcmd_trap(Hdr, intf);
+ free_pdu ( Hdr );
+ break;
+ }
+ break;
+ default:
+ /* Unknown state */
+ free_pdu ( Hdr );
+ break;
+ }
+ } /* if n > 0 */
+ } /* if received message */
+ } /* for each interface */
+ } /* for ever loop */
+
+}
+
+int
+main (int argc, char *argv[])
+{
+ int c;
+ int i;
+ int Reset = 0; /* Should we send a coldStart and exit? */
+
+ /*
+ * What are we running as? (argv[0])
+ */
+ progname = strdup ( (char *)basename ( argv[0] ) );
+ /*
+ * What host are we
+ */
+ gethostname ( hostname, sizeof ( hostname ) );
+
+ /*
+ * Ilmid needs to run as root to set prefix
+ */
+ if ( getuid() != 0 ) {
+ fprintf ( stderr, "%s: needs to run as root.\n", progname );
+ exit ( -1 );
+ }
+
+ /*
+ * Parse arguments
+ */
+ while ( ( c = getopt ( argc, argv, "d:fr" ) ) != -1 )
+ switch ( c ) {
+ case 'd':
+ Debug_Level = atoi ( optarg );
+ break;
+ case 'f':
+ foregnd++;
+ break;
+ case 'r':
+ Reset++;
+ break;
+ case '?':
+ fprintf ( stderr, "usage: %s [-d level] [-f] [-r]\n",
+ progname );
+ exit ( -1 );
+/* NOTREACHED */
+ break;
+ }
+
+ /*
+ * If we're not doing debugging, run in the background
+ */
+ if ( foregnd == 0 ) {
+ if ( daemon ( 0, 0 ) )
+ err ( 1, "Can't fork" );
+ } /* else
+ setbuf ( stdout, NULL ); */
+
+ signal ( SIGUSR1, Increment_DL );
+ signal ( SIGUSR2, Decrement_DL );
+
+ /*
+ * Open log file
+ */
+ if ( Debug_Level ) {
+ if ( foregnd ) {
+ Log = stderr;
+ } else {
+ if ( ( Log = fopen ( LOG_FILE, "a" ) ) == NULL )
+ Log = NULL;
+ }
+ }
+ if ( Log )
+ setbuf ( Log, NULL );
+
+ /*
+ * Get our startup time
+ */
+ (void) gettimeofday ( &starttime, NULL );
+ starttime.tv_sec--;
+ starttime.tv_usec += 1000000;
+
+ /* Randomize starting request ID */
+ Req_ID = starttime.tv_sec;
+
+ /*
+ * Reset all the interface descriptors
+ */
+ for ( i = 0; i < MAX_UNITS; i++ ) {
+ ilmi_fd[i] = -1;
+ }
+ /*
+ * Try to open all the interfaces
+ */
+ ilmi_open ();
+
+ /*
+ * If we're just sending a coldStart end exiting...
+ */
+ if ( Reset ) {
+ for ( i = 0; i < MAX_UNITS; i++ )
+ if ( ilmi_fd[i] >= 0 ) {
+ /* Build ColdStart TRAP header */
+ ColdStart_Header = build_cold_start();
+ build_pdu ( ColdStart_Header, PDU_TYPE_TRAP );
+ send_resp ( i, ColdStart_Header, Resp_Buf );
+ if ( Debug_Level > 1 && Log ) {
+ write_timestamp();
+ fprintf ( Log, "Close ilmi_fd[%d]: %d\n",
+ i, ilmi_fd[i] );
+ }
+ close ( ilmi_fd[i] );
+ }
+ exit ( 2 );
+ }
+
+ ilmi_do_state();
+
+ exit(0);
+}
diff --git a/sbin/badsect/Makefile b/sbin/badsect/Makefile
new file mode 100644
index 0000000..a392277
--- /dev/null
+++ b/sbin/badsect/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= badsect
+DPADD= ${LIBUFS}
+LDADD= -lufs
+MAN= badsect.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/badsect/badsect.8 b/sbin/badsect/badsect.8
new file mode 100644
index 0000000..293e958
--- /dev/null
+++ b/sbin/badsect/badsect.8
@@ -0,0 +1,133 @@
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)badsect.8 8.1 (Berkeley) 6/5/93
+.\" $FreeBSD$
+.\"
+.Dd June 5, 1993
+.Dt BADSECT 8
+.Os
+.Sh NAME
+.Nm badsect
+.Nd create files to contain bad sectors
+.Sh SYNOPSIS
+.Nm
+.Ar bbdir sector ...
+.Sh DESCRIPTION
+The
+.Nm
+utility makes a file to contain a bad sector.
+Normally, bad sectors
+are made inaccessible by the standard formatter, which provides
+a forwarding table for bad sectors to the driver.
+If a driver supports the bad blocking standard it is much preferable to
+use that method to isolate bad blocks, since the bad block forwarding
+makes the pack appear perfect, and such packs can then be copied with
+.Xr dd 1 .
+The technique used by this program is also less general than
+bad block forwarding, as
+.Nm
+cannot make amends for
+bad blocks in the i-list of file systems or in swap areas.
+.Pp
+On some disks,
+adding a sector which is suddenly bad to the bad sector table
+currently requires the running of the standard
+.Tn DEC
+formatter.
+Thus to deal with a newly bad block
+or on disks where the drivers
+do not support the bad-blocking standard
+.Nm
+may be used to good effect.
+.Pp
+The
+.Nm
+utility is used on a quiet file system in the following way:
+First mount the file system, and change to its root directory.
+Make a directory
+.Li BAD
+there.
+Run
+.Nm
+giving as argument the
+.Ar BAD
+directory followed by
+all the bad sectors you wish to add.
+(The sector numbers must be relative to the beginning of
+the file system, but this is not hard as the system reports
+relative sector numbers in its console error messages.)
+Then change back to the root directory, unmount the file system
+and run
+.Xr fsck 8
+on the file system.
+The bad sectors should show up in two files
+or in the bad sector files and the free list.
+Have
+.Xr fsck 8
+remove files containing the offending bad sectors, but
+.Em do not
+have it remove the
+.Pa BAD/ Ns Em nnnnn
+files.
+This will leave the bad sectors in only the
+.Li BAD
+files.
+.Pp
+The
+.Nm
+utility works by giving the specified sector numbers in a
+.Xr mknod 2
+system call,
+creating an illegal file whose first block address is the block containing
+bad sector and whose name is the bad sector number.
+When it is discovered by
+.Xr fsck 8
+it will ask
+.Dq Li "HOLD BAD BLOCK \&?" .
+A positive response will cause
+.Xr fsck 8
+to convert the inode to a regular file containing the bad block.
+.Sh DIAGNOSTICS
+The
+.Nm
+utility refuses to attach a block that
+resides in a critical area or is out of range of the file system.
+A warning is issued if the block is already in use.
+.Sh SEE ALSO
+.Xr fsck 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.1 .
+.Sh BUGS
+If more than one sector which comprise a file system fragment are bad,
+you should specify only one of them to
+.Nm ,
+as the blocks in the bad sector files actually cover all the sectors in a
+file system fragment.
diff --git a/sbin/badsect/badsect.c b/sbin/badsect/badsect.c
new file mode 100644
index 0000000..9389171
--- /dev/null
+++ b/sbin/badsect/badsect.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 1981, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1981, 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char sccsid[] = "@(#)badsect.c 8.1 (Berkeley) 6/5/93";
+#endif
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * badsect
+ *
+ * Badsect takes a list of file-system relative sector numbers
+ * and makes files containing the blocks of which these sectors are a part.
+ * It can be used to contain sectors which have problems if these sectors
+ * are not part of the bad file for the pack (see bad144). For instance,
+ * this program can be used if the driver for the file system in question
+ * does not support bad block forwarding.
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/disklabel.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <libufs.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define sblock disk.d_fs
+#define acg disk.d_cg
+struct uufsd disk;
+struct fs *fs = &sblock;
+int errs;
+
+int chkuse(daddr_t, int);
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: badsect bbdir blkno ...\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ daddr_t diskbn;
+ daddr_t number;
+ struct stat stbuf, devstat;
+ struct dirent *dp;
+ DIR *dirp;
+ char name[2 * MAXPATHLEN];
+ char *name_dir_end;
+
+ if (argc < 3)
+ usage();
+ if (chdir(argv[1]) < 0 || stat(".", &stbuf) < 0)
+ err(2, "%s", argv[1]);
+ strcpy(name, _PATH_DEV);
+ if ((dirp = opendir(name)) == NULL)
+ err(3, "%s", name);
+ name_dir_end = name + strlen(name);
+ while ((dp = readdir(dirp)) != NULL) {
+ strcpy(name_dir_end, dp->d_name);
+ if (lstat(name, &devstat) < 0)
+ err(4, "%s", name);
+ if (stbuf.st_dev == devstat.st_rdev &&
+ (devstat.st_mode & IFMT) == IFCHR)
+ break;
+ }
+ closedir(dirp);
+ if (dp == NULL) {
+ printf("Cannot find dev 0%lo corresponding to %s\n",
+ (u_long)stbuf.st_rdev, argv[1]);
+ exit(5);
+ }
+ if (ufs_disk_fillout(&disk, name) == -1) {
+ if (disk.d_error != NULL)
+ errx(6, "%s: %s", name, disk.d_error);
+ else
+ err(7, "%s", name);
+ }
+ for (argc -= 2, argv += 2; argc > 0; argc--, argv++) {
+ number = strtol(*argv, NULL, 0);
+ if (errno == EINVAL || errno == ERANGE)
+ err(8, "%s", *argv);
+ if (chkuse(number, 1))
+ continue;
+ /*
+ * Print a warning if converting the block number to a dev_t
+ * will truncate it. badsect was not very useful in versions
+ * of BSD before 4.4 because dev_t was 16 bits and another
+ * bit was lost by bogus sign extensions.
+ */
+ diskbn = dbtofsb(fs, number);
+ if ((dev_t)diskbn != diskbn) {
+ printf("sector %ld cannot be represented as a dev_t\n",
+ (long)number);
+ errs++;
+ }
+ else if (mknod(*argv, IFMT|0600, (dev_t)diskbn) < 0) {
+ warn("%s", *argv);
+ errs++;
+ }
+ }
+ ufs_disk_close(&disk);
+ printf("Don't forget to run ``fsck %s''\n", name);
+ exit(errs);
+}
+
+int
+chkuse(daddr_t blkno, int cnt)
+{
+ int cg;
+ daddr_t fsbn, bn;
+
+ fsbn = dbtofsb(fs, blkno);
+ if ((unsigned)(fsbn+cnt) > fs->fs_size) {
+ printf("block %ld out of range of file system\n", (long)blkno);
+ return (1);
+ }
+ cg = dtog(fs, fsbn);
+ if (fsbn < cgdmin(fs, cg)) {
+ if (cg == 0 || (fsbn+cnt) > cgsblock(fs, cg)) {
+ printf("block %ld in non-data area: cannot attach\n",
+ (long)blkno);
+ return (1);
+ }
+ } else {
+ if ((fsbn+cnt) > cgbase(fs, cg+1)) {
+ printf("block %ld in non-data area: cannot attach\n",
+ (long)blkno);
+ return (1);
+ }
+ }
+ if (cgread1(&disk, cg) != 1) {
+ fprintf(stderr, "cg %d: could not be read\n", cg);
+ errs++;
+ return (1);
+ }
+ if (!cg_chkmagic(&acg)) {
+ fprintf(stderr, "cg %d: bad magic number\n", cg);
+ errs++;
+ return (1);
+ }
+ bn = dtogd(fs, fsbn);
+ if (isclr(cg_blksfree(&acg), bn))
+ printf("Warning: sector %ld is in use\n", (long)blkno);
+ return (0);
+}
diff --git a/sbin/bsdlabel/Makefile b/sbin/bsdlabel/Makefile
new file mode 100644
index 0000000..716dab3
--- /dev/null
+++ b/sbin/bsdlabel/Makefile
@@ -0,0 +1,26 @@
+# @(#)Makefile 8.2 (Berkeley) 3/17/94
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../sys/geom
+
+PROG= bsdlabel
+SRCS= bsdlabel.c geom_bsd_enc.c
+#MAN= bsdlabel.5
+MAN+= bsdlabel.8
+
+.if ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "alpha" || \
+ ${MACHINE_ARCH} == "amd64"
+LINKS= ${BINDIR}/bsdlabel ${BINDIR}/disklabel
+MLINKS= bsdlabel.8 disklabel.8
+.endif
+
+DPADD= ${LIBGEOM}
+LDADD= -lgeom
+
+.include <bsd.prog.mk>
+
+test: ${PROG}
+ sh ${.CURDIR}/runtest.sh
+
+testx: ${PROG}
+ sh -x ${.CURDIR}/runtest.sh
diff --git a/sbin/bsdlabel/bsdlabel.5 b/sbin/bsdlabel/bsdlabel.5
new file mode 100644
index 0000000..a0bb82e
--- /dev/null
+++ b/sbin/bsdlabel/bsdlabel.5
@@ -0,0 +1,521 @@
+.\" Copyright (c) 1987, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Symmetric Computer Systems.
+.\"
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)disklabel.5.5 8.1 (Berkeley) 6/5/93
+.\" $FreeBSD$
+.\"
+.Dd June 5, 1993
+.Dt DISKLABEL 5
+.Os
+.Sh NAME
+.Nm disklabel
+.Nd disk pack label
+.Sh SYNOPSIS
+.In sys/disklabel.h
+.Sh DESCRIPTION
+Each disk or disk pack on a system may contain a disk label
+which provides detailed information
+about the geometry of the disk and the partitions into which the disk
+is divided.
+It should be initialized when the disk is formatted,
+and may be changed later with the
+.Xr disklabel 8
+program.
+This information is used by the system disk driver and by the bootstrap
+program to determine how to program the drive
+and where to find the file systems on the disk partitions.
+Additional information is used by the file system in order
+to use the disk most efficiently and to locate important file system information.
+The description of each partition contains an identifier for the partition
+type (standard file system, swap area, etc.).
+The file system updates the in-core copy of the label if it contains
+incomplete information about the file system.
+.Pp
+The label is located in sector number
+.Dv LABELSECTOR
+of the drive, usually sector 0 where it may be found
+without any information about the disk geometry.
+It is at an offset
+.Dv LABELOFFSET
+from the beginning of the sector, to allow room for the initial bootstrap.
+The disk sector containing the label is normally made read-only
+so that it is not accidentally overwritten by pack-to-pack copies
+or swap operations;
+the
+.Dv DIOCWLABEL
+.Xr ioctl 2 ,
+which is done as needed by the
+.Xr disklabel 8
+program.
+.Pp
+A copy of the in-core label for a disk can be obtained with the
+.Dv DIOCGDINFO
+.Xr ioctl 2 ;
+this works with a file descriptor for a block or character (``raw'') device
+for any partition of the disk.
+The in-core copy of the label is set by the
+.Dv DIOCSDINFO
+.Xr ioctl 2 .
+The offset of a partition cannot generally be changed while it is open,
+nor can it be made smaller while it is open.
+One exception is that any change is allowed if no label was found
+on the disk, and the driver was able to construct only a skeletal label
+without partition information.
+Finally, the
+.Dv DIOCWDINFO
+.Xr ioctl 2
+operation sets the in-core label and then updates the on-disk label;
+there must be an existing label on the disk for this operation to succeed.
+Thus, the initial label for a disk or disk pack must be installed
+by writing to the raw disk.
+All of these operations are normally done using
+.Xr disklabel 8 .
+.Pp
+The format of the disk label, as specified in
+.In sys/disklabel.h ,
+is
+.Bd -literal
+#ifndef _SYS_DISKLABEL_H_
+#define _SYS_DISKLABEL_H_
+
+#ifndef _KERNEL
+#include <sys/types.h>
+#endif
+#include <sys/ioccom.h>
+
+/*
+ * Disk description table, see disktab(5)
+ */
+#define _PATH_DISKTAB "/etc/disktab"
+#define DISKTAB "/etc/disktab" /* deprecated */
+
+/*
+ * Each disk has a label which includes information about the hardware
+ * disk geometry, file system partitions, and drive specific information.
+ * The label is in block 0 or 1, possibly offset from the beginning
+ * to leave room for a bootstrap, etc.
+ */
+
+/* XXX these should be defined per controller (or drive) elsewhere, not here! */
+#ifdef __i386__
+#define LABELSECTOR 1 /* sector containing label */
+#define LABELOFFSET 0 /* offset of label in sector */
+#endif
+
+#ifdef __alpha__
+#define LABELSECTOR 0
+#define LABELOFFSET 64
+#endif
+
+#ifndef LABELSECTOR
+#define LABELSECTOR 0 /* sector containing label */
+#endif
+
+#ifndef LABELOFFSET
+#define LABELOFFSET 64 /* offset of label in sector */
+#endif
+
+#define DISKMAGIC ((u_int32_t)0x82564557) /* The disk magic number */
+#ifndef MAXPARTITIONS
+#define MAXPARTITIONS 8
+#endif
+
+#define LABEL_PART 2 /* partition containing label */
+#define RAW_PART 2 /* partition containing whole disk */
+#define SWAP_PART 1 /* partition normally containing swap */
+
+#ifndef LOCORE
+struct disklabel {
+ u_int32_t d_magic; /* the magic number */
+ u_int16_t d_type; /* drive type */
+ u_int16_t d_subtype; /* controller/d_type specific */
+ char d_typename[16]; /* type name, e.g. "eagle" */
+ char d_packname[16]; /* pack identifier */
+
+ /* disk geometry: */
+ u_int32_t d_secsize; /* # of bytes per sector */
+ u_int32_t d_nsectors; /* # of data sectors per track */
+ u_int32_t d_ntracks; /* # of tracks per cylinder */
+ u_int32_t d_ncylinders; /* # of data cylinders per unit */
+ u_int32_t d_secpercyl; /* # of data sectors per cylinder */
+ u_int32_t d_secperunit; /* # of data sectors per unit */
+
+ /*
+ * Spares (bad sector replacements) below are not counted in
+ * d_nsectors or d_secpercyl. Spare sectors are assumed to
+ * be physical sectors which occupy space at the end of each
+ * track and/or cylinder.
+ */
+ u_int16_t d_sparespertrack; /* # of spare sectors per track */
+ u_int16_t d_sparespercyl; /* # of spare sectors per cylinder */
+ /*
+ * Alternate cylinders include maintenance, replacement, configuration
+ * description areas, etc.
+ */
+ u_int32_t d_acylinders; /* # of alt. cylinders per unit */
+
+ /* hardware characteristics: */
+ /*
+ * d_interleave, d_trackskew and d_cylskew describe perturbations
+ * in the media format used to compensate for a slow controller.
+ * Interleave is physical sector interleave, set up by the
+ * formatter or controller when formatting. When interleaving is
+ * in use, logically adjacent sectors are not physically
+ * contiguous, but instead are separated by some number of
+ * sectors. It is specified as the ratio of physical sectors
+ * traversed per logical sector. Thus an interleave of 1:1
+ * implies contiguous layout, while 2:1 implies that logical
+ * sector 0 is separated by one sector from logical sector 1.
+ * d_trackskew is the offset of sector 0 on track N relative to
+ * sector 0 on track N-1 on the same cylinder. Finally, d_cylskew
+ * is the offset of sector 0 on cylinder N relative to sector 0
+ * on cylinder N-1.
+ */
+ u_int16_t d_rpm; /* rotational speed */
+ u_int16_t d_interleave; /* hardware sector interleave */
+ u_int16_t d_trackskew; /* sector 0 skew, per track */
+ u_int16_t d_cylskew; /* sector 0 skew, per cylinder */
+ u_int32_t d_headswitch; /* head switch time, usec */
+ u_int32_t d_trkseek; /* track-to-track seek, usec */
+ u_int32_t d_flags; /* generic flags */
+#define NDDATA 5
+ u_int32_t d_drivedata[NDDATA]; /* drive-type specific information */
+#define NSPARE 5
+ u_int32_t d_spare[NSPARE]; /* reserved for future use */
+ u_int32_t d_magic2; /* the magic number (again) */
+ u_int16_t d_checksum; /* xor of data incl. partitions */
+
+ /* file system and partition information: */
+ u_int16_t d_npartitions; /* number of partitions in following */
+ u_int32_t d_bbsize; /* size of boot area at sn0, bytes */
+ u_int32_t d_sbsize; /* max size of fs superblock, bytes */
+ struct partition { /* the partition table */
+ u_int32_t p_size; /* number of sectors in partition */
+ u_int32_t p_offset; /* starting sector */
+ u_int32_t p_fsize; /* file system basic fragment size */
+ u_int8_t p_fstype; /* file system type, see below */
+ u_int8_t p_frag; /* file system fragments per block */
+ union {
+ u_int16_t cpg; /* UFS: FS cylinders per group */
+ u_int16_t sgs; /* LFS: FS segment shift */
+ } __partition_u1;
+#define p_cpg __partition_u1.cpg
+#define p_sgs __partition_u1.sgs
+ } d_partitions[MAXPARTITIONS]; /* actually may be more */
+};
+#else /* LOCORE */
+ /*
+ * offsets for asm boot files.
+ */
+ .set d_secsize,40
+ .set d_nsectors,44
+ .set d_ntracks,48
+ .set d_ncylinders,52
+ .set d_secpercyl,56
+ .set d_secperunit,60
+ .set d_end_,276 /* size of disk label */
+#endif /* LOCORE */
+
+/* d_type values: */
+#define DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */
+#define DTYPE_MSCP 2 /* MSCP */
+#define DTYPE_DEC 3 /* other DEC (rk, rl) */
+#define DTYPE_SCSI 4 /* SCSI */
+#define DTYPE_ESDI 5 /* ESDI interface */
+#define DTYPE_ST506 6 /* ST506 etc. */
+#define DTYPE_HPIB 7 /* CS/80 on HP-IB */
+#define DTYPE_HPFL 8 /* HP Fiber-link */
+#define DTYPE_FLOPPY 10 /* floppy */
+#define DTYPE_CCD 11 /* concatenated disk */
+#define DTYPE_VINUM 12 /* vinum volume */
+#define DTYPE_DOC2K 13 /* Msys DiskOnChip */
+
+#if defined(PC98) && !defined(PC98_ATCOMPAT)
+#define DSTYPE_SEC256 0x80 /* physical sector size=256 */
+#endif
+
+#ifdef DKTYPENAMES
+static char *dktypenames[] = {
+ "unknown",
+ "SMD",
+ "MSCP",
+ "old DEC",
+ "SCSI",
+ "ESDI",
+ "ST506",
+ "HP-IB",
+ "HP-FL",
+ "type 9",
+ "floppy",
+ "CCD",
+ "Vinum",
+ "DOC2K",
+ NULL
+};
+#define DKMAXTYPES (sizeof(dktypenames) / sizeof(dktypenames[0]) - 1)
+#endif
+
+/*
+ * File system type and version.
+ * Used to interpret other file system-specific
+ * per-partition information.
+ */
+#define FS_UNUSED 0 /* unused */
+#define FS_SWAP 1 /* swap */
+#define FS_V6 2 /* Sixth Edition */
+#define FS_V7 3 /* Seventh Edition */
+#define FS_SYSV 4 /* System V */
+#define FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */
+#define FS_V8 6 /* Eighth Edition, 4K blocks */
+#define FS_BSDFFS 7 /* 4.2BSD fast file system */
+#define FS_MSDOS 8 /* MSDOS file system */
+#define FS_BSDLFS 9 /* 4.4BSD log-structured file system */
+#define FS_OTHER 10 /* in use, but unknown/unsupported */
+#define FS_HPFS 11 /* OS/2 high-performance file system */
+#define FS_ISO9660 12 /* ISO 9660, normally CD-ROM */
+#define FS_BOOT 13 /* partition contains bootstrap */
+#define FS_VINUM 14 /* Vinum drive */
+
+#ifdef DKTYPENAMES
+static char *fstypenames[] = {
+ "unused",
+ "swap",
+ "Version 6",
+ "Version 7",
+ "System V",
+ "4.1BSD",
+ "Eighth Edition",
+ "4.2BSD",
+ "MSDOS",
+ "4.4LFS",
+ "unknown",
+ "HPFS",
+ "ISO9660",
+ "boot",
+ "vinum",
+ NULL
+};
+#define FSMAXTYPES (sizeof(fstypenames) / sizeof(fstypenames[0]) - 1)
+#endif
+
+/*
+ * flags shared by various drives:
+ */
+#define D_REMOVABLE 0x01 /* removable media */
+#define D_ECC 0x02 /* supports ECC */
+#define D_BADSECT 0x04 /* supports bad sector forw. */
+#define D_RAMDISK 0x08 /* disk emulator */
+#define D_CHAIN 0x10 /* can do back-back transfers */
+
+/*
+ * Drive data for SMD.
+ */
+#define d_smdflags d_drivedata[0]
+#define D_SSE 0x1 /* supports skip sectoring */
+#define d_mindist d_drivedata[1]
+#define d_maxdist d_drivedata[2]
+#define d_sdist d_drivedata[3]
+
+/*
+ * Drive data for ST506.
+ */
+#define d_precompcyl d_drivedata[0]
+#define d_gap3 d_drivedata[1] /* used only when formatting */
+
+/*
+ * Drive data for SCSI.
+ */
+#define d_blind d_drivedata[0]
+
+#ifndef LOCORE
+/*
+ * Structure used to perform a format or other raw operation, returning
+ * data and/or register values. Register identification and format
+ * are device- and driver-dependent.
+ */
+struct format_op {
+ char *df_buf;
+ int df_count; /* value-result */
+ daddr_t df_startblk;
+ int df_reg[8]; /* result */
+};
+
+/*
+ * Structure used internally to retrieve information about a partition
+ * on a disk.
+ */
+struct partinfo {
+ struct disklabel *disklab;
+ struct partition *part;
+};
+
+/* DOS partition table -- located in boot block */
+
+#if defined(PC98) && !defined(PC98_ATCOMPAT)
+#define DOSBBSECTOR 0 /* DOS boot block relative sector number */
+#define DOSLABELSECTOR 1 /* 0: 256b/s, 1: 512b/s */
+#define DOSPARTOFF 0
+#define NDOSPART 16
+#define DOSPTYP_386BSD 0x94 /* 386BSD partition type */
+#define MBR_PTYPE_FreeBSD 0x94 /* FreeBSD partition type */
+
+struct dos_partition {
+ unsigned char dp_mid;
+#define DOSMID_386BSD (0x14|0x80) /* 386bsd|bootable */
+ unsigned char dp_sid;
+#define DOSSID_386BSD (0x44|0x80) /* 386bsd|active */
+ unsigned char dp_dum1;
+ unsigned char dp_dum2;
+ unsigned char dp_ipl_sct;
+ unsigned char dp_ipl_head;
+ unsigned short dp_ipl_cyl;
+ unsigned char dp_ssect; /* starting sector */
+ unsigned char dp_shd; /* starting head */
+ unsigned short dp_scyl; /* starting cylinder */
+ unsigned char dp_esect; /* end sector */
+ unsigned char dp_ehd; /* end head */
+ unsigned short dp_ecyl; /* end cylinder */
+ unsigned char dp_name[16];
+};
+
+#else /* IBMPC */
+#define DOSBBSECTOR 0 /* DOS boot block relative sector number */
+#define DOSPARTOFF 446
+#define NDOSPART 4
+#define DOSPTYP_386BSD 0xa5 /* 386BSD partition type */
+
+struct dos_partition {
+ unsigned char dp_flag; /* bootstrap flags */
+ unsigned char dp_shd; /* starting head */
+ unsigned char dp_ssect; /* starting sector */
+ unsigned char dp_scyl; /* starting cylinder */
+ unsigned char dp_typ; /* partition type */
+ unsigned char dp_ehd; /* end head */
+ unsigned char dp_esect; /* end sector */
+ unsigned char dp_ecyl; /* end cylinder */
+ u_int32_t dp_start; /* absolute starting sector number */
+ u_int32_t dp_size; /* partition size in sectors */
+};
+#endif
+
+#define DPSECT(s) ((s) & 0x3f) /* isolate relevant bits of sector */
+#define DPCYL(c, s) ((c) + (((s) & 0xc0)<<2)) /* and those that are cylinder */
+
+/*
+ * Disk-specific ioctls.
+ */
+ /* get and set disklabel; DIOCGPART used internally */
+#define DIOCGDINFO _IOR('d', 101, struct disklabel)/* get */
+#define DIOCSDINFO _IOW('d', 102, struct disklabel)/* set */
+#define DIOCWDINFO _IOW('d', 103, struct disklabel)/* set, update disk */
+#define DIOCGPART _IOW('d', 104, struct partinfo) /* get partition */
+
+#define DIOCWLABEL _IOW('d', 109, int) /* write en/disable label */
+
+#ifdef _KERNEL
+
+/*
+ * XXX encoding of disk minor numbers, should be elsewhere.
+ *
+ * See <sys/reboot.h> for a possibly better encoding.
+ *
+ * "cpio -H newc" can be used to back up device files with large minor
+ * numbers (but not ones >= 2^31). Old cpio formats and all tar formats
+ * don't have enough bits, and cpio and tar don't notice the lossage.
+ * There are also some sign extension bugs.
+ */
+
+/*
+ 3 2 1 0
+ 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ _________________________________________________________________
+ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+ -----------------------------------------------------------------
+ | TYPE |UNIT_2 | SLICE | MAJOR? | UNIT |PART |
+ -----------------------------------------------------------------
+*/
+#define dkmakeminor(unit, slice, part) \e
+ (((slice) << 16) | (((unit) & 0x1e0) << 16) | \e
+ (((unit) & 0x1f) << 3) | (part))
+static __inline dev_t
+dkmodpart(dev_t dev, int part)
+{
+ return (makedev(major(dev), (minor(dev) & ~7) | part));
+}
+
+static __inline dev_t
+dkmodslice(dev_t dev, int slice)
+{
+ return (makedev(major(dev), (minor(dev) & ~0x1f0000) | (slice << 16)));
+}
+
+#define dkpart(dev) (minor(dev) & 7)
+#define dkslice(dev) ((minor(dev) >> 16) & 0x1f)
+#define dktype(dev) ((minor(dev) >> 25) & 0x7f)
+
+static __inline u_int
+dkunit(dev_t dev)
+{
+ return (((minor(dev) >> 16) & 0x1e0) | ((minor(dev) >> 3) & 0x1f));
+}
+
+struct buf;
+struct buf_queue_head;
+
+int bounds_check_with_label(struct buf *bp, struct disklabel *lp,
+ int wlabel);
+void diskerr(struct buf *bp, char *what, int pri, int blkdone,
+ struct disklabel *lp);
+void disksort(struct buf *ap, struct buf *bp);
+u_int dkcksum struct disklabel *lp);
+char *readdisklabel(dev_t dev, struct disklabel *lp);
+void bufqdisksort(struct buf_queue_head *ap, struct buf *bp);
+int setdisklabel(struct disklabel *olp, struct disklabel *nlp,
+ u_long openmask);
+int writedisklabel(dev_t dev, struct disklabel *lp);
+#ifdef __alpha__
+void alpha_fix_srm_checksum(struct buf *bp);
+#endif
+
+#endif /* _KERNEL */
+
+#endif /* LOCORE */
+
+#ifndef _KERNEL
+__BEGIN_DECLS
+struct disklabel *getdiskbyname(const char *);
+__END_DECLS
+#endif
+
+#endif /* !_SYS_DISKLABEL_H_ */
+.Ed
+.Sh SEE ALSO
+.Xr disktab 5 ,
+.Xr disklabel 8
diff --git a/sbin/bsdlabel/bsdlabel.8 b/sbin/bsdlabel/bsdlabel.8
new file mode 100644
index 0000000..46b1bdc
--- /dev/null
+++ b/sbin/bsdlabel/bsdlabel.8
@@ -0,0 +1,505 @@
+.\" Copyright (c) 1987, 1988, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Symmetric Computer Systems.
+.\"
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)disklabel.8 8.2 (Berkeley) 4/19/94
+.\" $FreeBSD$
+.\"
+.Dd September 17, 2005
+.Dt BSDLABEL 8
+.Os
+.Sh NAME
+.Nm bsdlabel
+.Nd read and write disk pack label
+.Sh SYNOPSIS
+.Nm
+.Op Fl A
+.Ar disk | Fl f Ar file
+.Nm
+.Fl w
+.Op Fl \&An
+.Op Fl B Op Fl b Ar boot
+.Op Fl m Ar machine
+.Ar disk | Fl f Ar file
+.Op Ar type
+.Nm
+.Fl e
+.Op Fl \&An
+.Op Fl B Op Fl b Ar boot
+.Op Fl m Ar machine
+.Ar disk | Fl f Ar file
+.Nm
+.Fl R
+.Op Fl \&An
+.Op Fl B Op Fl b Ar boot
+.Op Fl m Ar machine
+.Op Fl f
+.Ar disk | Fl f Ar file
+.Ar protofile
+.Sh DESCRIPTION
+The
+.Nm
+utility
+installs, examines or modifies the
+.Bx
+label on a disk partition, or on a file containing a partition image.
+In addition,
+.Nm
+can install bootstrap code.
+.Ss Disk Device Name
+When specifying the device (i.e., when the
+.Fl f
+option is not used),
+the
+.Pa /dev/
+path prefix may be omitted;
+the
+.Nm
+utility will automatically prepend it.
+.Ss General Options
+The
+.Fl A
+option enables processing of the historical parts of the
+.Bx
+label.
+If the option is not given, suitable values are set for these fields.
+.Pp
+The
+.Fl f
+option tells
+.Nm
+that the program will operate on a file instead of a disk partition.
+.Pp
+The
+.Fl n
+option stops the
+.Nm
+program right before the disk would have been modified, and displays
+the result instead of writing it.
+.Pp
+The
+.Fl m Ar machine
+argument forces
+.Nm
+to use a layout suitable for a different architecture.
+Current valid values are
+.Cm i386 , amd64 , ia64 , pc98 ,
+and
+.Cm alpha .
+If this option is omitted,
+.Nm
+will use a layout suitable for the current machine.
+.Ss Reading the Disk Label
+To examine the label on a disk drive, use
+.Nm
+without options:
+.Pp
+.Nm
+.Op Fl A
+.Op Fl m Ar machine
+.Ar disk
+.Pp
+.Ar disk
+represents the disk in question, and may be in the form
+.Pa da0
+or
+.Pa /dev/da0 .
+It will display the partition layout.
+.Ss Writing a Standard Label
+To write a standard label, use the form
+.Pp
+.Nm
+.Fl w
+.Op Fl \&An
+.Op Fl m Ar machine
+.Ar disk
+.Op Ar type
+.Pp
+If the drive type is specified, the entry of that name in the
+.Xr disktab 5
+file is used; otherwise a default layout is used.
+.Ss Editing an Existing Disk Label
+To edit an existing disk label, use the form
+.Pp
+.Nm
+.Fl e
+.Op Fl \&An
+.Op Fl m Ar machine
+.Ar disk
+.Pp
+This command opens the disk label in the default editor, and when the editor
+exits, the label is validated and if OK written to disk.
+.Ss Restoring a Disk Label From a File
+To restore a disk label from a file, use the form
+.Pp
+.Nm
+.Fl R
+.Op Fl \&An
+.Op Fl m Ar machine
+.Ar disk protofile
+.Pp
+.Nm
+is capable of restoring a disk label that was previously saved in a file in
+.Tn ASCII
+format.
+The prototype file used to create the label should be in the same format as that
+produced when reading or editing a label.
+Comments are delimited by
+.Ql #
+and newline.
+.Ss Installing Bootstraps
+If the
+.Fl B
+argument is specified, bootstrap code will be read from the file
+.Pa /boot/boot
+and written to the disk.
+The
+.Fl b Ar boot
+argument allows a different file to be used.
+.Sh FILES
+.Bl -tag -width ".Pa /etc/disktab" -compact
+.It Pa /boot/boot
+Default boot image.
+.It Pa /etc/disktab
+Disk description file.
+.El
+.Sh SAVED FILE FORMAT
+The
+.Nm
+utility
+uses an
+.Tn ASCII
+version of the label when examining, editing, or restoring a disk
+label.
+The format is:
+.Bd -literal -offset 4n
+
+8 partitions:
+# size offset fstype [fsize bsize bps/cpg]
+ a: 81920 0 4.2BSD 1024 8192 16
+ b: 160000 81920 swap
+ c: 1173930 0 unused 0 0 # "raw" part, don't edit
+.Ed
+.Pp
+If the
+.Fl A
+option is specified, the format is:
+.Bd -literal -offset 4n
+# /dev/da1c:
+type: SCSI
+disk: da0s1
+label:
+flags:
+bytes/sector: 512
+sectors/track: 51
+tracks/cylinder: 19
+sectors/cylinder: 969
+cylinders: 1211
+sectors/unit: 1173930
+rpm: 3600
+interleave: 1
+trackskew: 0
+cylinderskew: 0
+headswitch: 0 # milliseconds
+track-to-track seek: 0 # milliseconds
+drivedata: 0
+
+8 partitions:
+# size offset fstype [fsize bsize bps/cpg]
+ a: 81920 0 4.2BSD 1024 8192 16
+ b: 160000 81920 swap
+ c: 1173930 0 unused 0 0 # "raw" part, don't edit
+.Ed
+.Pp
+Lines starting with a
+.Ql #
+mark are comments.
+.Pp
+The partition table can have up to 8 entries.
+It contains the following information:
+.Bl -tag -width indent
+.It Ar #
+The partition identifier is a single letter in the range
+.Ql a
+to
+.Ql h .
+By convention, partition
+.Ql c
+is reserved to describe the entire disk.
+.It Ar size
+The size of the partition in sectors,
+.Cm K
+(kilobytes - 1024),
+.Cm M
+(megabytes - 1024*1024),
+.Cm G
+(gigabytes - 1024*1024*1024),
+.Cm %
+(percentage of free space
+.Em after
+removing any fixed-size partitions other than partition
+.Ql c ) ,
+or
+.Cm *
+(all remaining free space
+.Em after
+fixed-size and percentage partitions).
+For partition
+.Ql c ,
+a size of
+.Cm *
+indicates the entire disk.
+Lowercase versions of
+.Cm K , M ,
+and
+.Cm G
+are allowed.
+Size and type should be specified without any spaces between them.
+.Pp
+Example: 2097152, 1G, 1024M and 1048576K are all the same size
+(assuming 512-byte sectors).
+.It Ar offset
+The offset of the start of the partition from the beginning of the
+drive in sectors, or
+.Cm *
+to have
+.Nm
+calculate the correct offset to use (the end of the previous partition plus
+one, ignoring partition
+.Ql c .
+For partition
+.Ql c ,
+.Cm *
+will be interpreted as an offset of 0.
+.It Ar fstype
+Describes the purpose of the partition.
+The example shows all currently used partition types.
+For
+.Tn UFS
+file systems and
+.Xr ccd 4
+partitions, use type
+.Cm 4.2BSD .
+For Vinum drives, use type
+.Cm vinum .
+Other common types are
+.Cm swap
+and
+.Cm unused .
+By convention, partition
+.Ql c
+represents the entire slice and should be of type
+.Cm unused ,
+though
+.Nm
+does not enforce this convention.
+The
+.Nm
+utility
+also knows about a number of other partition types,
+none of which are in current use.
+(See the definitions starting with
+.Dv FS_UNUSED
+in
+.In sys/disklabel.h
+for more details.)
+.It Ar fsize
+For
+.Cm 4.2BSD
+and
+.Tn LFS
+file systems only, the fragment size.
+Defaults to 1024 for partitions smaller than 1GB,
+4096 for partitions 1GB or larger.
+.It Ar bsize
+For
+.Cm 4.2BSD
+and
+.Tn LFS
+file systems only, the block size.
+Defaults to 8192 for partitions smaller than 1GB,
+16384 for partitions 1GB or larger.
+.It Ar bps/cpg
+For
+.Cm 4.2BSD
+file systems, the number of cylinders in a cylinder group.
+For
+.Tn LFS
+file systems, the segment shift value.
+Defaults to 16 for partitions smaller than 1GB,
+64 for partitions 1GB or larger.
+.El
+.Sh EXAMPLES
+.Dl "bsdlabel da0s1"
+.Pp
+Display the label for the first slice of the
+.Pa da0
+disk, as obtained via
+.Pa /dev/da0s1 .
+.Pp
+.Dl "bsdlabel da0s1 > savedlabel"
+.Pp
+Save the in-core label for
+.Pa da0s1
+into the file
+.Pa savedlabel .
+This file can be used with the
+.Fl R
+option to restore the label at a later date.
+.Pp
+.Dl "bsdlabel -w /dev/da0s1"
+.Pp
+Create a label for
+.Pa da0s1 .
+.Pp
+.Dl "bsdlabel -e da0s1"
+.Pp
+Read the label for
+.Pa da0s1 ,
+edit it, and install the result.
+.Pp
+.Dl "bsdlabel -e -n da0s1"
+.Pp
+Read the on-disk label for
+.Pa da0s1 ,
+edit it, and display what the new label would be (in sectors).
+It does
+.Em not
+install the new label either in-core or on-disk.
+.Pp
+.Dl "bsdlabel -w da0s1"
+.Pp
+Write a default label on
+.Pa da0s1 .
+Use another
+.Nm Fl e
+command to edit the
+partitioning and file system information.
+.Pp
+.Dl "bsdlabel -R da0s1 savedlabel"
+.Pp
+Restore the on-disk and in-core label for
+.Pa da0s1
+from information in
+.Pa savedlabel .
+.Pp
+.Dl "bsdlabel -R -n da0s1 label_layout"
+.Pp
+Display what the label would be for
+.Pa da0s1
+using the partition layout in
+.Pa label_layout .
+This is useful for determining how much space would be allotted for various
+partitions with a labeling scheme using
+.Cm % Ns -based
+or
+.Cm *
+partition sizes.
+.Pp
+.Dl "bsdlabel -B da0s1"
+.Pp
+Install a new bootstrap on
+.Pa da0s1 .
+The boot code comes from
+.Pa /boot/boot .
+.Pp
+.Dl "bsdlabel -w -B -b newboot /dev/da0s1"
+.Pp
+Install a new label and bootstrap.
+The bootstrap code comes from the file
+.Pa newboot
+in the current working directory.
+.Bd -literal -offset indent
+dd if=/dev/zero of=/dev/da0 bs=512 count=32
+fdisk -BI da0
+dd if=/dev/zero of=/dev/da0s1 bs=512 count=32
+bsdlabel -w -B da0s1
+bsdlabel -e da0s1
+.Ed
+.Pp
+Completely wipe any prior information on the disk, creating a new bootable
+disk with a
+.Tn DOS
+partition table containing one slice, covering the whole disk.
+Initialize the label on this slice,
+then edit it.
+The
+.Xr dd 1
+commands are optional, but may be necessary for some
+.Tn BIOS Ns es
+to properly
+recognize the disk.
+.Pp
+This is an example disk label that uses some of the new partition size types
+such as
+.Cm % , M , G ,
+and
+.Cm * ,
+which could be used as a source file for
+.Dq Li "bsdlabel -R ad0s1c new_label_file" :
+.Bd -literal -offset 4n
+# /dev/ad0s1c:
+
+8 partitions:
+# size offset fstype [fsize bsize bps/cpg]
+ a: 400M 0 4.2BSD 4096 16384 75 # (Cyl. 0 - 812*)
+ b: 1G * swap
+ c: * * unused
+ e: 204800 * 4.2BSD
+ f: 5g * 4.2BSD
+ g: * * 4.2BSD
+.Ed
+.Sh DIAGNOSTICS
+The kernel device drivers will not allow the size of a disk partition
+to be decreased or the offset of a partition to be changed while it is open.
+.Sh COMPATIBILITY
+Due to the use of an
+.Vt u_int32_t
+to store the number of sectors,
+.Bx
+labels are restricted to a maximum of 2^32-1 sectors.
+This usually means 2TB of disk space.
+Larger disks should be partitioned using another method such as
+.Xr gpt 8 .
+.Pp
+The various
+.Bx Ns s
+all use slightly different versions of
+.Bx
+labels and
+are not generally compatible.
+.Sh SEE ALSO
+.Xr ccd 4 ,
+.Xr geom 4 ,
+.Xr md 4 ,
+.\" Xr bsdlabel 5 ,
+.Xr disktab 5 ,
+.Xr boot0cfg 8 ,
+.Xr fdisk 8 ,
+.Xr gpt 8
diff --git a/sbin/bsdlabel/bsdlabel.c b/sbin/bsdlabel/bsdlabel.c
new file mode 100644
index 0000000..366d5ec
--- /dev/null
+++ b/sbin/bsdlabel/bsdlabel.c
@@ -0,0 +1,1498 @@
+/*
+ * Copyright (c) 1994, 1995 Gordon W. Ross
+ * Copyright (c) 1994 Theo de Raadt
+ * All rights reserved.
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Symmetric Computer Systems.
+ *
+ * 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 the University of
+ * California, Berkeley and its contributors.
+ * This product includes software developed by Theo de Raadt.
+ * 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 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: $NetBSD: disksubr.c,v 1.13 2000/12/17 22:39:18 pk $
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1987, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)disklabel.c 8.2 (Berkeley) 1/7/94";
+/* from static char sccsid[] = "@(#)disklabel.c 1.2 (Symmetric) 11/28/85"; */
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <stdint.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/disk.h>
+#define DKTYPENAMES
+#define FSTYPENAMES
+#include <sys/disklabel.h>
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <libgeom.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+
+#include "pathnames.h"
+
+static void makelabel(const char *, struct disklabel *);
+static int writelabel(void);
+static int readlabel(int flag);
+static void display(FILE *, const struct disklabel *);
+static int edit(void);
+static int editit(void);
+static void fixlabel(struct disklabel *);
+static char *skip(char *);
+static char *word(char *);
+static int getasciilabel(FILE *, struct disklabel *);
+static int getasciipartspec(char *, struct disklabel *, int, int);
+static int checklabel(struct disklabel *);
+static void usage(void);
+static struct disklabel *getvirginlabel(void);
+
+#define DEFEDITOR _PATH_VI
+
+static char *dkname;
+static char *specname;
+static char tmpfil[] = PATH_TMPFILE;
+
+static struct disklabel lab;
+static u_char bootarea[BBSIZE];
+static off_t mediasize;
+static u_int secsize;
+static char blank[] = "";
+static char unknown[] = "unknown";
+
+#define MAX_PART ('z')
+#define MAX_NUM_PARTS (1 + MAX_PART - 'a')
+static char part_size_type[MAX_NUM_PARTS];
+static char part_offset_type[MAX_NUM_PARTS];
+static int part_set[MAX_NUM_PARTS];
+
+static int installboot; /* non-zero if we should install a boot program */
+static int allfields; /* present all fields in edit */
+static char const *xxboot; /* primary boot */
+
+static off_t mbroffset;
+#ifndef LABELSECTOR
+#define LABELSECTOR -1
+#endif
+#ifndef LABELOFFSET
+#define LABELOFFSET -1
+#endif
+static int labelsoffset = LABELSECTOR;
+static int labeloffset = LABELOFFSET;
+static int bbsize = BBSIZE;
+static int alphacksum =
+#if defined(__alpha__)
+ 1;
+#else
+ 0;
+#endif
+
+enum {
+ UNSPEC, EDIT, READ, RESTORE, WRITE, WRITEBOOT
+} op = UNSPEC;
+
+
+static int disable_write; /* set to disable writing to disk label */
+static int is_file; /* work on a file (abs. pathname), "-f" opt. */
+
+int
+main(int argc, char *argv[])
+{
+ FILE *t;
+ int ch, error = 0;
+ char const *name = 0;
+
+ while ((ch = getopt(argc, argv, "ABb:efm:nRrs:w")) != -1)
+ switch (ch) {
+ case 'A':
+ allfields = 1;
+ break;
+ case 'B':
+ ++installboot;
+ break;
+ case 'b':
+ xxboot = optarg;
+ break;
+ case 'f':
+ is_file=1;
+ break;
+ case 'm':
+ if (!strcmp(optarg, "i386") ||
+ !strcmp(optarg, "amd64") ||
+ !strcmp(optarg, "ia64") ||
+ !strcmp(optarg, "pc98")) {
+ labelsoffset = 1;
+ labeloffset = 0;
+ bbsize = 8192;
+ alphacksum = 0;
+ } else if (!strcmp(optarg, "alpha")) {
+ labelsoffset = 0;
+ labeloffset = 64;
+ bbsize = 8192;
+ alphacksum = 1;
+ } else {
+ errx(1, "Unsupported architecture");
+ }
+ break;
+ case 'n':
+ disable_write = 1;
+ break;
+ case 'R':
+ if (op != UNSPEC)
+ usage();
+ op = RESTORE;
+ break;
+ case 'e':
+ if (op != UNSPEC)
+ usage();
+ op = EDIT;
+ break;
+ case 'r':
+ /*
+ * We accept and ignode -r for compatibility with
+ * historically disklabel usage.
+ */
+ break;
+ case 'w':
+ if (op != UNSPEC)
+ usage();
+ op = WRITE;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage();
+ if (labelsoffset < 0 || labeloffset < 0)
+ errx(1, "a -m <architecture> option must be specified");
+
+ /* Figure out the names of the thing we're working on */
+ if (is_file) {
+ dkname = specname = argv[0];
+ } else if (argv[0][0] != '/') {
+ dkname = argv[0];
+ asprintf(&specname, "%s%s", _PATH_DEV, argv[0]);
+ } else if (strncmp(argv[0], _PATH_DEV, strlen(_PATH_DEV)) == 0) {
+ dkname = argv[0] + strlen(_PATH_DEV);
+ specname = argv[0];
+ } else {
+ dkname = strrchr(argv[0], '/');
+ dkname++;
+ specname = argv[0];
+ }
+
+ if (installboot && op == UNSPEC)
+ op = WRITEBOOT;
+ else if (op == UNSPEC)
+ op = READ;
+
+ switch(op) {
+
+ case UNSPEC:
+ break;
+
+ case EDIT:
+ if (argc != 1)
+ usage();
+ readlabel(1);
+ fixlabel(&lab);
+ error = edit();
+ break;
+
+ case READ:
+ if (argc != 1)
+ usage();
+ readlabel(1);
+ display(stdout, NULL);
+ error = checklabel(NULL);
+ break;
+
+ case RESTORE:
+ if (argc != 2)
+ usage();
+ if (!(t = fopen(argv[1], "r")))
+ err(4, "fopen %s", argv[1]);
+ readlabel(0);
+ if (!getasciilabel(t, &lab))
+ exit(1);
+ error = writelabel();
+ break;
+
+ case WRITE:
+ if (argc == 2)
+ name = argv[1];
+ else if (argc == 1)
+ name = "auto";
+ else
+ usage();
+ readlabel(0);
+ makelabel(name, &lab);
+ fixlabel(&lab);
+ if (checklabel(NULL) == 0)
+ error = writelabel();
+ break;
+
+ case WRITEBOOT:
+
+ readlabel(1);
+ fixlabel(&lab);
+ if (argc == 2)
+ makelabel(argv[1], &lab);
+ if (checklabel(NULL) == 0)
+ error = writelabel();
+ break;
+ }
+ exit(error);
+}
+
+static void
+fixlabel(struct disklabel *lp)
+{
+ struct partition *dp;
+ int i;
+
+ for (i = 0; i < MAXPARTITIONS; i++) {
+ if (i == RAW_PART)
+ continue;
+ if (lp->d_partitions[i].p_size)
+ return;
+ }
+
+ dp = &lp->d_partitions[0];
+ dp->p_offset = BBSIZE / secsize;
+ dp->p_size = lp->d_secperunit - dp->p_offset;
+}
+
+/*
+ * Construct a prototype disklabel from /etc/disktab.
+ */
+static void
+makelabel(const char *type, struct disklabel *lp)
+{
+ struct disklabel *dp;
+
+ if (strcmp(type, "auto") == 0)
+ dp = getvirginlabel();
+ else
+ dp = getdiskbyname(type);
+ if (dp == NULL)
+ errx(1, "%s: unknown disk type", type);
+ *lp = *dp;
+ bzero(lp->d_packname, sizeof(lp->d_packname));
+}
+
+static void
+readboot(void)
+{
+ int fd, i;
+ struct stat st;
+ uint64_t *p;
+
+ if (xxboot == NULL)
+ xxboot = "/boot/boot";
+ fd = open(xxboot, O_RDONLY);
+ if (fd < 0)
+ err(1, "cannot open %s", xxboot);
+ fstat(fd, &st);
+ if (alphacksum && st.st_size <= BBSIZE - 512) {
+ i = read(fd, bootarea + 512, st.st_size);
+ if (i != st.st_size)
+ err(1, "read error %s", xxboot);
+
+ /*
+ * Set the location and length so SRM can find the
+ * boot blocks.
+ */
+ p = (uint64_t *)bootarea;
+ p[60] = (st.st_size + secsize - 1) / secsize;
+ p[61] = 1;
+ p[62] = 0;
+ return;
+ } else if ((!alphacksum) && st.st_size <= BBSIZE) {
+ i = read(fd, bootarea, st.st_size);
+ if (i != st.st_size)
+ err(1, "read error %s", xxboot);
+ return;
+ }
+ errx(1, "boot code %s is wrong size", xxboot);
+}
+
+static int
+writelabel(void)
+{
+ uint64_t *p, sum;
+ int i, fd;
+ struct gctl_req *grq;
+ char const *errstr;
+ struct disklabel *lp = &lab;
+
+ if (disable_write) {
+ warnx("write to disk label supressed - label was as follows:");
+ display(stdout, NULL);
+ return (0);
+ }
+
+ lp->d_magic = DISKMAGIC;
+ lp->d_magic2 = DISKMAGIC;
+ lp->d_checksum = 0;
+ lp->d_checksum = dkcksum(lp);
+ if (installboot)
+ readboot();
+ for (i = 0; i < lab.d_npartitions; i++)
+ if (lab.d_partitions[i].p_size)
+ lab.d_partitions[i].p_offset += mbroffset;
+ bsd_disklabel_le_enc(bootarea + labeloffset + labelsoffset * secsize,
+ lp);
+ if (alphacksum) {
+ /* Generate the bootblock checksum for the SRM console. */
+ for (p = (uint64_t *)bootarea, i = 0, sum = 0; i < 63; i++)
+ sum += p[i];
+ p[63] = sum;
+ }
+
+ fd = open(specname, O_RDWR);
+ if (fd < 0) {
+ if (is_file) {
+ warn("cannot open file %s for writing label", specname);
+ return(1);
+ }
+ grq = gctl_get_handle();
+ gctl_ro_param(grq, "verb", -1, "write label");
+ gctl_ro_param(grq, "class", -1, "BSD");
+ gctl_ro_param(grq, "geom", -1, dkname);
+ gctl_ro_param(grq, "label", 148+16*8,
+ bootarea + labeloffset + labelsoffset * secsize);
+ errstr = gctl_issue(grq);
+ if (errstr != NULL) {
+ warnx("%s", errstr);
+ gctl_free(grq);
+ return(1);
+ }
+ gctl_free(grq);
+ if (installboot) {
+ grq = gctl_get_handle();
+ gctl_ro_param(grq, "verb", -1, "write bootcode");
+ gctl_ro_param(grq, "class", -1, "BSD");
+ gctl_ro_param(grq, "geom", -1, dkname);
+ gctl_ro_param(grq, "bootcode", BBSIZE, bootarea);
+ errstr = gctl_issue(grq);
+ if (errstr != NULL) {
+ warnx("%s", errstr);
+ gctl_free(grq);
+ return (1);
+ }
+ gctl_free(grq);
+ }
+ } else {
+ if (write(fd, bootarea, bbsize) != bbsize) {
+ warn("write %s", specname);
+ close (fd);
+ return (1);
+ }
+ close (fd);
+ }
+ return (0);
+}
+
+static void
+get_file_parms(int f)
+{
+ int i;
+ struct stat sb;
+
+ if (fstat(f, &sb) != 0)
+ err(4, "fstat failed");
+ i = sb.st_mode & S_IFMT;
+ if (i != S_IFREG && i != S_IFLNK)
+ errx(4, "%s is not a valid file or link", specname);
+ secsize = DEV_BSIZE;
+ mediasize = sb.st_size;
+}
+
+/*
+ * Fetch disklabel for disk.
+ * Use ioctl to get label unless -r flag is given.
+ */
+static int
+readlabel(int flag)
+{
+ int f, i;
+ int error;
+ struct gctl_req *grq;
+ char const *errstr;
+
+ f = open(specname, O_RDONLY);
+ if (f < 0)
+ err(1, specname);
+ if (is_file)
+ get_file_parms(f);
+ else if ((ioctl(f, DIOCGMEDIASIZE, &mediasize) != 0) ||
+ (ioctl(f, DIOCGSECTORSIZE, &secsize) != 0)) {
+ err(4, "cannot get disk geometry");
+ }
+ if (mediasize > (off_t)0xffffffff * secsize)
+ errx(1,
+ "disks with more than 2^32-1 sectors are not supported");
+ (void)lseek(f, (off_t)0, SEEK_SET);
+ if (read(f, bootarea, BBSIZE) != BBSIZE)
+ err(4, "%s read", specname);
+ close (f);
+ error = bsd_disklabel_le_dec(
+ bootarea + (labeloffset + labelsoffset * secsize),
+ &lab, MAXPARTITIONS);
+ if (flag && error)
+ errx(1, "%s: no valid label found", specname);
+
+ grq = gctl_get_handle();
+ gctl_ro_param(grq, "verb", -1, "read mbroffset");
+ gctl_ro_param(grq, "class", -1, "BSD");
+ gctl_ro_param(grq, "geom", -1, dkname);
+ gctl_rw_param(grq, "mbroffset", sizeof(mbroffset), &mbroffset);
+ errstr = gctl_issue(grq);
+ if (errstr != NULL) {
+ mbroffset = 0;
+ gctl_free(grq);
+ return (error);
+ }
+ mbroffset /= lab.d_secsize;
+ if (lab.d_partitions[RAW_PART].p_offset == mbroffset)
+ for (i = 0; i < lab.d_npartitions; i++)
+ if (lab.d_partitions[i].p_size)
+ lab.d_partitions[i].p_offset -= mbroffset;
+ return (error);
+}
+
+
+static void
+display(FILE *f, const struct disklabel *lp)
+{
+ int i, j;
+ const struct partition *pp;
+
+ if (lp == NULL)
+ lp = &lab;
+
+ fprintf(f, "# %s:\n", specname);
+ if (allfields) {
+ if (lp->d_type < DKMAXTYPES)
+ fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
+ else
+ fprintf(f, "type: %u\n", lp->d_type);
+ fprintf(f, "disk: %.*s\n", (int)sizeof(lp->d_typename),
+ lp->d_typename);
+ fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname),
+ lp->d_packname);
+ fprintf(f, "flags:");
+ if (lp->d_flags & D_REMOVABLE)
+ fprintf(f, " removeable");
+ if (lp->d_flags & D_ECC)
+ fprintf(f, " ecc");
+ if (lp->d_flags & D_BADSECT)
+ fprintf(f, " badsect");
+ fprintf(f, "\n");
+ fprintf(f, "bytes/sector: %lu\n", (u_long)lp->d_secsize);
+ fprintf(f, "sectors/track: %lu\n", (u_long)lp->d_nsectors);
+ fprintf(f, "tracks/cylinder: %lu\n", (u_long)lp->d_ntracks);
+ fprintf(f, "sectors/cylinder: %lu\n", (u_long)lp->d_secpercyl);
+ fprintf(f, "cylinders: %lu\n", (u_long)lp->d_ncylinders);
+ fprintf(f, "sectors/unit: %lu\n", (u_long)lp->d_secperunit);
+ fprintf(f, "rpm: %u\n", lp->d_rpm);
+ fprintf(f, "interleave: %u\n", lp->d_interleave);
+ fprintf(f, "trackskew: %u\n", lp->d_trackskew);
+ fprintf(f, "cylinderskew: %u\n", lp->d_cylskew);
+ fprintf(f, "headswitch: %lu\t\t# milliseconds\n",
+ (u_long)lp->d_headswitch);
+ fprintf(f, "track-to-track seek: %ld\t# milliseconds\n",
+ (u_long)lp->d_trkseek);
+ fprintf(f, "drivedata: ");
+ for (i = NDDATA - 1; i >= 0; i--)
+ if (lp->d_drivedata[i])
+ break;
+ if (i < 0)
+ i = 0;
+ for (j = 0; j <= i; j++)
+ fprintf(f, "%lu ", (u_long)lp->d_drivedata[j]);
+ fprintf(f, "\n\n");
+ }
+ fprintf(f, "%u partitions:\n", lp->d_npartitions);
+ fprintf(f,
+ "# size offset fstype [fsize bsize bps/cpg]\n");
+ pp = lp->d_partitions;
+ for (i = 0; i < lp->d_npartitions; i++, pp++) {
+ if (pp->p_size) {
+ fprintf(f, " %c: %8lu %8lu ", 'a' + i,
+ (u_long)pp->p_size, (u_long)pp->p_offset);
+ if (pp->p_fstype < FSMAXTYPES)
+ fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
+ else
+ fprintf(f, "%8d", pp->p_fstype);
+ switch (pp->p_fstype) {
+
+ case FS_UNUSED: /* XXX */
+ fprintf(f, " %5lu %5lu %5.5s ",
+ (u_long)pp->p_fsize,
+ (u_long)(pp->p_fsize * pp->p_frag), "");
+ break;
+
+ case FS_BSDFFS:
+ fprintf(f, " %5lu %5lu %5u ",
+ (u_long)pp->p_fsize,
+ (u_long)(pp->p_fsize * pp->p_frag),
+ pp->p_cpg);
+ break;
+
+ case FS_BSDLFS:
+ fprintf(f, " %5lu %5lu %5d",
+ (u_long)pp->p_fsize,
+ (u_long)(pp->p_fsize * pp->p_frag),
+ pp->p_cpg);
+ break;
+
+ default:
+ fprintf(f, "%20.20s", "");
+ break;
+ }
+ if (i == RAW_PART) {
+ fprintf(f, " # \"raw\" part, don't edit");
+ }
+ fprintf(f, "\n");
+ }
+ }
+ fflush(f);
+}
+
+static int
+edit(void)
+{
+ int c, fd;
+ struct disklabel label;
+ FILE *fp;
+
+ if ((fd = mkstemp(tmpfil)) == -1 ||
+ (fp = fdopen(fd, "w")) == NULL) {
+ warnx("can't create %s", tmpfil);
+ return (1);
+ }
+ display(fp, NULL);
+ fclose(fp);
+ for (;;) {
+ if (!editit())
+ break;
+ fp = fopen(tmpfil, "r");
+ if (fp == NULL) {
+ warnx("can't reopen %s for reading", tmpfil);
+ break;
+ }
+ bzero((char *)&label, sizeof(label));
+ c = getasciilabel(fp, &label);
+ fclose(fp);
+ if (c) {
+ lab = label;
+ if (writelabel() == 0) {
+ (void) unlink(tmpfil);
+ return (0);
+ }
+ }
+ printf("re-edit the label? [y]: ");
+ fflush(stdout);
+ c = getchar();
+ if (c != EOF && c != (int)'\n')
+ while (getchar() != (int)'\n')
+ ;
+ if (c == (int)'n')
+ break;
+ }
+ (void) unlink(tmpfil);
+ return (1);
+}
+
+static int
+editit(void)
+{
+ int pid, xpid;
+ int locstat, omask;
+ const char *ed;
+
+ omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
+ while ((pid = fork()) < 0) {
+ if (errno == EPROCLIM) {
+ warnx("you have too many processes");
+ return(0);
+ }
+ if (errno != EAGAIN) {
+ warn("fork");
+ return(0);
+ }
+ sleep(1);
+ }
+ if (pid == 0) {
+ sigsetmask(omask);
+ setgid(getgid());
+ setuid(getuid());
+ if ((ed = getenv("EDITOR")) == (char *)0)
+ ed = DEFEDITOR;
+ execlp(ed, ed, tmpfil, (char *)0);
+ err(1, "%s", ed);
+ }
+ while ((xpid = wait(&locstat)) >= 0)
+ if (xpid == pid)
+ break;
+ sigsetmask(omask);
+ return(!locstat);
+}
+
+static char *
+skip(char *cp)
+{
+
+ while (*cp != '\0' && isspace(*cp))
+ cp++;
+ if (*cp == '\0' || *cp == '#')
+ return (NULL);
+ return (cp);
+}
+
+static char *
+word(char *cp)
+{
+ char c;
+
+ while (*cp != '\0' && !isspace(*cp) && *cp != '#')
+ cp++;
+ if ((c = *cp) != '\0') {
+ *cp++ = '\0';
+ if (c != '#')
+ return (skip(cp));
+ }
+ return (NULL);
+}
+
+/*
+ * Read an ascii label in from fd f,
+ * in the same format as that put out by display(),
+ * and fill in lp.
+ */
+static int
+getasciilabel(FILE *f, struct disklabel *lp)
+{
+ char *cp;
+ const char **cpp;
+ u_int part;
+ char *tp, line[BUFSIZ];
+ u_long v;
+ int lineno = 0, errors = 0;
+ int i;
+
+ makelabel("auto", lp);
+ bzero(&part_set, sizeof(part_set));
+ bzero(&part_size_type, sizeof(part_size_type));
+ bzero(&part_offset_type, sizeof(part_offset_type));
+ lp->d_bbsize = BBSIZE; /* XXX */
+ lp->d_sbsize = 0; /* XXX */
+ while (fgets(line, sizeof(line) - 1, f)) {
+ lineno++;
+ if ((cp = index(line,'\n')) != 0)
+ *cp = '\0';
+ cp = skip(line);
+ if (cp == NULL)
+ continue;
+ tp = index(cp, ':');
+ if (tp == NULL) {
+ fprintf(stderr, "line %d: syntax error\n", lineno);
+ errors++;
+ continue;
+ }
+ *tp++ = '\0', tp = skip(tp);
+ if (!strcmp(cp, "type")) {
+ if (tp == NULL)
+ tp = unknown;
+ cpp = dktypenames;
+ for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
+ if (*cpp && !strcmp(*cpp, tp)) {
+ lp->d_type = cpp - dktypenames;
+ break;
+ }
+ if (cpp < &dktypenames[DKMAXTYPES])
+ continue;
+ v = strtoul(tp, NULL, 10);
+ if (v >= DKMAXTYPES)
+ fprintf(stderr, "line %d:%s %lu\n", lineno,
+ "Warning, unknown disk type", v);
+ lp->d_type = v;
+ continue;
+ }
+ if (!strcmp(cp, "flags")) {
+ for (v = 0; (cp = tp) && *cp != '\0';) {
+ tp = word(cp);
+ if (!strcmp(cp, "removeable"))
+ v |= D_REMOVABLE;
+ else if (!strcmp(cp, "ecc"))
+ v |= D_ECC;
+ else if (!strcmp(cp, "badsect"))
+ v |= D_BADSECT;
+ else {
+ fprintf(stderr,
+ "line %d: %s: bad flag\n",
+ lineno, cp);
+ errors++;
+ }
+ }
+ lp->d_flags = v;
+ continue;
+ }
+ if (!strcmp(cp, "drivedata")) {
+ for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
+ lp->d_drivedata[i++] = strtoul(cp, NULL, 10);
+ tp = word(cp);
+ }
+ continue;
+ }
+ if (sscanf(cp, "%lu partitions", &v) == 1) {
+ if (v == 0 || v > MAXPARTITIONS) {
+ fprintf(stderr,
+ "line %d: bad # of partitions\n", lineno);
+ lp->d_npartitions = MAXPARTITIONS;
+ errors++;
+ } else
+ lp->d_npartitions = v;
+ continue;
+ }
+ if (tp == NULL)
+ tp = blank;
+ if (!strcmp(cp, "disk")) {
+ strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
+ continue;
+ }
+ if (!strcmp(cp, "label")) {
+ strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
+ continue;
+ }
+ if (!strcmp(cp, "bytes/sector")) {
+ v = strtoul(tp, NULL, 10);
+ if (v == 0 || (v % DEV_BSIZE) != 0) {
+ fprintf(stderr,
+ "line %d: %s: bad sector size\n",
+ lineno, tp);
+ errors++;
+ } else
+ lp->d_secsize = v;
+ continue;
+ }
+ if (!strcmp(cp, "sectors/track")) {
+ v = strtoul(tp, NULL, 10);
+#if (ULONG_MAX != 0xffffffffUL)
+ if (v == 0 || v > 0xffffffff)
+#else
+ if (v == 0)
+#endif
+ {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_nsectors = v;
+ continue;
+ }
+ if (!strcmp(cp, "sectors/cylinder")) {
+ v = strtoul(tp, NULL, 10);
+ if (v == 0) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_secpercyl = v;
+ continue;
+ }
+ if (!strcmp(cp, "tracks/cylinder")) {
+ v = strtoul(tp, NULL, 10);
+ if (v == 0) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_ntracks = v;
+ continue;
+ }
+ if (!strcmp(cp, "cylinders")) {
+ v = strtoul(tp, NULL, 10);
+ if (v == 0) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_ncylinders = v;
+ continue;
+ }
+ if (!strcmp(cp, "sectors/unit")) {
+ v = strtoul(tp, NULL, 10);
+ if (v == 0) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_secperunit = v;
+ continue;
+ }
+ if (!strcmp(cp, "rpm")) {
+ v = strtoul(tp, NULL, 10);
+ if (v == 0 || v > USHRT_MAX) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_rpm = v;
+ continue;
+ }
+ if (!strcmp(cp, "interleave")) {
+ v = strtoul(tp, NULL, 10);
+ if (v == 0 || v > USHRT_MAX) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_interleave = v;
+ continue;
+ }
+ if (!strcmp(cp, "trackskew")) {
+ v = strtoul(tp, NULL, 10);
+ if (v > USHRT_MAX) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_trackskew = v;
+ continue;
+ }
+ if (!strcmp(cp, "cylinderskew")) {
+ v = strtoul(tp, NULL, 10);
+ if (v > USHRT_MAX) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_cylskew = v;
+ continue;
+ }
+ if (!strcmp(cp, "headswitch")) {
+ v = strtoul(tp, NULL, 10);
+ lp->d_headswitch = v;
+ continue;
+ }
+ if (!strcmp(cp, "track-to-track seek")) {
+ v = strtoul(tp, NULL, 10);
+ lp->d_trkseek = v;
+ continue;
+ }
+ /* the ':' was removed above */
+ if (*cp < 'a' || *cp > MAX_PART || cp[1] != '\0') {
+ fprintf(stderr,
+ "line %d: %s: Unknown disklabel field\n", lineno,
+ cp);
+ errors++;
+ continue;
+ }
+
+ /* Process a partition specification line. */
+ part = *cp - 'a';
+ if (part >= lp->d_npartitions) {
+ fprintf(stderr,
+ "line %d: partition name out of range a-%c: %s\n",
+ lineno, 'a' + lp->d_npartitions - 1, cp);
+ errors++;
+ continue;
+ }
+ part_set[part] = 1;
+
+ if (getasciipartspec(tp, lp, part, lineno) != 0) {
+ errors++;
+ break;
+ }
+ }
+ errors += checklabel(lp);
+ return (errors == 0);
+}
+
+#define NXTNUM(n) do { \
+ if (tp == NULL) { \
+ fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
+ return (1); \
+ } else { \
+ cp = tp, tp = word(cp); \
+ (n) = strtoul(cp, NULL, 10); \
+ } \
+} while (0)
+
+/* retain 1 character following number */
+#define NXTWORD(w,n) do { \
+ if (tp == NULL) { \
+ fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
+ return (1); \
+ } else { \
+ char *tmp; \
+ cp = tp, tp = word(cp); \
+ (n) = strtoul(cp, &tmp, 10); \
+ if (tmp) (w) = *tmp; \
+ } \
+} while (0)
+
+/*
+ * Read a partition line into partition `part' in the specified disklabel.
+ * Return 0 on success, 1 on failure.
+ */
+static int
+getasciipartspec(char *tp, struct disklabel *lp, int part, int lineno)
+{
+ struct partition *pp;
+ char *cp;
+ const char **cpp;
+ u_long v;
+
+ pp = &lp->d_partitions[part];
+ cp = NULL;
+
+ v = 0;
+ NXTWORD(part_size_type[part],v);
+ if (v == 0 && part_size_type[part] != '*') {
+ fprintf(stderr,
+ "line %d: %s: bad partition size\n", lineno, cp);
+ return (1);
+ }
+ pp->p_size = v;
+
+ v = 0;
+ NXTWORD(part_offset_type[part],v);
+ if (v == 0 && part_offset_type[part] != '*' &&
+ part_offset_type[part] != '\0') {
+ fprintf(stderr,
+ "line %d: %s: bad partition offset\n", lineno, cp);
+ return (1);
+ }
+ pp->p_offset = v;
+ if (tp == NULL) {
+ fprintf(stderr, "line %d: missing file system type\n", lineno);
+ return (1);
+ }
+ cp = tp, tp = word(cp);
+ for (cpp = fstypenames; cpp < &fstypenames[FSMAXTYPES]; cpp++)
+ if (*cpp && !strcmp(*cpp, cp))
+ break;
+ if (*cpp != NULL) {
+ pp->p_fstype = cpp - fstypenames;
+ } else {
+ if (isdigit(*cp))
+ v = strtoul(cp, NULL, 10);
+ else
+ v = FSMAXTYPES;
+ if (v >= FSMAXTYPES) {
+ fprintf(stderr,
+ "line %d: Warning, unknown file system type %s\n",
+ lineno, cp);
+ v = FS_UNUSED;
+ }
+ pp->p_fstype = v;
+ }
+
+ switch (pp->p_fstype) {
+ case FS_UNUSED:
+ case FS_BSDFFS:
+ case FS_BSDLFS:
+ /* accept defaults for fsize/frag/cpg */
+ if (tp) {
+ NXTNUM(pp->p_fsize);
+ if (pp->p_fsize == 0)
+ break;
+ NXTNUM(v);
+ pp->p_frag = v / pp->p_fsize;
+ if (tp != NULL)
+ NXTNUM(pp->p_cpg);
+ }
+ /* else default to 0's */
+ break;
+ default:
+ break;
+ }
+ return (0);
+}
+
+/*
+ * Check disklabel for errors and fill in
+ * derived fields according to supplied values.
+ */
+static int
+checklabel(struct disklabel *lp)
+{
+ struct partition *pp;
+ int i, errors = 0;
+ char part;
+ u_long base_offset, needed, total_size, total_percent, current_offset;
+ long free_space;
+ int seen_default_offset;
+ int hog_part;
+ int j;
+ struct partition *pp2;
+
+ if (lp == NULL)
+ lp = &lab;
+
+ if (allfields) {
+
+ if (lp->d_secsize == 0) {
+ fprintf(stderr, "sector size 0\n");
+ return (1);
+ }
+ if (lp->d_nsectors == 0) {
+ fprintf(stderr, "sectors/track 0\n");
+ return (1);
+ }
+ if (lp->d_ntracks == 0) {
+ fprintf(stderr, "tracks/cylinder 0\n");
+ return (1);
+ }
+ if (lp->d_ncylinders == 0) {
+ fprintf(stderr, "cylinders/unit 0\n");
+ errors++;
+ }
+ if (lp->d_rpm == 0)
+ warnx("revolutions/minute 0");
+ if (lp->d_secpercyl == 0)
+ lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
+ if (lp->d_secperunit == 0)
+ lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
+ if (lp->d_bbsize == 0) {
+ fprintf(stderr, "boot block size 0\n");
+ errors++;
+ } else if (lp->d_bbsize % lp->d_secsize)
+ warnx("boot block size %% sector-size != 0");
+ if (lp->d_npartitions > MAXPARTITIONS)
+ warnx("number of partitions (%lu) > MAXPARTITIONS (%d)",
+ (u_long)lp->d_npartitions, MAXPARTITIONS);
+ } else {
+ struct disklabel *vl;
+
+ vl = getvirginlabel();
+ lp->d_secsize = vl->d_secsize;
+ lp->d_nsectors = vl->d_nsectors;
+ lp->d_ntracks = vl->d_ntracks;
+ lp->d_ncylinders = vl->d_ncylinders;
+ lp->d_rpm = vl->d_rpm;
+ lp->d_interleave = vl->d_interleave;
+ lp->d_secpercyl = vl->d_secpercyl;
+ lp->d_secperunit = vl->d_secperunit;
+ lp->d_bbsize = vl->d_bbsize;
+ lp->d_npartitions = vl->d_npartitions;
+ }
+
+
+ /* first allocate space to the partitions, then offsets */
+ total_size = 0; /* in sectors */
+ total_percent = 0; /* in percent */
+ hog_part = -1;
+ /* find all fixed partitions */
+ for (i = 0; i < lp->d_npartitions; i++) {
+ pp = &lp->d_partitions[i];
+ if (part_set[i]) {
+ if (part_size_type[i] == '*') {
+ if (i == RAW_PART) {
+ pp->p_size = lp->d_secperunit;
+ } else {
+ if (hog_part != -1)
+ warnx("Too many '*' partitions (%c and %c)",
+ hog_part + 'a',i + 'a');
+ else
+ hog_part = i;
+ }
+ } else {
+ off_t size;
+
+ size = pp->p_size;
+ switch (part_size_type[i]) {
+ case '%':
+ total_percent += size;
+ break;
+ case 't':
+ case 'T':
+ size *= 1024ULL;
+ /* FALLTHROUGH */
+ case 'g':
+ case 'G':
+ size *= 1024ULL;
+ /* FALLTHROUGH */
+ case 'm':
+ case 'M':
+ size *= 1024ULL;
+ /* FALLTHROUGH */
+ case 'k':
+ case 'K':
+ size *= 1024ULL;
+ break;
+ case '\0':
+ break;
+ default:
+ warnx("unknown multiplier suffix '%c' for partition %c (should be K, M, G or T)",
+ part_size_type[i], i + 'a');
+ break;
+ }
+ /* don't count %'s yet */
+ if (part_size_type[i] != '%') {
+ /*
+ * for all not in sectors, convert to
+ * sectors
+ */
+ if (part_size_type[i] != '\0') {
+ if (size % lp->d_secsize != 0)
+ warnx("partition %c not an integer number of sectors",
+ i + 'a');
+ size /= lp->d_secsize;
+ pp->p_size = size;
+ }
+ /* else already in sectors */
+ if (i != RAW_PART)
+ total_size += size;
+ }
+ }
+ }
+ }
+
+ /* Find out the total free space, excluding the boot block area. */
+ base_offset = BBSIZE / secsize;
+ free_space = 0;
+ for (i = 0; i < lp->d_npartitions; i++) {
+ pp = &lp->d_partitions[i];
+ if (!part_set[i] || i == RAW_PART ||
+ part_size_type[i] == '%' || part_size_type[i] == '*')
+ continue;
+ if (pp->p_offset > base_offset)
+ free_space += pp->p_offset - base_offset;
+ if (pp->p_offset + pp->p_size > base_offset)
+ base_offset = pp->p_offset + pp->p_size;
+ }
+ if (base_offset < lp->d_secperunit)
+ free_space += lp->d_secperunit - base_offset;
+
+ /* handle % partitions - note %'s don't need to add up to 100! */
+ if (total_percent != 0) {
+ if (total_percent > 100) {
+ fprintf(stderr,"total percentage %lu is greater than 100\n",
+ total_percent);
+ errors++;
+ }
+
+ if (free_space > 0) {
+ for (i = 0; i < lp->d_npartitions; i++) {
+ pp = &lp->d_partitions[i];
+ if (part_set[i] && part_size_type[i] == '%') {
+ /* careful of overflows! and integer roundoff */
+ pp->p_size = ((double)pp->p_size/100) * free_space;
+ total_size += pp->p_size;
+
+ /* FIX we can lose a sector or so due to roundoff per
+ partition. A more complex algorithm could avoid that */
+ }
+ }
+ } else {
+ fprintf(stderr,
+ "%ld sectors available to give to '*' and '%%' partitions\n",
+ free_space);
+ errors++;
+ /* fix? set all % partitions to size 0? */
+ }
+ }
+ /* give anything remaining to the hog partition */
+ if (hog_part != -1) {
+ /*
+ * Find the range of offsets usable by '*' partitions around
+ * the hog partition and how much space they need.
+ */
+ needed = 0;
+ base_offset = BBSIZE / secsize;
+ for (i = hog_part - 1; i >= 0; i--) {
+ pp = &lp->d_partitions[i];
+ if (!part_set[i] || i == RAW_PART)
+ continue;
+ if (part_offset_type[i] == '*') {
+ needed += pp->p_size;
+ continue;
+ }
+ base_offset = pp->p_offset + pp->p_size;
+ break;
+ }
+ current_offset = lp->d_secperunit;
+ for (i = lp->d_npartitions - 1; i > hog_part; i--) {
+ pp = &lp->d_partitions[i];
+ if (!part_set[i] || i == RAW_PART)
+ continue;
+ if (part_offset_type[i] == '*') {
+ needed += pp->p_size;
+ continue;
+ }
+ current_offset = pp->p_offset;
+ }
+
+ if (current_offset - base_offset <= needed) {
+ fprintf(stderr, "Cannot find space for partition %c\n",
+ hog_part + 'a');
+ fprintf(stderr,
+ "Need more than %lu sectors between %lu and %lu\n",
+ needed, base_offset, current_offset);
+ errors++;
+ lp->d_partitions[hog_part].p_size = 0;
+ } else {
+ lp->d_partitions[hog_part].p_size = current_offset -
+ base_offset - needed;
+ total_size += lp->d_partitions[hog_part].p_size;
+ }
+ }
+
+ /* Now set the offsets for each partition */
+ current_offset = BBSIZE / secsize; /* in sectors */
+ seen_default_offset = 0;
+ for (i = 0; i < lp->d_npartitions; i++) {
+ part = 'a' + i;
+ pp = &lp->d_partitions[i];
+ if (part_set[i]) {
+ if (part_offset_type[i] == '*') {
+ if (i == RAW_PART) {
+ pp->p_offset = 0;
+ } else {
+ pp->p_offset = current_offset;
+ seen_default_offset = 1;
+ }
+ } else {
+ /* allow them to be out of order for old-style tables */
+ if (pp->p_offset < current_offset &&
+ seen_default_offset && i != RAW_PART &&
+ pp->p_fstype != FS_VINUM) {
+ fprintf(stderr,
+"Offset %ld for partition %c overlaps previous partition which ends at %lu\n",
+ (long)pp->p_offset,i+'a',current_offset);
+ fprintf(stderr,
+"Labels with any *'s for offset must be in ascending order by sector\n");
+ errors++;
+ } else if (pp->p_offset != current_offset &&
+ i != RAW_PART && seen_default_offset) {
+ /*
+ * this may give unneeded warnings if
+ * partitions are out-of-order
+ */
+ warnx(
+"Offset %ld for partition %c doesn't match expected value %ld",
+ (long)pp->p_offset, i + 'a', current_offset);
+ }
+ }
+ if (i != RAW_PART)
+ current_offset = pp->p_offset + pp->p_size;
+ }
+ }
+
+ for (i = 0; i < lp->d_npartitions; i++) {
+ part = 'a' + i;
+ pp = &lp->d_partitions[i];
+ if (pp->p_size == 0 && pp->p_offset != 0)
+ warnx("partition %c: size 0, but offset %lu",
+ part, (u_long)pp->p_offset);
+#ifdef notdef
+ if (pp->p_size % lp->d_secpercyl)
+ warnx("partition %c: size %% cylinder-size != 0",
+ part);
+ if (pp->p_offset % lp->d_secpercyl)
+ warnx("partition %c: offset %% cylinder-size != 0",
+ part);
+#endif
+ if (pp->p_offset > lp->d_secperunit) {
+ fprintf(stderr,
+ "partition %c: offset past end of unit\n", part);
+ errors++;
+ }
+ if (pp->p_offset + pp->p_size > lp->d_secperunit) {
+ fprintf(stderr,
+ "partition %c: partition extends past end of unit\n",
+ part);
+ errors++;
+ }
+ if (i == RAW_PART) {
+ if (pp->p_fstype != FS_UNUSED)
+ warnx("partition %c is not marked as unused!",part);
+ if (pp->p_offset != 0)
+ warnx("partition %c doesn't start at 0!",part);
+ if (pp->p_size != lp->d_secperunit)
+ warnx("partition %c doesn't cover the whole unit!",part);
+
+ if ((pp->p_fstype != FS_UNUSED) || (pp->p_offset != 0) ||
+ (pp->p_size != lp->d_secperunit)) {
+ warnx("An incorrect partition %c may cause problems for "
+ "standard system utilities",part);
+ }
+ }
+
+ /* check for overlaps */
+ /* this will check for all possible overlaps once and only once */
+ for (j = 0; j < i; j++) {
+ pp2 = &lp->d_partitions[j];
+ if (j != RAW_PART && i != RAW_PART &&
+ pp->p_fstype != FS_VINUM &&
+ pp2->p_fstype != FS_VINUM &&
+ part_set[i] && part_set[j]) {
+ if (pp2->p_offset < pp->p_offset + pp->p_size &&
+ (pp2->p_offset + pp2->p_size > pp->p_offset ||
+ pp2->p_offset >= pp->p_offset)) {
+ fprintf(stderr,"partitions %c and %c overlap!\n",
+ j + 'a', i + 'a');
+ errors++;
+ }
+ }
+ }
+ }
+ for (; i < MAXPARTITIONS; i++) {
+ part = 'a' + i;
+ pp = &lp->d_partitions[i];
+ if (pp->p_size || pp->p_offset)
+ warnx("unused partition %c: size %d offset %lu",
+ 'a' + i, pp->p_size, (u_long)pp->p_offset);
+ }
+ return (errors);
+}
+
+/*
+ * When operating on a "virgin" disk, try getting an initial label
+ * from the associated device driver. This might work for all device
+ * drivers that are able to fetch some initial device parameters
+ * without even having access to a (BSD) disklabel, like SCSI disks,
+ * most IDE drives, or vn devices.
+ *
+ * The device name must be given in its "canonical" form.
+ */
+static struct disklabel *
+getvirginlabel(void)
+{
+ static struct disklabel loclab;
+ struct partition *dp;
+ int f;
+ u_int u;
+
+ if ((f = open(specname, O_RDONLY)) == -1) {
+ warn("cannot open %s", specname);
+ return (NULL);
+ }
+
+ if (is_file)
+ get_file_parms(f);
+ else if ((ioctl(f, DIOCGMEDIASIZE, &mediasize) != 0) ||
+ (ioctl(f, DIOCGSECTORSIZE, &secsize) != 0)) {
+ close (f);
+ return (NULL);
+ }
+ memset(&loclab, 0, sizeof loclab);
+ loclab.d_magic = DISKMAGIC;
+ loclab.d_magic2 = DISKMAGIC;
+ loclab.d_secsize = secsize;
+ loclab.d_secperunit = mediasize / secsize;
+
+ /*
+ * Nobody in these enligthened days uses the CHS geometry for
+ * anything, but nontheless try to get it right. If we fail
+ * to get any good ideas from the device, construct something
+ * which is IBM-PC friendly.
+ */
+ if (ioctl(f, DIOCGFWSECTORS, &u) == 0)
+ loclab.d_nsectors = u;
+ else
+ loclab.d_nsectors = 63;
+ if (ioctl(f, DIOCGFWHEADS, &u) == 0)
+ loclab.d_ntracks = u;
+ else if (loclab.d_secperunit <= 63*1*1024)
+ loclab.d_ntracks = 1;
+ else if (loclab.d_secperunit <= 63*16*1024)
+ loclab.d_ntracks = 16;
+ else
+ loclab.d_ntracks = 255;
+ loclab.d_secpercyl = loclab.d_ntracks * loclab.d_nsectors;
+ loclab.d_ncylinders = loclab.d_secperunit / loclab.d_secpercyl;
+ loclab.d_npartitions = MAXPARTITIONS;
+
+ /* Various (unneeded) compat stuff */
+ loclab.d_rpm = 3600;
+ loclab.d_bbsize = BBSIZE;
+ loclab.d_interleave = 1;
+ strncpy(loclab.d_typename, "amnesiac",
+ sizeof(loclab.d_typename));
+
+ dp = &loclab.d_partitions[RAW_PART];
+ dp->p_size = loclab.d_secperunit;
+ loclab.d_checksum = dkcksum(&loclab);
+ close (f);
+ return (&loclab);
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr,
+ "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+ "usage: bsdlabel disk",
+ "\t\t(to read label)",
+ " bsdlabel -w [-n] [-m machine] disk [type]",
+ "\t\t(to write label with existing boot program)",
+ " bsdlabel -e [-n] [-m machine] disk",
+ "\t\t(to edit label)",
+ " bsdlabel -R [-n] [-m machine] disk protofile",
+ "\t\t(to restore label with existing boot program)",
+ " bsdlabel -B [-b boot] [-m machine] disk",
+ "\t\t(to install boot program with existing on-disk label)",
+ " bsdlabel -w -B [-n] [-b boot] [-m machine] disk [type]",
+ "\t\t(to write label and install boot program)",
+ " bsdlabel -R -B [-n] [-b boot] [-m machine] disk protofile",
+ "\t\t(to restore label and install boot program)"
+ );
+ exit(1);
+}
diff --git a/sbin/bsdlabel/pathnames.h b/sbin/bsdlabel/pathnames.h
new file mode 100644
index 0000000..c8a9ef3
--- /dev/null
+++ b/sbin/bsdlabel/pathnames.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ * 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 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.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/5/93
+ * $FreeBSD$
+ */
+
+#include <paths.h>
+
+#define _PATH_BOOTDIR "/boot"
+#define PATH_TMPFILE "/tmp/EdDk.XXXXXXXXXX"
diff --git a/sbin/bsdlabel/runtest.sh b/sbin/bsdlabel/runtest.sh
new file mode 100644
index 0000000..9d561f3
--- /dev/null
+++ b/sbin/bsdlabel/runtest.sh
@@ -0,0 +1,175 @@
+#!/bin/sh
+# $FreeBSD$
+
+TMP=/tmp/$$.
+set -e
+for TEST in "i386 512" "i386 4096" "alpha 512"
+do
+ set $TEST
+ ARCH=$1
+ SEC=$2
+ echo "ARCH $ARCH SEC $SEC"
+ MD=`mdconfig -a -t malloc -s 2m -S $SEC`
+ trap "exec 7</dev/null; rm -f ${TMP}* ; mdconfig -d -u ${MD}" EXIT INT TERM
+
+ ./bsdlabel -m ${ARCH} -r -w $MD auto
+
+ dd if=/dev/$MD of=${TMP}i0 count=1 bs=8k > /dev/null 2>&1
+ if [ "$ARCH" = "alpha" ] ; then
+ dd if=${TMP}i0 of=${TMP}b0 iseek=1 count=15 > /dev/null 2>&1
+ else
+ cp ${TMP}i0 ${TMP}b0
+ fi
+ ./bsdlabel -m ${ARCH} $MD > ${TMP}l0
+
+ sed '
+ / c:/{
+ p
+ s/c:/a:/
+ s/4096/1024/
+ s/512/64/
+ }
+ ' ${TMP}l0 > ${TMP}l1
+
+ ./bsdlabel -m ${ARCH} -R $MD ${TMP}l1
+ if [ -c /dev/${MD}a ] ; then
+ echo "PASS: Created a: partition" 1>&2
+ else
+ echo "FAIL: Did not create a: partition" 1>&2
+ exit 2
+ fi
+
+ # Spoil and rediscover
+
+ true > /dev/${MD}
+ if [ -c /dev/${MD}a ] ; then
+ echo "PASS: Recreated a: partition after spoilage" 1>&2
+ else
+ echo "FAIL: Did not recreate a: partition after spoilage" 1>&2
+ exit 2
+ fi
+
+ dd if=/dev/$MD of=${TMP}i1 count=1 bs=8k > /dev/null 2>&1
+ sed '
+ / c:/{
+ p
+ s/c:/a:/
+ s/4096/2048/
+ s/512/256/
+ }
+ ' ${TMP}l0 > ${TMP}l2
+
+ ./bsdlabel -m ${ARCH} -R $MD ${TMP}l2
+ dd if=/dev/$MD of=${TMP}i2 count=1 bs=8k > /dev/null 2>&1
+
+ exec 7< /dev/${MD}a
+
+ for t in a c
+ do
+ if dd if=${TMP}i2 of=/dev/${MD}$t bs=8k 2>/dev/null ; then
+ echo "PASS: Could rewrite same label to ...$t while ...a open" 1>&2
+ else
+ echo "FAIL: Could not rewrite same label to ...$t while ...a open" 1>&2
+ exit 2
+ fi
+
+ if dd if=${TMP}i1 of=/dev/${MD}$t bs=8k 2>/dev/null ; then
+ echo "FAIL: Could label with smaller ...a to ...$t while ...a open" 1>&2
+ exit 2
+ else
+ echo "PASS: Could not label with smaller ...a to ...$t while ...a open" 1>&2
+ fi
+
+ if dd if=${TMP}i0 of=/dev/${MD}$t 2>/dev/null ; then
+ echo "FAIL: Could write label missing ...a to ...$t while ...a open" 1>&2
+ exit 2
+ else
+ echo "PASS: Could not write label missing ...a to ...$t while ...a open" 1>&2
+ fi
+ done
+
+ exec 7< /dev/null
+
+ if dd if=${TMP}i0 of=/dev/${MD}c bs=8k 2>/dev/null ; then
+ echo "PASS: Could write missing ...a label to ...c" 1>&2
+ else
+ echo "FAIL: Could not write missing ...a label to ...c" 1>&2
+ exit 2
+ fi
+
+ if dd if=${TMP}i2 of=/dev/${MD}c bs=8k 2>/dev/null ; then
+ echo "PASS: Could write large ...a label to ...c" 1>&2
+ else
+ echo "FAIL: Could not write large ...a label to ...c" 1>&2
+ exit 2
+ fi
+
+ if dd if=${TMP}i1 of=/dev/${MD}c bs=8k 2>/dev/null ; then
+ echo "PASS: Could write small ...a label to ...c" 1>&2
+ else
+ echo "FAIL: Could not write small ...a label to ...c" 1>&2
+ exit 2
+ fi
+
+ if dd if=${TMP}i2 of=/dev/${MD}a bs=8k 2>/dev/null ; then
+ echo "PASS: Could increase size of ...a by writing to ...a" 1>&2
+ else
+ echo "FAIL: Could not increase size of ...a by writing to ...a" 1>&2
+ exit 2
+ fi
+
+ if dd if=${TMP}i1 of=/dev/${MD}a bs=8k 2>/dev/null ; then
+ echo "FAIL: Could decrease size of ...a by writing to ...a" 1>&2
+ exit 2
+ else
+ echo "PASS: Could not decrease size of ...a by writing to ...a" 1>&2
+ fi
+
+ if dd if=${TMP}i0 of=/dev/${MD}a bs=8k 2>/dev/null ; then
+ echo "FAIL: Could delete ...a by writing to ...a" 1>&2
+ exit 2
+ else
+ echo "PASS: Could not delete ...a by writing to ...a" 1>&2
+ fi
+
+ if ./bsdlabel -m ${ARCH} -B -b ${TMP}b0 ${MD} ; then
+ if [ ! -c /dev/${MD}a ] ; then
+ echo "FAILED: Writing bootcode killed ...a" 1>&2
+ exit 2
+ else
+ echo "PASS: Could write bootcode while closed" 1>&2
+ fi
+ else
+ echo "FAILED: Could not write bootcode while closed" 1>&2
+ exit 2
+ fi
+
+ exec 7> /dev/${MD}c
+ if ./bsdlabel -m ${ARCH} -B -b ${TMP}b0 ${MD} ; then
+ if [ ! -c /dev/${MD}a ] ; then
+ echo "FAILED: Writing bootcode killed ...a" 1>&2
+ exit 2
+ else
+ echo "PASS: Could write bootcode while open" 1>&2
+ fi
+ else
+ echo "FAILED: Could not write bootcode while open" 1>&2
+ exit 2
+ fi
+ exec 7> /dev/null
+
+ if dd if=${TMP}i0 of=/dev/${MD}c bs=8k 2>/dev/null ; then
+ echo "PASS: Could delete ...a by writing to ...c" 1>&2
+ else
+ echo "FAIL: Could not delete ...a by writing to ...c" 1>&2
+ exit 2
+ fi
+
+ # XXX: need to add a 'b' partition and check for overlaps.
+
+ rm -f ${TMP}*
+ mdconfig -d -u ${MD}
+
+done
+trap "" EXIT INT TERM
+exit 0
diff --git a/sbin/camcontrol/Makefile b/sbin/camcontrol/Makefile
new file mode 100644
index 0000000..bf2ba4a
--- /dev/null
+++ b/sbin/camcontrol/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+PROG= camcontrol
+SRCS= camcontrol.c util.c
+.if !defined(RELEASE_CRUNCH)
+SRCS+= modeedit.c
+.else
+CFLAGS+= -DMINIMALISTIC
+.endif
+.if ${MACHINE_ARCH} == "arm"
+WARNS?= 3
+.else
+WARNS?= 6
+.endif
+DPADD= ${LIBCAM} ${LIBSBUF}
+LDADD= -lcam -lsbuf
+MAN= camcontrol.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8
new file mode 100644
index 0000000..3923d5a
--- /dev/null
+++ b/sbin/camcontrol/camcontrol.8
@@ -0,0 +1,885 @@
+.\"
+.\" Copyright (c) 1998, 1999, 2000, 2002, 2005 Kenneth D. Merry.
+.\" 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 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 14, 1998
+.Dt CAMCONTROL 8
+.Os
+.Sh NAME
+.Nm camcontrol
+.Nd CAM control program
+.Sh SYNOPSIS
+.Nm
+.Aq Ar command
+.Op device id
+.Op generic args
+.Op command args
+.Nm
+.Ic devlist
+.Op Fl v
+.Nm
+.Ic periphlist
+.Op device id
+.Op Fl n Ar dev_name
+.Op Fl u Ar unit_number
+.Nm
+.Ic tur
+.Op device id
+.Op generic args
+.Nm
+.Ic inquiry
+.Op device id
+.Op generic args
+.Op Fl D
+.Op Fl S
+.Op Fl R
+.Nm
+.Ic start
+.Op device id
+.Op generic args
+.Nm
+.Ic stop
+.Op device id
+.Op generic args
+.Nm
+.Ic load
+.Op device id
+.Op generic args
+.Nm
+.Ic eject
+.Op device id
+.Op generic args
+.Nm
+.Ic rescan
+.Aq all | bus Ns Op :target:lun
+.Nm
+.Ic reset
+.Aq all | bus Ns Op :target:lun
+.Nm
+.Ic defects
+.Op device id
+.Op generic args
+.Aq Fl f Ar format
+.Op Fl P
+.Op Fl G
+.Nm
+.Ic modepage
+.Op device id
+.Op generic args
+.Aq Fl m Ar page | Fl l
+.Op Fl P Ar pgctl
+.Op Fl b | Fl e
+.Op Fl d
+.Nm
+.Ic cmd
+.Op device id
+.Op generic args
+.Aq Fl c Ar cmd Op args
+.Op Fl i Ar len Ar fmt
+.Bk -words
+.Op Fl o Ar len Ar fmt Op args
+.Ek
+.Nm
+.Ic debug
+.Op Fl I
+.Op Fl P
+.Op Fl T
+.Op Fl S
+.Op Fl X
+.Op Fl c
+.Aq all|off|bus Ns Op :target Ns Op :lun
+.Nm
+.Ic tags
+.Op device id
+.Op generic args
+.Op Fl N Ar tags
+.Op Fl q
+.Op Fl v
+.Nm
+.Ic negotiate
+.Op device id
+.Op generic args
+.Op Fl c
+.Op Fl D Ar enable|disable
+.Op Fl O Ar offset
+.Op Fl q
+.Op Fl R Ar syncrate
+.Op Fl T Ar enable|disable
+.Op Fl U
+.Op Fl W Ar bus_width
+.Op Fl v
+.Nm
+.Ic format
+.Op device id
+.Op generic args
+.Op Fl q
+.Op Fl r
+.Op Fl w
+.Op Fl y
+.Nm
+.Ic help
+.Sh DESCRIPTION
+The
+.Nm
+utility is designed to provide a way for users to access and control the
+.Fx
+CAM subsystem.
+.Pp
+The
+.Nm
+utility
+can cause a loss of data and/or system crashes if used improperly.
+Even
+expert users are encouraged to exercise caution when using this command.
+Novice users should stay away from this utility.
+.Pp
+The
+.Nm
+utility has a number of primary functions, many of which support an optional
+device identifier.
+A device identifier can take one of three forms:
+.Bl -tag -width 14n
+.It deviceUNIT
+Specify a device name and unit number combination, like "da5" or "cd3".
+Note that character device node names (e.g.\& /dev/da0) are
+.Em not
+allowed here.
+.It bus:target
+Specify a bus number and target id.
+The bus number can be determined from
+the output of
+.Dq camcontrol devlist .
+The lun defaults to 0.
+.It bus:target:lun
+Specify the bus, target and lun for a device.
+(e.g.\& 1:2:0)
+.El
+.Pp
+The device identifier, if it is specified,
+.Em must
+come immediately after the function name, and before any generic or
+function-specific arguments.
+Note that the
+.Fl n
+and
+.Fl u
+arguments described below will override any device name or unit number
+specified beforehand.
+The
+.Fl n
+and
+.Fl u
+arguments will
+.Em not
+override a specified bus:target or bus:target:lun, however.
+.Pp
+Most of the
+.Nm
+primary functions support these generic arguments:
+.Bl -tag -width 14n
+.It Fl C Ar count
+SCSI command retry count.
+In order for this to work, error recovery
+.Pq Fl E
+must be turned on.
+.It Fl E
+Instruct the kernel to perform generic SCSI error recovery for the given
+command.
+This is needed in order for the retry count
+.Pq Fl C
+to be honored.
+Other than retrying commands, the generic error recovery in
+the code will generally attempt to spin up drives that are not spinning.
+It may take some other actions, depending upon the sense code returned from
+the command.
+.It Fl n Ar dev_name
+Specify the device type to operate on, e.g.\& "da", "cd".
+.It Fl t Ar timeout
+SCSI command timeout in seconds.
+This overrides the default timeout for
+any given command.
+.It Fl u Ar unit_number
+Specify the device unit number, e.g.\& "1", "5".
+.It Fl v
+Be verbose, print out sense information for failed SCSI commands.
+.El
+.Pp
+Primary command functions:
+.Bl -tag -width periphlist
+.It Ic devlist
+List all physical devices (logical units) attached to the CAM subsystem.
+This also includes a list of peripheral drivers attached to each device.
+With the
+.Fl v
+argument, SCSI bus number, adapter name and unit numbers are printed as
+well.
+.It Ic periphlist
+List all peripheral drivers attached to a given physical device (logical
+unit).
+.It Ic tur
+Send the SCSI test unit ready (0x00) command to the given device.
+The
+.Nm
+utility will report whether the device is ready or not.
+.It Ic inquiry
+Send a SCSI inquiry command (0x12) to a device.
+By default,
+.Nm
+will print out the standard inquiry data, device serial number, and
+transfer rate information.
+The user can specify that only certain types of
+inquiry data be printed:
+.Bl -tag -width 4n
+.It Fl D
+Get the standard inquiry data.
+.It Fl S
+Print out the serial number.
+If this flag is the only one specified,
+.Nm
+will not print out "Serial Number" before the value returned by the drive.
+This is to aid in script writing.
+.It Fl R
+Print out transfer rate information.
+.El
+.It Ic start
+Send the SCSI Start/Stop Unit (0x1B) command to the given device with the
+start bit set.
+.It Ic stop
+Send the SCSI Start/Stop Unit (0x1B) command to the given device with the
+start bit cleared.
+.It Ic load
+Send the SCSI Start/Stop Unit (0x1B) command to the given device with the
+start bit set and the load/eject bit set.
+.It Ic eject
+Send the SCSI Start/Stop Unit (0x1B) command to the given device with the
+start bit cleared and the load/eject bit set.
+.It Ic rescan
+Tell the kernel to scan all busses in the system (with the
+.Ar all
+argument), the given bus (XPT_SCAN_BUS), or bus:target:lun
+(XPT_SCAN_LUN) for new devices or devices that have gone away.
+The user
+may specify a scan of all busses, a single bus, or a lun.
+Scanning all luns
+on a target is not supported.
+.It Ic reset
+Tell the kernel to reset all busses in the system (with the
+.Ar all
+argument) or the given bus (XPT_RESET_BUS) by issuing a SCSI bus
+reset for that bus, or to reset the given bus:target:lun
+(XPT_RESET_DEV), typically by issuing a BUS DEVICE RESET message after
+connecting to that device.
+Note that this can have a destructive impact
+on the system.
+.It Ic defects
+Send the SCSI READ DEFECT DATA (10) command (0x37) to the given device, and
+print out any combination of: the total number of defects, the primary
+defect list (PLIST), and the grown defect list (GLIST).
+.Bl -tag -width 11n
+.It Fl f Ar format
+The three format options are:
+.Em block ,
+to print out the list as logical blocks,
+.Em bfi ,
+to print out the list in bytes from index format, and
+.Em phys ,
+to print out the list in physical sector format.
+The format argument is
+required.
+Most drives support the physical sector format.
+Some drives
+support the logical block format.
+Many drives, if they do not support the
+requested format, return the data in an alternate format, along with sense
+information indicating that the requested data format is not supported.
+The
+.Nm
+utility
+attempts to detect this, and print out whatever format the drive returns.
+If the drive uses a non-standard sense code to report that it does not
+support the requested format,
+.Nm
+will probably see the error as a failure to complete the request.
+.It Fl G
+Print out the grown defect list.
+This is a list of bad blocks that have
+been remapped since the disk left the factory.
+.It Fl P
+Print out the primary defect list.
+.El
+.Pp
+If neither
+.Fl P
+nor
+.Fl G
+is specified,
+.Nm
+will print out the number of defects given in the READ DEFECT DATA header
+returned from the drive.
+.It Ic modepage
+Allows the user to display and optionally edit a SCSI mode page.
+The mode
+page formats are located in
+.Pa /usr/share/misc/scsi_modes .
+This can be overridden by specifying a different file in the
+.Ev SCSI_MODES
+environment variable.
+The
+.Ic modepage
+command takes several arguments:
+.Bl -tag -width 12n
+.It Fl d
+Disable block descriptors for mode sense.
+.It Fl b
+Displays mode page data in binary format.
+.It Fl e
+This flag allows the user to edit values in the mode page.
+The user may
+either edit mode page values with the text editor pointed to by his
+.Ev EDITOR
+environment variable, or supply mode page values via standard input, using
+the same format that
+.Nm
+uses to display mode page values.
+The editor will be invoked if
+.Nm
+detects that standard input is terminal.
+.It Fl l
+Lists all available mode pages.
+.It Fl m Ar mode_page
+This specifies the number of the mode page the user would like to view
+and/or edit.
+This argument is mandatory unless
+.Fl l
+is specified.
+.It Fl P Ar pgctl
+This allows the user to specify the page control field.
+Possible values are:
+.Bl -tag -width xxx -compact
+.It 0
+Current values
+.It 1
+Changeable values
+.It 2
+Default values
+.It 3
+Saved values
+.El
+.El
+.It Ic cmd
+Allows the user to send an arbitrary SCSI CDB to any device.
+The
+.Ic cmd
+function requires the
+.Fl c
+argument to specify the CDB.
+Other arguments are optional, depending on
+the command type.
+The command and data specification syntax is documented
+in
+.Xr cam_cdbparse 3 .
+NOTE: If the CDB specified causes data to be transfered to or from the
+SCSI device in question, you MUST specify either
+.Fl i
+or
+.Fl o .
+.Bl -tag -width 17n
+.It Fl c Ar cmd Op args
+This specifies the SCSI CDB.
+CDBs may be 6, 10, 12 or 16 bytes.
+.It Fl i Ar len Ar fmt
+This specifies the amount of data to read, and how it should be displayed.
+If the format is
+.Sq - ,
+.Ar len
+bytes of data will be read from the device and written to standard output.
+.It Fl o Ar len Ar fmt Op args
+This specifies the amount of data to be written to a device, and the data
+that is to be written.
+If the format is
+.Sq - ,
+.Ar len
+bytes of data will be read from standard input and written to the device.
+.El
+.It Ic debug
+Turn on CAM debugging printfs in the kernel.
+This requires options CAMDEBUG
+in your kernel config file.
+WARNING: enabling debugging printfs currently
+causes an EXTREME number of kernel printfs.
+You may have difficulty
+turning off the debugging printfs once they start, since the kernel will be
+busy printing messages and unable to service other requests quickly.
+The
+.Ic debug
+function takes a number of arguments:
+.Bl -tag -width 18n
+.It Fl I
+Enable CAM_DEBUG_INFO printfs.
+.It Fl P
+Enable CAM_DEBUG_PERIPH printfs.
+.It Fl T
+Enable CAM_DEBUG_TRACE printfs.
+.It Fl S
+Enable CAM_DEBUG_SUBTRACE printfs.
+.It Fl X
+Enable CAM_DEBUG_XPT printfs.
+.It Fl c
+Enable CAM_DEBUG_CDB printfs.
+This will cause the kernel to print out the
+SCSI CDBs sent to the specified device(s).
+.It all
+Enable debugging for all devices.
+.It off
+Turn off debugging for all devices
+.It bus Ns Op :target Ns Op :lun
+Turn on debugging for the given bus, target or lun.
+If the lun or target
+and lun are not specified, they are wildcarded.
+(i.e., just specifying a
+bus turns on debugging printfs for all devices on that bus.)
+.El
+.It Ic tags
+Show or set the number of "tagged openings" or simultaneous transactions
+we attempt to queue to a particular device.
+By default, the
+.Ic tags
+command, with no command-specific arguments (i.e., only generic arguments)
+prints out the "soft" maximum number of transactions that can be queued to
+the device in question.
+For more detailed information, use the
+.Fl v
+argument described below.
+.Bl -tag -width 7n
+.It Fl N Ar tags
+Set the number of tags for the given device.
+This must be between the
+minimum and maximum number set in the kernel quirk table.
+The default for
+most devices that support tagged queueing is a minimum of 2 and a maximum
+of 255.
+The minimum and maximum values for a given device may be
+determined by using the
+.Fl v
+switch.
+The meaning of the
+.Fl v
+switch for this
+.Nm
+subcommand is described below.
+.It Fl q
+Be quiet, and do not report the number of tags.
+This is generally used when
+setting the number of tags.
+.It Fl v
+The verbose flag has special functionality for the
+.Em tags
+argument.
+It causes
+.Nm
+to print out the tagged queueing related fields of the XPT_GDEV_TYPE CCB:
+.Bl -tag -width 13n
+.It dev_openings
+This is the amount of capacity for transactions queued to a given device.
+.It dev_active
+This is the number of transactions currently queued to a device.
+.It devq_openings
+This is the kernel queue space for transactions.
+This count usually mirrors
+dev_openings except during error recovery operations when
+the device queue is frozen (device is not allowed to receive
+commands), the number of dev_openings is reduced, or transaction
+replay is occurring.
+.It devq_queued
+This is the number of transactions waiting in the kernel queue for capacity
+on the device.
+This number is usually zero unless error recovery is in
+progress.
+.It held
+The held count is the number of CCBs held by peripheral drivers that have
+either just been completed or are about to be released to the transport
+layer for service by a device.
+Held CCBs reserve capacity on a given
+device.
+.It mintags
+This is the current "hard" minimum number of transactions that can be
+queued to a device at once.
+The
+.Ar dev_openings
+value above cannot go below this number.
+The default value for
+.Ar mintags
+is 2, although it may be set higher or lower for various devices.
+.It maxtags
+This is the "hard" maximum number of transactions that can be queued to a
+device at one time.
+The
+.Ar dev_openings
+value cannot go above this number.
+The default value for
+.Ar maxtags
+is 255, although it may be set higher or lower for various devices.
+.El
+.El
+.It Ic negotiate
+Show or negotiate various communication parameters.
+Some controllers may
+not support setting or changing some of these values.
+For instance, the
+Adaptec 174x controllers do not support changing a device's sync rate or
+offset.
+The
+.Nm
+utility
+will not attempt to set the parameter if the controller indicates that it
+does not support setting the parameter.
+To find out what the controller
+supports, use the
+.Fl v
+flag.
+The meaning of the
+.Fl v
+flag for the
+.Ic negotiate
+command is described below.
+Also, some controller drivers do not support
+setting negotiation parameters, even if the underlying controller supports
+negotiation changes.
+Some controllers, such as the Advansys wide
+controllers, support enabling and disabling synchronous negotiation for
+a device, but do not support setting the synchronous negotiation rate.
+.Bl -tag -width 17n
+.It Fl a
+Attempt to make the negotiation settings take effect immediately by sending
+a Test Unit Ready command to the device.
+.It Fl c
+Show or set current negotiation settings.
+This is the default.
+.It Fl D Ar enable|disable
+Enable or disable disconnection.
+.It Fl O Ar offset
+Set the command delay offset.
+.It Fl q
+Be quiet, do not print anything.
+This is generally useful when you want to
+set a parameter, but do not want any status information.
+.It Fl R Ar syncrate
+Change the synchronization rate for a device.
+The sync rate is a floating
+point value specified in MHz.
+So, for instance,
+.Sq 20.000
+is a legal value, as is
+.Sq 20 .
+.It Fl T Ar enable|disable
+Enable or disable tagged queueing for a device.
+.It Fl U
+Show or set user negotiation settings.
+The default is to show or set
+current negotiation settings.
+.It Fl v
+The verbose switch has special meaning for the
+.Ic negotiate
+subcommand.
+It causes
+.Nm
+to print out the contents of a Path Inquiry (XPT_PATH_INQ) CCB sent to the
+controller driver.
+.It Fl W Ar bus_width
+Specify the bus width to negotiate with a device.
+The bus width is
+specified in bits.
+The only useful values to specify are 8, 16, and 32
+bits.
+The controller must support the bus width in question in order for
+the setting to take effect.
+.El
+.Pp
+In general, sync rate and offset settings will not take effect for a
+device until a command has been sent to the device.
+The
+.Fl a
+switch above will automatically send a Test Unit Ready to the device so
+negotiation parameters will take effect.
+.It Ic format
+Issue the
+.Tn SCSI
+FORMAT UNIT command to the named device.
+.Pp
+.Em WARNING! WARNING! WARNING!
+.Pp
+Low level formatting a disk will destroy ALL data on the disk.
+Use
+extreme caution when issuing this command.
+Many users low-level format
+disks that do not really need to be low-level formatted.
+There are
+relatively few scenarios that call for low-level formatting a disk.
+One reason for
+low-level formatting a disk is to initialize the disk after changing
+its physical sector size.
+Another reason for low-level formatting a disk
+is to revive the disk if you are getting "medium format corrupted" errors
+from the disk in response to read and write requests.
+.Pp
+Some disks take longer than others to format.
+Users should specify a
+timeout long enough to allow the format to complete.
+The default format
+timeout is 3 hours, which should be long enough for most disks.
+Some hard
+disks will complete a format operation in a very short period of time
+(on the order of 5 minutes or less).
+This is often because the drive
+does not really support the FORMAT UNIT command -- it just accepts the
+command, waits a few minutes and then returns it.
+.Pp
+The
+.Sq format
+subcommand takes several arguments that modify its default behavior.
+The
+.Fl q
+and
+.Fl y
+arguments can be useful for scripts.
+.Pp
+.Bl -tag -width 6n
+.It Fl q
+Be quiet, do not print any status messages.
+This option will not disable
+the questions, however.
+To disable questions, use the
+.Fl y
+argument, below.
+.It Fl r
+Run in
+.Dq report only
+mode.
+This will report status on a format that is already running on the drive.
+.It Fl w
+Issue a non-immediate format command.
+By default,
+.Nm
+issues the FORMAT UNIT command with the immediate bit set.
+This tells the
+device to immediately return the format command, before the format has
+actually completed.
+Then,
+.Nm
+gathers
+.Tn SCSI
+sense information from the device every second to determine how far along
+in the format process it is.
+If the
+.Fl w
+argument is specified,
+.Nm
+will issue a non-immediate format command, and will be unable to print any
+information to let the user know what percentage of the disk has been
+formatted.
+.It Fl y
+Do not ask any questions.
+By default,
+.Nm
+will ask the user if he/she really wants to format the disk in question,
+and also if the default format command timeout is acceptable.
+The user
+will not be asked about the timeout if a timeout is specified on the
+command line.
+.El
+.It Ic help
+Print out verbose usage information.
+.El
+.Sh ENVIRONMENT
+The
+.Ev SCSI_MODES
+variable allows the user to specify an alternate mode page format file.
+.Pp
+The
+.Ev EDITOR
+variable determines which text editor
+.Nm
+starts when editing mode pages.
+.Sh FILES
+.Bl -tag -width /usr/share/misc/scsi_modes -compact
+.It Pa /usr/share/misc/scsi_modes
+is the SCSI mode format database.
+.It Pa /dev/xpt0
+is the transport layer device.
+.It Pa /dev/pass*
+are the CAM application passthrough devices.
+.El
+.Sh EXAMPLES
+.Dl camcontrol eject -n cd -u 1 -v
+.Pp
+Eject the CD from cd1, and print SCSI sense information if the command
+fails.
+.Pp
+.Dl camcontrol tur da0
+.Pp
+Send the SCSI test unit ready command to da0.
+The
+.Nm
+utility will report whether the disk is ready, but will not display sense
+information if the command fails since the
+.Fl v
+switch was not specified.
+.Pp
+.Bd -literal -offset indent
+camcontrol tur da1 -E -C 4 -t 50 -v
+.Ed
+.Pp
+Send a test unit ready command to da1.
+Enable kernel error recovery.
+Specify a retry count of 4, and a timeout of 50 seconds.
+Enable sense
+printing (with the
+.Fl v
+flag) if the command fails.
+Since error recovery is turned on, the
+disk will be spun up if it is not currently spinning.
+The
+.Nm
+utility will report whether the disk is ready.
+.Bd -literal -offset indent
+camcontrol cmd -n cd -u 1 -v -c "3C 00 00 00 00 00 00 00 0e 00" \e
+ -i 0xe "s1 i3 i1 i1 i1 i1 i1 i1 i1 i1 i1 i1"
+.Ed
+.Pp
+Issue a READ BUFFER command (0x3C) to cd1.
+Display the buffer size of cd1,
+and display the first 10 bytes from the cache on cd1.
+Display SCSI sense
+information if the command fails.
+.Pp
+.Bd -literal -offset indent
+camcontrol cmd -n cd -u 1 -v -c "3B 00 00 00 00 00 00 00 0e 00" \e
+ -o 14 "00 00 00 00 1 2 3 4 5 6 v v v v" 7 8 9 8
+.Ed
+.Pp
+Issue a WRITE BUFFER (0x3B) command to cd1.
+Write out 10 bytes of data,
+not including the (reserved) 4 byte header.
+Print out sense information if
+the command fails.
+Be very careful with this command, improper use may
+cause data corruption.
+.Pp
+.Bd -literal -offset indent
+camcontrol modepage da3 -m 1 -e -P 3
+.Ed
+.Pp
+Edit mode page 1 (the Read-Write Error Recover page) for da3, and save the
+settings on the drive.
+Mode page 1 contains a disk drive's auto read and
+write reallocation settings, among other things.
+.Pp
+.Dl camcontrol rescan all
+.Pp
+Rescan all SCSI busses in the system for devices that have been added,
+removed or changed.
+.Pp
+.Dl camcontrol rescan 0
+.Pp
+Rescan SCSI bus 0 for devices that have been added, removed or changed.
+.Pp
+.Dl camcontrol rescan 0:1:0
+.Pp
+Rescan SCSI bus 0, target 1, lun 0 to see if it has been added, removed, or
+changed.
+.Pp
+.Dl camcontrol tags da5 -N 24
+.Pp
+Set the number of concurrent transactions for da5 to 24.
+.Pp
+.Bd -literal -offset indent
+camcontrol negotiate -n da -u 4 -T disable
+.Ed
+.Pp
+Disable tagged queueing for da4.
+.Pp
+.Bd -literal -offset indent
+camcontrol negotiate -n da -u 3 -R 20.000 -O 15 -a
+.Ed
+.Pp
+Negotiate a sync rate of 20MHz and an offset of 15 with da3.
+Then send a
+Test Unit Ready command to make the settings take effect.
+.Sh SEE ALSO
+.Xr cam 3 ,
+.Xr cam_cdbparse 3 ,
+.Xr cam 4 ,
+.Xr pass 4 ,
+.Xr xpt 4
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 3.0 .
+.Pp
+The mode page editing code and arbitrary SCSI command code are based upon
+code in the old
+.Xr scsi 8
+utility and
+.Xr scsi 3
+library, written by Julian Elischer and Peter Dufault.
+The
+.Xr scsi 8
+program first appeared in
+.Bx 386 0.1.2.4 ,
+and first appeared in
+.Fx
+in
+.Fx 2.0.5 .
+.Sh AUTHORS
+.An Kenneth Merry Aq ken@FreeBSD.org
+.Sh BUGS
+The code that parses the generic command line arguments does not know that
+some of the subcommands take multiple arguments.
+So if, for instance, you
+tried something like this:
+.Bd -literal -offset indent
+camcontrol cmd -n da -u 1 -c "00 00 00 00 00 v" 0x00 -v
+.Ed
+.Pp
+The sense information from the test unit ready command would not get
+printed out, since the first
+.Xr getopt 3
+call in
+.Nm
+bails out when it sees the second argument to
+.Fl c
+(0x00),
+above.
+Fixing this behavior would take some gross code, or changes to the
+.Xr getopt 3
+interface.
+The best way to circumvent this problem is to always make sure
+to specify generic
+.Nm
+arguments before any command-specific arguments.
diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c
new file mode 100644
index 0000000..a8447ee
--- /dev/null
+++ b/sbin/camcontrol/camcontrol.c
@@ -0,0 +1,3564 @@
+/*
+ * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2005 Kenneth D. Merry
+ * 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 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/ioctl.h>
+#include <sys/stdint.h>
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <err.h>
+
+#include <cam/cam.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_ccb.h>
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_da.h>
+#include <cam/scsi/scsi_pass.h>
+#include <cam/scsi/scsi_message.h>
+#include <camlib.h>
+#include "camcontrol.h"
+
+typedef enum {
+ CAM_CMD_NONE = 0x00000000,
+ CAM_CMD_DEVLIST = 0x00000001,
+ CAM_CMD_TUR = 0x00000002,
+ CAM_CMD_INQUIRY = 0x00000003,
+ CAM_CMD_STARTSTOP = 0x00000004,
+ CAM_CMD_RESCAN = 0x00000005,
+ CAM_CMD_READ_DEFECTS = 0x00000006,
+ CAM_CMD_MODE_PAGE = 0x00000007,
+ CAM_CMD_SCSI_CMD = 0x00000008,
+ CAM_CMD_DEVTREE = 0x00000009,
+ CAM_CMD_USAGE = 0x0000000a,
+ CAM_CMD_DEBUG = 0x0000000b,
+ CAM_CMD_RESET = 0x0000000c,
+ CAM_CMD_FORMAT = 0x0000000d,
+ CAM_CMD_TAG = 0x0000000e,
+ CAM_CMD_RATE = 0x0000000f,
+ CAM_CMD_DETACH = 0x00000010,
+} cam_cmdmask;
+
+typedef enum {
+ CAM_ARG_NONE = 0x00000000,
+ CAM_ARG_VERBOSE = 0x00000001,
+ CAM_ARG_DEVICE = 0x00000002,
+ CAM_ARG_BUS = 0x00000004,
+ CAM_ARG_TARGET = 0x00000008,
+ CAM_ARG_LUN = 0x00000010,
+ CAM_ARG_EJECT = 0x00000020,
+ CAM_ARG_UNIT = 0x00000040,
+ CAM_ARG_FORMAT_BLOCK = 0x00000080,
+ CAM_ARG_FORMAT_BFI = 0x00000100,
+ CAM_ARG_FORMAT_PHYS = 0x00000200,
+ CAM_ARG_PLIST = 0x00000400,
+ CAM_ARG_GLIST = 0x00000800,
+ CAM_ARG_GET_SERIAL = 0x00001000,
+ CAM_ARG_GET_STDINQ = 0x00002000,
+ CAM_ARG_GET_XFERRATE = 0x00004000,
+ CAM_ARG_INQ_MASK = 0x00007000,
+ CAM_ARG_MODE_EDIT = 0x00008000,
+ CAM_ARG_PAGE_CNTL = 0x00010000,
+ CAM_ARG_TIMEOUT = 0x00020000,
+ CAM_ARG_CMD_IN = 0x00040000,
+ CAM_ARG_CMD_OUT = 0x00080000,
+ CAM_ARG_DBD = 0x00100000,
+ CAM_ARG_ERR_RECOVER = 0x00200000,
+ CAM_ARG_RETRIES = 0x00400000,
+ CAM_ARG_START_UNIT = 0x00800000,
+ CAM_ARG_DEBUG_INFO = 0x01000000,
+ CAM_ARG_DEBUG_TRACE = 0x02000000,
+ CAM_ARG_DEBUG_SUBTRACE = 0x04000000,
+ CAM_ARG_DEBUG_CDB = 0x08000000,
+ CAM_ARG_DEBUG_XPT = 0x10000000,
+ CAM_ARG_DEBUG_PERIPH = 0x20000000,
+} cam_argmask;
+
+struct camcontrol_opts {
+ const char *optname;
+ cam_cmdmask cmdnum;
+ cam_argmask argnum;
+ const char *subopt;
+};
+
+#ifndef MINIMALISTIC
+static const char scsicmd_opts[] = "c:i:o:";
+static const char readdefect_opts[] = "f:GP";
+static const char negotiate_opts[] = "acD:O:qR:T:UW:";
+#endif
+
+struct camcontrol_opts option_table[] = {
+#ifndef MINIMALISTIC
+ {"tur", CAM_CMD_TUR, CAM_ARG_NONE, NULL},
+ {"inquiry", CAM_CMD_INQUIRY, CAM_ARG_NONE, "DSR"},
+ {"start", CAM_CMD_STARTSTOP, CAM_ARG_START_UNIT, NULL},
+ {"stop", CAM_CMD_STARTSTOP, CAM_ARG_NONE, NULL},
+ {"load", CAM_CMD_STARTSTOP, CAM_ARG_START_UNIT | CAM_ARG_EJECT, NULL},
+ {"eject", CAM_CMD_STARTSTOP, CAM_ARG_EJECT, NULL},
+#endif /* MINIMALISTIC */
+ {"rescan", CAM_CMD_RESCAN, CAM_ARG_NONE, NULL},
+ {"reset", CAM_CMD_RESET, CAM_ARG_NONE, NULL},
+#ifndef MINIMALISTIC
+ {"cmd", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts},
+ {"command", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts},
+ {"defects", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts},
+ {"defectlist", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts},
+#endif /* MINIMALISTIC */
+ {"devlist", CAM_CMD_DEVTREE, CAM_ARG_NONE, NULL},
+#ifndef MINIMALISTIC
+ {"periphlist", CAM_CMD_DEVLIST, CAM_ARG_NONE, NULL},
+ {"modepage", CAM_CMD_MODE_PAGE, CAM_ARG_NONE, "bdelm:P:"},
+ {"tags", CAM_CMD_TAG, CAM_ARG_NONE, "N:q"},
+ {"negotiate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts},
+ {"rate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts},
+ {"debug", CAM_CMD_DEBUG, CAM_ARG_NONE, "IPTSXc"},
+ {"format", CAM_CMD_FORMAT, CAM_ARG_NONE, "qrwy"},
+#endif /* MINIMALISTIC */
+ {"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
+ {"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
+ {"-h", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
+ {NULL, 0, 0, NULL}
+};
+
+typedef enum {
+ CC_OR_NOT_FOUND,
+ CC_OR_AMBIGUOUS,
+ CC_OR_FOUND
+} camcontrol_optret;
+
+cam_cmdmask cmdlist;
+cam_argmask arglist;
+
+
+camcontrol_optret getoption(char *arg, cam_cmdmask *cmdnum, cam_argmask *argnum,
+ const char **subopt);
+#ifndef MINIMALISTIC
+static int getdevlist(struct cam_device *device);
+#endif /* MINIMALISTIC */
+static int getdevtree(void);
+#ifndef MINIMALISTIC
+static int testunitready(struct cam_device *device, int retry_count,
+ int timeout, int quiet);
+static int scsistart(struct cam_device *device, int startstop, int loadeject,
+ int retry_count, int timeout);
+static int scsidoinquiry(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int retry_count, int timeout);
+static int scsiinquiry(struct cam_device *device, int retry_count, int timeout);
+static int scsiserial(struct cam_device *device, int retry_count, int timeout);
+static int scsixferrate(struct cam_device *device);
+#endif /* MINIMALISTIC */
+static int parse_btl(char *tstr, int *bus, int *target, int *lun,
+ cam_argmask *arglst);
+static int dorescan_or_reset(int argc, char **argv, int rescan);
+static int rescan_or_reset_bus(int bus, int rescan);
+static int scanlun_or_reset_dev(int bus, int target, int lun, int scan);
+#ifndef MINIMALISTIC
+static int readdefects(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int retry_count, int timeout);
+static void modepage(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int retry_count, int timeout);
+static int scsicmd(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int retry_count, int timeout);
+static int tagcontrol(struct cam_device *device, int argc, char **argv,
+ char *combinedopt);
+static void cts_print(struct cam_device *device,
+ struct ccb_trans_settings *cts);
+static void cpi_print(struct ccb_pathinq *cpi);
+static int get_cpi(struct cam_device *device, struct ccb_pathinq *cpi);
+static int get_print_cts(struct cam_device *device, int user_settings,
+ int quiet, struct ccb_trans_settings *cts);
+static int ratecontrol(struct cam_device *device, int retry_count,
+ int timeout, int argc, char **argv, char *combinedopt);
+static int scsiformat(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int retry_count, int timeout);
+#endif /* MINIMALISTIC */
+
+camcontrol_optret
+getoption(char *arg, cam_cmdmask *cmdnum, cam_argmask *argnum,
+ const char **subopt)
+{
+ struct camcontrol_opts *opts;
+ int num_matches = 0;
+
+ for (opts = option_table; (opts != NULL) && (opts->optname != NULL);
+ opts++) {
+ if (strncmp(opts->optname, arg, strlen(arg)) == 0) {
+ *cmdnum = opts->cmdnum;
+ *argnum = opts->argnum;
+ *subopt = opts->subopt;
+ if (++num_matches > 1)
+ return(CC_OR_AMBIGUOUS);
+ }
+ }
+
+ if (num_matches > 0)
+ return(CC_OR_FOUND);
+ else
+ return(CC_OR_NOT_FOUND);
+}
+
+#ifndef MINIMALISTIC
+static int
+getdevlist(struct cam_device *device)
+{
+ union ccb *ccb;
+ char status[32];
+ int error = 0;
+
+ ccb = cam_getccb(device);
+
+ ccb->ccb_h.func_code = XPT_GDEVLIST;
+ ccb->ccb_h.flags = CAM_DIR_NONE;
+ ccb->ccb_h.retry_count = 1;
+ ccb->cgdl.index = 0;
+ ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS;
+ while (ccb->cgdl.status == CAM_GDEVLIST_MORE_DEVS) {
+ if (cam_send_ccb(device, ccb) < 0) {
+ perror("error getting device list");
+ cam_freeccb(ccb);
+ return(1);
+ }
+
+ status[0] = '\0';
+
+ switch (ccb->cgdl.status) {
+ case CAM_GDEVLIST_MORE_DEVS:
+ strcpy(status, "MORE");
+ break;
+ case CAM_GDEVLIST_LAST_DEVICE:
+ strcpy(status, "LAST");
+ break;
+ case CAM_GDEVLIST_LIST_CHANGED:
+ strcpy(status, "CHANGED");
+ break;
+ case CAM_GDEVLIST_ERROR:
+ strcpy(status, "ERROR");
+ error = 1;
+ break;
+ }
+
+ fprintf(stdout, "%s%d: generation: %d index: %d status: %s\n",
+ ccb->cgdl.periph_name,
+ ccb->cgdl.unit_number,
+ ccb->cgdl.generation,
+ ccb->cgdl.index,
+ status);
+
+ /*
+ * If the list has changed, we need to start over from the
+ * beginning.
+ */
+ if (ccb->cgdl.status == CAM_GDEVLIST_LIST_CHANGED)
+ ccb->cgdl.index = 0;
+ }
+
+ cam_freeccb(ccb);
+
+ return(error);
+}
+#endif /* MINIMALISTIC */
+
+static int
+getdevtree(void)
+{
+ union ccb ccb;
+ int bufsize, fd;
+ unsigned int i;
+ int need_close = 0;
+ int error = 0;
+ int skip_device = 0;
+
+ if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) {
+ warn("couldn't open %s", XPT_DEVICE);
+ return(1);
+ }
+
+ bzero(&ccb, sizeof(union ccb));
+
+ ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
+ ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
+ ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
+
+ ccb.ccb_h.func_code = XPT_DEV_MATCH;
+ bufsize = sizeof(struct dev_match_result) * 100;
+ ccb.cdm.match_buf_len = bufsize;
+ ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize);
+ if (ccb.cdm.matches == NULL) {
+ warnx("can't malloc memory for matches");
+ close(fd);
+ return(1);
+ }
+ ccb.cdm.num_matches = 0;
+
+ /*
+ * We fetch all nodes, since we display most of them in the default
+ * case, and all in the verbose case.
+ */
+ ccb.cdm.num_patterns = 0;
+ ccb.cdm.pattern_buf_len = 0;
+
+ /*
+ * We do the ioctl multiple times if necessary, in case there are
+ * more than 100 nodes in the EDT.
+ */
+ do {
+ if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
+ warn("error sending CAMIOCOMMAND ioctl");
+ error = 1;
+ break;
+ }
+
+ if ((ccb.ccb_h.status != CAM_REQ_CMP)
+ || ((ccb.cdm.status != CAM_DEV_MATCH_LAST)
+ && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
+ warnx("got CAM error %#x, CDM error %d\n",
+ ccb.ccb_h.status, ccb.cdm.status);
+ error = 1;
+ break;
+ }
+
+ for (i = 0; i < ccb.cdm.num_matches; i++) {
+ switch (ccb.cdm.matches[i].type) {
+ case DEV_MATCH_BUS: {
+ struct bus_match_result *bus_result;
+
+ /*
+ * Only print the bus information if the
+ * user turns on the verbose flag.
+ */
+ if ((arglist & CAM_ARG_VERBOSE) == 0)
+ break;
+
+ bus_result =
+ &ccb.cdm.matches[i].result.bus_result;
+
+ if (need_close) {
+ fprintf(stdout, ")\n");
+ need_close = 0;
+ }
+
+ fprintf(stdout, "scbus%d on %s%d bus %d:\n",
+ bus_result->path_id,
+ bus_result->dev_name,
+ bus_result->unit_number,
+ bus_result->bus_id);
+ break;
+ }
+ case DEV_MATCH_DEVICE: {
+ struct device_match_result *dev_result;
+ char vendor[16], product[48], revision[16];
+ char tmpstr[256];
+
+ dev_result =
+ &ccb.cdm.matches[i].result.device_result;
+
+ if ((dev_result->flags
+ & DEV_RESULT_UNCONFIGURED)
+ && ((arglist & CAM_ARG_VERBOSE) == 0)) {
+ skip_device = 1;
+ break;
+ } else
+ skip_device = 0;
+
+ cam_strvis(vendor, dev_result->inq_data.vendor,
+ sizeof(dev_result->inq_data.vendor),
+ sizeof(vendor));
+ cam_strvis(product,
+ dev_result->inq_data.product,
+ sizeof(dev_result->inq_data.product),
+ sizeof(product));
+ cam_strvis(revision,
+ dev_result->inq_data.revision,
+ sizeof(dev_result->inq_data.revision),
+ sizeof(revision));
+ sprintf(tmpstr, "<%s %s %s>", vendor, product,
+ revision);
+ if (need_close) {
+ fprintf(stdout, ")\n");
+ need_close = 0;
+ }
+
+ fprintf(stdout, "%-33s at scbus%d "
+ "target %d lun %d (",
+ tmpstr,
+ dev_result->path_id,
+ dev_result->target_id,
+ dev_result->target_lun);
+
+ need_close = 1;
+
+ break;
+ }
+ case DEV_MATCH_PERIPH: {
+ struct periph_match_result *periph_result;
+
+ periph_result =
+ &ccb.cdm.matches[i].result.periph_result;
+
+ if (skip_device != 0)
+ break;
+
+ if (need_close > 1)
+ fprintf(stdout, ",");
+
+ fprintf(stdout, "%s%d",
+ periph_result->periph_name,
+ periph_result->unit_number);
+
+ need_close++;
+ break;
+ }
+ default:
+ fprintf(stdout, "unknown match type\n");
+ break;
+ }
+ }
+
+ } while ((ccb.ccb_h.status == CAM_REQ_CMP)
+ && (ccb.cdm.status == CAM_DEV_MATCH_MORE));
+
+ if (need_close)
+ fprintf(stdout, ")\n");
+
+ close(fd);
+
+ return(error);
+}
+
+#ifndef MINIMALISTIC
+static int
+testunitready(struct cam_device *device, int retry_count, int timeout,
+ int quiet)
+{
+ int error = 0;
+ union ccb *ccb;
+
+ ccb = cam_getccb(device);
+
+ scsi_test_unit_ready(&ccb->csio,
+ /* retries */ retry_count,
+ /* cbfcnp */ NULL,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ timeout ? timeout : 5000);
+
+ /* Disable freezing the device queue */
+ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ if (arglist & CAM_ARG_ERR_RECOVER)
+ ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ if (quiet == 0)
+ perror("error sending test unit ready");
+
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+
+ cam_freeccb(ccb);
+ return(1);
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
+ if (quiet == 0)
+ fprintf(stdout, "Unit is ready\n");
+ } else {
+ if (quiet == 0)
+ fprintf(stdout, "Unit is not ready\n");
+ error = 1;
+
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ }
+
+ cam_freeccb(ccb);
+
+ return(error);
+}
+
+static int
+scsistart(struct cam_device *device, int startstop, int loadeject,
+ int retry_count, int timeout)
+{
+ union ccb *ccb;
+ int error = 0;
+
+ ccb = cam_getccb(device);
+
+ /*
+ * If we're stopping, send an ordered tag so the drive in question
+ * will finish any previously queued writes before stopping. If
+ * the device isn't capable of tagged queueing, or if tagged
+ * queueing is turned off, the tag action is a no-op.
+ */
+ scsi_start_stop(&ccb->csio,
+ /* retries */ retry_count,
+ /* cbfcnp */ NULL,
+ /* tag_action */ startstop ? MSG_SIMPLE_Q_TAG :
+ MSG_ORDERED_Q_TAG,
+ /* start/stop */ startstop,
+ /* load_eject */ loadeject,
+ /* immediate */ 0,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ timeout ? timeout : 120000);
+
+ /* Disable freezing the device queue */
+ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ if (arglist & CAM_ARG_ERR_RECOVER)
+ ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ perror("error sending start unit");
+
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+
+ cam_freeccb(ccb);
+ return(1);
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
+ if (startstop) {
+ fprintf(stdout, "Unit started successfully");
+ if (loadeject)
+ fprintf(stdout,", Media loaded\n");
+ else
+ fprintf(stdout,"\n");
+ } else {
+ fprintf(stdout, "Unit stopped successfully");
+ if (loadeject)
+ fprintf(stdout, ", Media ejected\n");
+ else
+ fprintf(stdout, "\n");
+ }
+ else {
+ error = 1;
+ if (startstop)
+ fprintf(stdout,
+ "Error received from start unit command\n");
+ else
+ fprintf(stdout,
+ "Error received from stop unit command\n");
+
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ }
+
+ cam_freeccb(ccb);
+
+ return(error);
+}
+
+static int
+scsidoinquiry(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int retry_count, int timeout)
+{
+ int c;
+ int error = 0;
+
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch(c) {
+ case 'D':
+ arglist |= CAM_ARG_GET_STDINQ;
+ break;
+ case 'R':
+ arglist |= CAM_ARG_GET_XFERRATE;
+ break;
+ case 'S':
+ arglist |= CAM_ARG_GET_SERIAL;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * If the user didn't specify any inquiry options, he wants all of
+ * them.
+ */
+ if ((arglist & CAM_ARG_INQ_MASK) == 0)
+ arglist |= CAM_ARG_INQ_MASK;
+
+ if (arglist & CAM_ARG_GET_STDINQ)
+ error = scsiinquiry(device, retry_count, timeout);
+
+ if (error != 0)
+ return(error);
+
+ if (arglist & CAM_ARG_GET_SERIAL)
+ scsiserial(device, retry_count, timeout);
+
+ if (error != 0)
+ return(error);
+
+ if (arglist & CAM_ARG_GET_XFERRATE)
+ error = scsixferrate(device);
+
+ return(error);
+}
+
+static int
+scsiinquiry(struct cam_device *device, int retry_count, int timeout)
+{
+ union ccb *ccb;
+ struct scsi_inquiry_data *inq_buf;
+ int error = 0;
+
+ ccb = cam_getccb(device);
+
+ if (ccb == NULL) {
+ warnx("couldn't allocate CCB");
+ return(1);
+ }
+
+ /* cam_getccb cleans up the header, caller has to zero the payload */
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+ inq_buf = (struct scsi_inquiry_data *)malloc(
+ sizeof(struct scsi_inquiry_data));
+
+ if (inq_buf == NULL) {
+ cam_freeccb(ccb);
+ warnx("can't malloc memory for inquiry\n");
+ return(1);
+ }
+ bzero(inq_buf, sizeof(*inq_buf));
+
+ /*
+ * Note that although the size of the inquiry buffer is the full
+ * 256 bytes specified in the SCSI spec, we only tell the device
+ * that we have allocated SHORT_INQUIRY_LENGTH bytes. There are
+ * two reasons for this:
+ *
+ * - The SCSI spec says that when a length field is only 1 byte,
+ * a value of 0 will be interpreted as 256. Therefore
+ * scsi_inquiry() will convert an inq_len (which is passed in as
+ * a u_int32_t, but the field in the CDB is only 1 byte) of 256
+ * to 0. Evidently, very few devices meet the spec in that
+ * regard. Some devices, like many Seagate disks, take the 0 as
+ * 0, and don't return any data. One Pioneer DVD-R drive
+ * returns more data than the command asked for.
+ *
+ * So, since there are numerous devices that just don't work
+ * right with the full inquiry size, we don't send the full size.
+ *
+ * - The second reason not to use the full inquiry data length is
+ * that we don't need it here. The only reason we issue a
+ * standard inquiry is to get the vendor name, device name,
+ * and revision so scsi_print_inquiry() can print them.
+ *
+ * If, at some point in the future, more inquiry data is needed for
+ * some reason, this code should use a procedure similar to the
+ * probe code. i.e., issue a short inquiry, and determine from
+ * the additional length passed back from the device how much
+ * inquiry data the device supports. Once the amount the device
+ * supports is determined, issue an inquiry for that amount and no
+ * more.
+ *
+ * KDM, 2/18/2000
+ */
+ scsi_inquiry(&ccb->csio,
+ /* retries */ retry_count,
+ /* cbfcnp */ NULL,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* inq_buf */ (u_int8_t *)inq_buf,
+ /* inq_len */ SHORT_INQUIRY_LENGTH,
+ /* evpd */ 0,
+ /* page_code */ 0,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ timeout ? timeout : 5000);
+
+ /* Disable freezing the device queue */
+ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ if (arglist & CAM_ARG_ERR_RECOVER)
+ ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ perror("error sending SCSI inquiry");
+
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+
+ cam_freeccb(ccb);
+ return(1);
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ error = 1;
+
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ }
+
+ cam_freeccb(ccb);
+
+ if (error != 0) {
+ free(inq_buf);
+ return(error);
+ }
+
+ fprintf(stdout, "%s%d: ", device->device_name,
+ device->dev_unit_num);
+ scsi_print_inquiry(inq_buf);
+
+ free(inq_buf);
+
+ return(0);
+}
+
+static int
+scsiserial(struct cam_device *device, int retry_count, int timeout)
+{
+ union ccb *ccb;
+ struct scsi_vpd_unit_serial_number *serial_buf;
+ char serial_num[SVPD_SERIAL_NUM_SIZE + 1];
+ int error = 0;
+
+ ccb = cam_getccb(device);
+
+ if (ccb == NULL) {
+ warnx("couldn't allocate CCB");
+ return(1);
+ }
+
+ /* cam_getccb cleans up the header, caller has to zero the payload */
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+ serial_buf = (struct scsi_vpd_unit_serial_number *)
+ malloc(sizeof(*serial_buf));
+
+ if (serial_buf == NULL) {
+ cam_freeccb(ccb);
+ warnx("can't malloc memory for serial number");
+ return(1);
+ }
+
+ scsi_inquiry(&ccb->csio,
+ /*retries*/ retry_count,
+ /*cbfcnp*/ NULL,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* inq_buf */ (u_int8_t *)serial_buf,
+ /* inq_len */ sizeof(*serial_buf),
+ /* evpd */ 1,
+ /* page_code */ SVPD_UNIT_SERIAL_NUMBER,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ timeout ? timeout : 5000);
+
+ /* Disable freezing the device queue */
+ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ if (arglist & CAM_ARG_ERR_RECOVER)
+ ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ warn("error getting serial number");
+
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+
+ cam_freeccb(ccb);
+ free(serial_buf);
+ return(1);
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ error = 1;
+
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ }
+
+ cam_freeccb(ccb);
+
+ if (error != 0) {
+ free(serial_buf);
+ return(error);
+ }
+
+ bcopy(serial_buf->serial_num, serial_num, serial_buf->length);
+ serial_num[serial_buf->length] = '\0';
+
+ if ((arglist & CAM_ARG_GET_STDINQ)
+ || (arglist & CAM_ARG_GET_XFERRATE))
+ fprintf(stdout, "%s%d: Serial Number ",
+ device->device_name, device->dev_unit_num);
+
+ fprintf(stdout, "%.60s\n", serial_num);
+
+ free(serial_buf);
+
+ return(0);
+}
+
+static int
+scsixferrate(struct cam_device *device)
+{
+ u_int32_t freq;
+ u_int32_t speed;
+ union ccb *ccb;
+ u_int mb;
+ int retval = 0;
+
+ ccb = cam_getccb(device);
+
+ if (ccb == NULL) {
+ warnx("couldn't allocate CCB");
+ return(1);
+ }
+
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_trans_settings) - sizeof(struct ccb_hdr));
+
+ ccb->ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
+ ccb->cts.flags = CCB_TRANS_CURRENT_SETTINGS;
+
+ if (((retval = cam_send_ccb(device, ccb)) < 0)
+ || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
+ const char error_string[] = "error getting transfer settings";
+
+ if (retval < 0)
+ warn(error_string);
+ else
+ warnx(error_string);
+
+ if (arglist & CAM_ARG_VERBOSE)
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+
+ retval = 1;
+
+ goto xferrate_bailout;
+
+ }
+
+ if (((ccb->cts.valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0)
+ && (ccb->cts.sync_offset != 0)) {
+ freq = scsi_calc_syncsrate(ccb->cts.sync_period);
+ speed = freq;
+ } else {
+ struct ccb_pathinq cpi;
+
+ retval = get_cpi(device, &cpi);
+
+ if (retval != 0)
+ goto xferrate_bailout;
+
+ speed = cpi.base_transfer_speed;
+ freq = 0;
+ }
+
+ fprintf(stdout, "%s%d: ", device->device_name,
+ device->dev_unit_num);
+
+ if ((ccb->cts.valid & CCB_TRANS_BUS_WIDTH_VALID) != 0)
+ speed *= (0x01 << device->bus_width);
+
+ mb = speed / 1000;
+
+ if (mb > 0)
+ fprintf(stdout, "%d.%03dMB/s transfers ",
+ mb, speed % 1000);
+ else
+ fprintf(stdout, "%dKB/s transfers ",
+ speed);
+
+ if (((ccb->cts.valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0)
+ && (ccb->cts.sync_offset != 0))
+ fprintf(stdout, "(%d.%03dMHz, offset %d", freq / 1000,
+ freq % 1000, ccb->cts.sync_offset);
+
+ if (((ccb->cts.valid & CCB_TRANS_BUS_WIDTH_VALID) != 0)
+ && (ccb->cts.bus_width > 0)) {
+ if (((ccb->cts.valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0)
+ && (ccb->cts.sync_offset != 0)) {
+ fprintf(stdout, ", ");
+ } else {
+ fprintf(stdout, " (");
+ }
+ fprintf(stdout, "%dbit)", 8 * (0x01 << ccb->cts.bus_width));
+ } else if (((ccb->cts.valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0)
+ && (ccb->cts.sync_offset != 0)) {
+ fprintf(stdout, ")");
+ }
+
+ if (((ccb->cts.valid & CCB_TRANS_TQ_VALID) != 0)
+ && (ccb->cts.flags & CCB_TRANS_TAG_ENB))
+ fprintf(stdout, ", Tagged Queueing Enabled");
+
+ fprintf(stdout, "\n");
+
+xferrate_bailout:
+
+ cam_freeccb(ccb);
+
+ return(retval);
+}
+#endif /* MINIMALISTIC */
+
+/*
+ * Parse out a bus, or a bus, target and lun in the following
+ * format:
+ * bus
+ * bus:target
+ * bus:target:lun
+ *
+ * Returns the number of parsed components, or 0.
+ */
+static int
+parse_btl(char *tstr, int *bus, int *target, int *lun, cam_argmask *arglst)
+{
+ char *tmpstr;
+ int convs = 0;
+
+ while (isspace(*tstr) && (*tstr != '\0'))
+ tstr++;
+
+ tmpstr = (char *)strtok(tstr, ":");
+ if ((tmpstr != NULL) && (*tmpstr != '\0')) {
+ *bus = strtol(tmpstr, NULL, 0);
+ *arglst |= CAM_ARG_BUS;
+ convs++;
+ tmpstr = (char *)strtok(NULL, ":");
+ if ((tmpstr != NULL) && (*tmpstr != '\0')) {
+ *target = strtol(tmpstr, NULL, 0);
+ *arglst |= CAM_ARG_TARGET;
+ convs++;
+ tmpstr = (char *)strtok(NULL, ":");
+ if ((tmpstr != NULL) && (*tmpstr != '\0')) {
+ *lun = strtol(tmpstr, NULL, 0);
+ *arglst |= CAM_ARG_LUN;
+ convs++;
+ }
+ }
+ }
+
+ return convs;
+}
+
+static int
+dorescan_or_reset(int argc, char **argv, int rescan)
+{
+ static const char must[] =
+ "you must specify \"all\", a bus, or a bus:target:lun to %s";
+ int rv, error = 0;
+ int bus = -1, target = -1, lun = -1;
+ char *tstr;
+
+ if (argc < 3) {
+ warnx(must, rescan? "rescan" : "reset");
+ return(1);
+ }
+
+ tstr = argv[optind];
+ while (isspace(*tstr) && (*tstr != '\0'))
+ tstr++;
+ if (strncasecmp(tstr, "all", strlen("all")) == 0)
+ arglist |= CAM_ARG_BUS;
+ else {
+ rv = parse_btl(argv[optind], &bus, &target, &lun, &arglist);
+ if (rv != 1 && rv != 3) {
+ warnx(must, rescan? "rescan" : "reset");
+ return(1);
+ }
+ }
+
+ if ((arglist & CAM_ARG_BUS)
+ && (arglist & CAM_ARG_TARGET)
+ && (arglist & CAM_ARG_LUN))
+ error = scanlun_or_reset_dev(bus, target, lun, rescan);
+ else
+ error = rescan_or_reset_bus(bus, rescan);
+
+ return(error);
+}
+
+static int
+rescan_or_reset_bus(int bus, int rescan)
+{
+ union ccb ccb, matchccb;
+ int fd, retval;
+ int bufsize;
+
+ retval = 0;
+
+ if ((fd = open(XPT_DEVICE, O_RDWR)) < 0) {
+ warnx("error opening tranport layer device %s", XPT_DEVICE);
+ warn("%s", XPT_DEVICE);
+ return(1);
+ }
+
+ if (bus != -1) {
+ ccb.ccb_h.func_code = rescan ? XPT_SCAN_BUS : XPT_RESET_BUS;
+ ccb.ccb_h.path_id = bus;
+ ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
+ ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
+ ccb.crcn.flags = CAM_FLAG_NONE;
+
+ /* run this at a low priority */
+ ccb.ccb_h.pinfo.priority = 5;
+
+ if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
+ warn("CAMIOCOMMAND ioctl failed");
+ close(fd);
+ return(1);
+ }
+
+ if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
+ fprintf(stdout, "%s of bus %d was successful\n",
+ rescan ? "Re-scan" : "Reset", bus);
+ } else {
+ fprintf(stdout, "%s of bus %d returned error %#x\n",
+ rescan ? "Re-scan" : "Reset", bus,
+ ccb.ccb_h.status & CAM_STATUS_MASK);
+ retval = 1;
+ }
+
+ close(fd);
+ return(retval);
+
+ }
+
+
+ /*
+ * The right way to handle this is to modify the xpt so that it can
+ * handle a wildcarded bus in a rescan or reset CCB. At the moment
+ * that isn't implemented, so instead we enumerate the busses and
+ * send the rescan or reset to those busses in the case where the
+ * given bus is -1 (wildcard). We don't send a rescan or reset
+ * to the xpt bus; sending a rescan to the xpt bus is effectively a
+ * no-op, sending a rescan to the xpt bus would result in a status of
+ * CAM_REQ_INVALID.
+ */
+ bzero(&(&matchccb.ccb_h)[1],
+ sizeof(struct ccb_dev_match) - sizeof(struct ccb_hdr));
+ matchccb.ccb_h.func_code = XPT_DEV_MATCH;
+ bufsize = sizeof(struct dev_match_result) * 20;
+ matchccb.cdm.match_buf_len = bufsize;
+ matchccb.cdm.matches=(struct dev_match_result *)malloc(bufsize);
+ if (matchccb.cdm.matches == NULL) {
+ warnx("can't malloc memory for matches");
+ retval = 1;
+ goto bailout;
+ }
+ matchccb.cdm.num_matches = 0;
+
+ matchccb.cdm.num_patterns = 1;
+ matchccb.cdm.pattern_buf_len = sizeof(struct dev_match_pattern);
+
+ matchccb.cdm.patterns = (struct dev_match_pattern *)malloc(
+ matchccb.cdm.pattern_buf_len);
+ if (matchccb.cdm.patterns == NULL) {
+ warnx("can't malloc memory for patterns");
+ retval = 1;
+ goto bailout;
+ }
+ matchccb.cdm.patterns[0].type = DEV_MATCH_BUS;
+ matchccb.cdm.patterns[0].pattern.bus_pattern.flags = BUS_MATCH_ANY;
+
+ do {
+ unsigned int i;
+
+ if (ioctl(fd, CAMIOCOMMAND, &matchccb) == -1) {
+ warn("CAMIOCOMMAND ioctl failed");
+ retval = 1;
+ goto bailout;
+ }
+
+ if ((matchccb.ccb_h.status != CAM_REQ_CMP)
+ || ((matchccb.cdm.status != CAM_DEV_MATCH_LAST)
+ && (matchccb.cdm.status != CAM_DEV_MATCH_MORE))) {
+ warnx("got CAM error %#x, CDM error %d\n",
+ matchccb.ccb_h.status, matchccb.cdm.status);
+ retval = 1;
+ goto bailout;
+ }
+
+ for (i = 0; i < matchccb.cdm.num_matches; i++) {
+ struct bus_match_result *bus_result;
+
+ /* This shouldn't happen. */
+ if (matchccb.cdm.matches[i].type != DEV_MATCH_BUS)
+ continue;
+
+ bus_result = &matchccb.cdm.matches[i].result.bus_result;
+
+ /*
+ * We don't want to rescan or reset the xpt bus.
+ * See above.
+ */
+ if ((int)bus_result->path_id == -1)
+ continue;
+
+ ccb.ccb_h.func_code = rescan ? XPT_SCAN_BUS :
+ XPT_RESET_BUS;
+ ccb.ccb_h.path_id = bus_result->path_id;
+ ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
+ ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
+ ccb.crcn.flags = CAM_FLAG_NONE;
+
+ /* run this at a low priority */
+ ccb.ccb_h.pinfo.priority = 5;
+
+ if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
+ warn("CAMIOCOMMAND ioctl failed");
+ retval = 1;
+ goto bailout;
+ }
+
+ if ((ccb.ccb_h.status & CAM_STATUS_MASK) ==CAM_REQ_CMP){
+ fprintf(stdout, "%s of bus %d was successful\n",
+ rescan? "Re-scan" : "Reset",
+ bus_result->path_id);
+ } else {
+ /*
+ * Don't bail out just yet, maybe the other
+ * rescan or reset commands will complete
+ * successfully.
+ */
+ fprintf(stderr, "%s of bus %d returned error "
+ "%#x\n", rescan? "Re-scan" : "Reset",
+ bus_result->path_id,
+ ccb.ccb_h.status & CAM_STATUS_MASK);
+ retval = 1;
+ }
+ }
+ } while ((matchccb.ccb_h.status == CAM_REQ_CMP)
+ && (matchccb.cdm.status == CAM_DEV_MATCH_MORE));
+
+bailout:
+
+ if (fd != -1)
+ close(fd);
+
+ if (matchccb.cdm.patterns != NULL)
+ free(matchccb.cdm.patterns);
+ if (matchccb.cdm.matches != NULL)
+ free(matchccb.cdm.matches);
+
+ return(retval);
+}
+
+static int
+scanlun_or_reset_dev(int bus, int target, int lun, int scan)
+{
+ union ccb ccb;
+ struct cam_device *device;
+ int fd;
+
+ device = NULL;
+
+ if (bus < 0) {
+ warnx("invalid bus number %d", bus);
+ return(1);
+ }
+
+ if (target < 0) {
+ warnx("invalid target number %d", target);
+ return(1);
+ }
+
+ if (lun < 0) {
+ warnx("invalid lun number %d", lun);
+ return(1);
+ }
+
+ fd = -1;
+
+ bzero(&ccb, sizeof(union ccb));
+
+ if (scan) {
+ if ((fd = open(XPT_DEVICE, O_RDWR)) < 0) {
+ warnx("error opening tranport layer device %s\n",
+ XPT_DEVICE);
+ warn("%s", XPT_DEVICE);
+ return(1);
+ }
+ } else {
+ device = cam_open_btl(bus, target, lun, O_RDWR, NULL);
+ if (device == NULL) {
+ warnx("%s", cam_errbuf);
+ return(1);
+ }
+ }
+
+ ccb.ccb_h.func_code = (scan)? XPT_SCAN_LUN : XPT_RESET_DEV;
+ ccb.ccb_h.path_id = bus;
+ ccb.ccb_h.target_id = target;
+ ccb.ccb_h.target_lun = lun;
+ ccb.ccb_h.timeout = 5000;
+ ccb.crcn.flags = CAM_FLAG_NONE;
+
+ /* run this at a low priority */
+ ccb.ccb_h.pinfo.priority = 5;
+
+ if (scan) {
+ if (ioctl(fd, CAMIOCOMMAND, &ccb) < 0) {
+ warn("CAMIOCOMMAND ioctl failed");
+ close(fd);
+ return(1);
+ }
+ } else {
+ if (cam_send_ccb(device, &ccb) < 0) {
+ warn("error sending XPT_RESET_DEV CCB");
+ cam_close_device(device);
+ return(1);
+ }
+ }
+
+ if (scan)
+ close(fd);
+ else
+ cam_close_device(device);
+
+ /*
+ * An error code of CAM_BDR_SENT is normal for a BDR request.
+ */
+ if (((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
+ || ((!scan)
+ && ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_BDR_SENT))) {
+ fprintf(stdout, "%s of %d:%d:%d was successful\n",
+ scan? "Re-scan" : "Reset", bus, target, lun);
+ return(0);
+ } else {
+ fprintf(stdout, "%s of %d:%d:%d returned error %#x\n",
+ scan? "Re-scan" : "Reset", bus, target, lun,
+ ccb.ccb_h.status & CAM_STATUS_MASK);
+ return(1);
+ }
+}
+
+#ifndef MINIMALISTIC
+static int
+readdefects(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int retry_count, int timeout)
+{
+ union ccb *ccb = NULL;
+ struct scsi_read_defect_data_10 *rdd_cdb;
+ u_int8_t *defect_list = NULL;
+ u_int32_t dlist_length = 65000;
+ u_int32_t returned_length = 0;
+ u_int32_t num_returned = 0;
+ u_int8_t returned_format;
+ unsigned int i;
+ int c, error = 0;
+ int lists_specified = 0;
+
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch(c){
+ case 'f':
+ {
+ char *tstr;
+ tstr = optarg;
+ while (isspace(*tstr) && (*tstr != '\0'))
+ tstr++;
+ if (strcmp(tstr, "block") == 0)
+ arglist |= CAM_ARG_FORMAT_BLOCK;
+ else if (strcmp(tstr, "bfi") == 0)
+ arglist |= CAM_ARG_FORMAT_BFI;
+ else if (strcmp(tstr, "phys") == 0)
+ arglist |= CAM_ARG_FORMAT_PHYS;
+ else {
+ error = 1;
+ warnx("invalid defect format %s", tstr);
+ goto defect_bailout;
+ }
+ break;
+ }
+ case 'G':
+ arglist |= CAM_ARG_GLIST;
+ break;
+ case 'P':
+ arglist |= CAM_ARG_PLIST;
+ break;
+ default:
+ break;
+ }
+ }
+
+ ccb = cam_getccb(device);
+
+ /*
+ * Hopefully 65000 bytes is enough to hold the defect list. If it
+ * isn't, the disk is probably dead already. We'd have to go with
+ * 12 byte command (i.e. alloc_length is 32 bits instead of 16)
+ * to hold them all.
+ */
+ defect_list = malloc(dlist_length);
+ if (defect_list == NULL) {
+ warnx("can't malloc memory for defect list");
+ error = 1;
+ goto defect_bailout;
+ }
+
+ rdd_cdb =(struct scsi_read_defect_data_10 *)&ccb->csio.cdb_io.cdb_bytes;
+
+ /*
+ * cam_getccb() zeros the CCB header only. So we need to zero the
+ * payload portion of the ccb.
+ */
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+ cam_fill_csio(&ccb->csio,
+ /*retries*/ retry_count,
+ /*cbfcnp*/ NULL,
+ /*flags*/ CAM_DIR_IN | ((arglist & CAM_ARG_ERR_RECOVER) ?
+ CAM_PASS_ERR_RECOVER : 0),
+ /*tag_action*/ MSG_SIMPLE_Q_TAG,
+ /*data_ptr*/ defect_list,
+ /*dxfer_len*/ dlist_length,
+ /*sense_len*/ SSD_FULL_SIZE,
+ /*cdb_len*/ sizeof(struct scsi_read_defect_data_10),
+ /*timeout*/ timeout ? timeout : 5000);
+
+ rdd_cdb->opcode = READ_DEFECT_DATA_10;
+ if (arglist & CAM_ARG_FORMAT_BLOCK)
+ rdd_cdb->format = SRDD10_BLOCK_FORMAT;
+ else if (arglist & CAM_ARG_FORMAT_BFI)
+ rdd_cdb->format = SRDD10_BYTES_FROM_INDEX_FORMAT;
+ else if (arglist & CAM_ARG_FORMAT_PHYS)
+ rdd_cdb->format = SRDD10_PHYSICAL_SECTOR_FORMAT;
+ else {
+ error = 1;
+ warnx("no defect list format specified");
+ goto defect_bailout;
+ }
+ if (arglist & CAM_ARG_PLIST) {
+ rdd_cdb->format |= SRDD10_PLIST;
+ lists_specified++;
+ }
+
+ if (arglist & CAM_ARG_GLIST) {
+ rdd_cdb->format |= SRDD10_GLIST;
+ lists_specified++;
+ }
+
+ scsi_ulto2b(dlist_length, rdd_cdb->alloc_length);
+
+ /* Disable freezing the device queue */
+ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ perror("error reading defect list");
+
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+
+ error = 1;
+ goto defect_bailout;
+ }
+
+ returned_length = scsi_2btoul(((struct
+ scsi_read_defect_data_hdr_10 *)defect_list)->length);
+
+ returned_format = ((struct scsi_read_defect_data_hdr_10 *)
+ defect_list)->format;
+
+ if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR)
+ && (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND)
+ && ((ccb->ccb_h.status & CAM_AUTOSNS_VALID) != 0)) {
+ struct scsi_sense_data *sense;
+ int error_code, sense_key, asc, ascq;
+
+ sense = &ccb->csio.sense_data;
+ scsi_extract_sense(sense, &error_code, &sense_key, &asc, &ascq);
+
+ /*
+ * According to the SCSI spec, if the disk doesn't support
+ * the requested format, it will generally return a sense
+ * key of RECOVERED ERROR, and an additional sense code
+ * of "DEFECT LIST NOT FOUND". So, we check for that, and
+ * also check to make sure that the returned length is
+ * greater than 0, and then print out whatever format the
+ * disk gave us.
+ */
+ if ((sense_key == SSD_KEY_RECOVERED_ERROR)
+ && (asc == 0x1c) && (ascq == 0x00)
+ && (returned_length > 0)) {
+ warnx("requested defect format not available");
+ switch(returned_format & SRDDH10_DLIST_FORMAT_MASK) {
+ case SRDD10_BLOCK_FORMAT:
+ warnx("Device returned block format");
+ break;
+ case SRDD10_BYTES_FROM_INDEX_FORMAT:
+ warnx("Device returned bytes from index"
+ " format");
+ break;
+ case SRDD10_PHYSICAL_SECTOR_FORMAT:
+ warnx("Device returned physical sector format");
+ break;
+ default:
+ error = 1;
+ warnx("Device returned unknown defect"
+ " data format %#x", returned_format);
+ goto defect_bailout;
+ break; /* NOTREACHED */
+ }
+ } else {
+ error = 1;
+ warnx("Error returned from read defect data command");
+ if (arglist & CAM_ARG_VERBOSE)
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ goto defect_bailout;
+ }
+ } else if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ error = 1;
+ warnx("Error returned from read defect data command");
+ if (arglist & CAM_ARG_VERBOSE)
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ goto defect_bailout;
+ }
+
+ /*
+ * XXX KDM I should probably clean up the printout format for the
+ * disk defects.
+ */
+ switch (returned_format & SRDDH10_DLIST_FORMAT_MASK){
+ case SRDDH10_PHYSICAL_SECTOR_FORMAT:
+ {
+ struct scsi_defect_desc_phys_sector *dlist;
+
+ dlist = (struct scsi_defect_desc_phys_sector *)
+ (defect_list +
+ sizeof(struct scsi_read_defect_data_hdr_10));
+
+ num_returned = returned_length /
+ sizeof(struct scsi_defect_desc_phys_sector);
+
+ fprintf(stderr, "Got %d defect", num_returned);
+
+ if ((lists_specified == 0) || (num_returned == 0)) {
+ fprintf(stderr, "s.\n");
+ break;
+ } else if (num_returned == 1)
+ fprintf(stderr, ":\n");
+ else
+ fprintf(stderr, "s:\n");
+
+ for (i = 0; i < num_returned; i++) {
+ fprintf(stdout, "%d:%d:%d\n",
+ scsi_3btoul(dlist[i].cylinder),
+ dlist[i].head,
+ scsi_4btoul(dlist[i].sector));
+ }
+ break;
+ }
+ case SRDDH10_BYTES_FROM_INDEX_FORMAT:
+ {
+ struct scsi_defect_desc_bytes_from_index *dlist;
+
+ dlist = (struct scsi_defect_desc_bytes_from_index *)
+ (defect_list +
+ sizeof(struct scsi_read_defect_data_hdr_10));
+
+ num_returned = returned_length /
+ sizeof(struct scsi_defect_desc_bytes_from_index);
+
+ fprintf(stderr, "Got %d defect", num_returned);
+
+ if ((lists_specified == 0) || (num_returned == 0)) {
+ fprintf(stderr, "s.\n");
+ break;
+ } else if (num_returned == 1)
+ fprintf(stderr, ":\n");
+ else
+ fprintf(stderr, "s:\n");
+
+ for (i = 0; i < num_returned; i++) {
+ fprintf(stdout, "%d:%d:%d\n",
+ scsi_3btoul(dlist[i].cylinder),
+ dlist[i].head,
+ scsi_4btoul(dlist[i].bytes_from_index));
+ }
+ break;
+ }
+ case SRDDH10_BLOCK_FORMAT:
+ {
+ struct scsi_defect_desc_block *dlist;
+
+ dlist = (struct scsi_defect_desc_block *)(defect_list +
+ sizeof(struct scsi_read_defect_data_hdr_10));
+
+ num_returned = returned_length /
+ sizeof(struct scsi_defect_desc_block);
+
+ fprintf(stderr, "Got %d defect", num_returned);
+
+ if ((lists_specified == 0) || (num_returned == 0)) {
+ fprintf(stderr, "s.\n");
+ break;
+ } else if (num_returned == 1)
+ fprintf(stderr, ":\n");
+ else
+ fprintf(stderr, "s:\n");
+
+ for (i = 0; i < num_returned; i++)
+ fprintf(stdout, "%u\n",
+ scsi_4btoul(dlist[i].address));
+ break;
+ }
+ default:
+ fprintf(stderr, "Unknown defect format %d\n",
+ returned_format & SRDDH10_DLIST_FORMAT_MASK);
+ error = 1;
+ break;
+ }
+defect_bailout:
+
+ if (defect_list != NULL)
+ free(defect_list);
+
+ if (ccb != NULL)
+ cam_freeccb(ccb);
+
+ return(error);
+}
+#endif /* MINIMALISTIC */
+
+#if 0
+void
+reassignblocks(struct cam_device *device, u_int32_t *blocks, int num_blocks)
+{
+ union ccb *ccb;
+
+ ccb = cam_getccb(device);
+
+ cam_freeccb(ccb);
+}
+#endif
+
+#ifndef MINIMALISTIC
+void
+mode_sense(struct cam_device *device, int mode_page, int page_control,
+ int dbd, int retry_count, int timeout, u_int8_t *data, int datalen)
+{
+ union ccb *ccb;
+ int retval;
+
+ ccb = cam_getccb(device);
+
+ if (ccb == NULL)
+ errx(1, "mode_sense: couldn't allocate CCB");
+
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+ scsi_mode_sense(&ccb->csio,
+ /* retries */ retry_count,
+ /* cbfcnp */ NULL,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* dbd */ dbd,
+ /* page_code */ page_control << 6,
+ /* page */ mode_page,
+ /* param_buf */ data,
+ /* param_len */ datalen,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ timeout ? timeout : 5000);
+
+ if (arglist & CAM_ARG_ERR_RECOVER)
+ ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+ /* Disable freezing the device queue */
+ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ if (((retval = cam_send_ccb(device, ccb)) < 0)
+ || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ cam_freeccb(ccb);
+ cam_close_device(device);
+ if (retval < 0)
+ err(1, "error sending mode sense command");
+ else
+ errx(1, "error sending mode sense command");
+ }
+
+ cam_freeccb(ccb);
+}
+
+void
+mode_select(struct cam_device *device, int save_pages, int retry_count,
+ int timeout, u_int8_t *data, int datalen)
+{
+ union ccb *ccb;
+ int retval;
+
+ ccb = cam_getccb(device);
+
+ if (ccb == NULL)
+ errx(1, "mode_select: couldn't allocate CCB");
+
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+ scsi_mode_select(&ccb->csio,
+ /* retries */ retry_count,
+ /* cbfcnp */ NULL,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* scsi_page_fmt */ 1,
+ /* save_pages */ save_pages,
+ /* param_buf */ data,
+ /* param_len */ datalen,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ timeout ? timeout : 5000);
+
+ if (arglist & CAM_ARG_ERR_RECOVER)
+ ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+ /* Disable freezing the device queue */
+ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ if (((retval = cam_send_ccb(device, ccb)) < 0)
+ || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ cam_freeccb(ccb);
+ cam_close_device(device);
+
+ if (retval < 0)
+ err(1, "error sending mode select command");
+ else
+ errx(1, "error sending mode select command");
+
+ }
+
+ cam_freeccb(ccb);
+}
+
+void
+modepage(struct cam_device *device, int argc, char **argv, char *combinedopt,
+ int retry_count, int timeout)
+{
+ int c, mode_page = -1, page_control = 0;
+ int binary = 0, list = 0;
+
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch(c) {
+ case 'b':
+ binary = 1;
+ break;
+ case 'd':
+ arglist |= CAM_ARG_DBD;
+ break;
+ case 'e':
+ arglist |= CAM_ARG_MODE_EDIT;
+ break;
+ case 'l':
+ list = 1;
+ break;
+ case 'm':
+ mode_page = strtol(optarg, NULL, 0);
+ if (mode_page < 0)
+ errx(1, "invalid mode page %d", mode_page);
+ break;
+ case 'P':
+ page_control = strtol(optarg, NULL, 0);
+ if ((page_control < 0) || (page_control > 3))
+ errx(1, "invalid page control field %d",
+ page_control);
+ arglist |= CAM_ARG_PAGE_CNTL;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (mode_page == -1 && list == 0)
+ errx(1, "you must specify a mode page!");
+
+ if (list) {
+ mode_list(device, page_control, arglist & CAM_ARG_DBD,
+ retry_count, timeout);
+ } else {
+ mode_edit(device, mode_page, page_control,
+ arglist & CAM_ARG_DBD, arglist & CAM_ARG_MODE_EDIT, binary,
+ retry_count, timeout);
+ }
+}
+
+static int
+scsicmd(struct cam_device *device, int argc, char **argv, char *combinedopt,
+ int retry_count, int timeout)
+{
+ union ccb *ccb;
+ u_int32_t flags = CAM_DIR_NONE;
+ u_int8_t *data_ptr = NULL;
+ u_int8_t cdb[20];
+ struct get_hook hook;
+ int c, data_bytes = 0;
+ int cdb_len = 0;
+ char *datastr = NULL, *tstr;
+ int error = 0;
+ int fd_data = 0;
+ int retval;
+
+ ccb = cam_getccb(device);
+
+ if (ccb == NULL) {
+ warnx("scsicmd: error allocating ccb");
+ return(1);
+ }
+
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch(c) {
+ case 'c':
+ tstr = optarg;
+ while (isspace(*tstr) && (*tstr != '\0'))
+ tstr++;
+ hook.argc = argc - optind;
+ hook.argv = argv + optind;
+ hook.got = 0;
+ cdb_len = buff_encode_visit(cdb, sizeof(cdb), tstr,
+ iget, &hook);
+ /*
+ * Increment optind by the number of arguments the
+ * encoding routine processed. After each call to
+ * getopt(3), optind points to the argument that
+ * getopt should process _next_. In this case,
+ * that means it points to the first command string
+ * argument, if there is one. Once we increment
+ * this, it should point to either the next command
+ * line argument, or it should be past the end of
+ * the list.
+ */
+ optind += hook.got;
+ break;
+ case 'i':
+ if (arglist & CAM_ARG_CMD_OUT) {
+ warnx("command must either be "
+ "read or write, not both");
+ error = 1;
+ goto scsicmd_bailout;
+ }
+ arglist |= CAM_ARG_CMD_IN;
+ flags = CAM_DIR_IN;
+ data_bytes = strtol(optarg, NULL, 0);
+ if (data_bytes <= 0) {
+ warnx("invalid number of input bytes %d",
+ data_bytes);
+ error = 1;
+ goto scsicmd_bailout;
+ }
+ hook.argc = argc - optind;
+ hook.argv = argv + optind;
+ hook.got = 0;
+ optind++;
+ datastr = cget(&hook, NULL);
+ /*
+ * If the user supplied "-" instead of a format, he
+ * wants the data to be written to stdout.
+ */
+ if ((datastr != NULL)
+ && (datastr[0] == '-'))
+ fd_data = 1;
+
+ data_ptr = (u_int8_t *)malloc(data_bytes);
+ if (data_ptr == NULL) {
+ warnx("can't malloc memory for data_ptr");
+ error = 1;
+ goto scsicmd_bailout;
+ }
+ break;
+ case 'o':
+ if (arglist & CAM_ARG_CMD_IN) {
+ warnx("command must either be "
+ "read or write, not both");
+ error = 1;
+ goto scsicmd_bailout;
+ }
+ arglist |= CAM_ARG_CMD_OUT;
+ flags = CAM_DIR_OUT;
+ data_bytes = strtol(optarg, NULL, 0);
+ if (data_bytes <= 0) {
+ warnx("invalid number of output bytes %d",
+ data_bytes);
+ error = 1;
+ goto scsicmd_bailout;
+ }
+ hook.argc = argc - optind;
+ hook.argv = argv + optind;
+ hook.got = 0;
+ datastr = cget(&hook, NULL);
+ data_ptr = (u_int8_t *)malloc(data_bytes);
+ if (data_ptr == NULL) {
+ warnx("can't malloc memory for data_ptr");
+ error = 1;
+ goto scsicmd_bailout;
+ }
+ /*
+ * If the user supplied "-" instead of a format, he
+ * wants the data to be read from stdin.
+ */
+ if ((datastr != NULL)
+ && (datastr[0] == '-'))
+ fd_data = 1;
+ else
+ buff_encode_visit(data_ptr, data_bytes, datastr,
+ iget, &hook);
+ optind += hook.got;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * If fd_data is set, and we're writing to the device, we need to
+ * read the data the user wants written from stdin.
+ */
+ if ((fd_data == 1) && (arglist & CAM_ARG_CMD_OUT)) {
+ ssize_t amt_read;
+ int amt_to_read = data_bytes;
+ u_int8_t *buf_ptr = data_ptr;
+
+ for (amt_read = 0; amt_to_read > 0;
+ amt_read = read(STDIN_FILENO, buf_ptr, amt_to_read)) {
+ if (amt_read == -1) {
+ warn("error reading data from stdin");
+ error = 1;
+ goto scsicmd_bailout;
+ }
+ amt_to_read -= amt_read;
+ buf_ptr += amt_read;
+ }
+ }
+
+ if (arglist & CAM_ARG_ERR_RECOVER)
+ flags |= CAM_PASS_ERR_RECOVER;
+
+ /* Disable freezing the device queue */
+ flags |= CAM_DEV_QFRZDIS;
+
+ /*
+ * This is taken from the SCSI-3 draft spec.
+ * (T10/1157D revision 0.3)
+ * The top 3 bits of an opcode are the group code. The next 5 bits
+ * are the command code.
+ * Group 0: six byte commands
+ * Group 1: ten byte commands
+ * Group 2: ten byte commands
+ * Group 3: reserved
+ * Group 4: sixteen byte commands
+ * Group 5: twelve byte commands
+ * Group 6: vendor specific
+ * Group 7: vendor specific
+ */
+ switch((cdb[0] >> 5) & 0x7) {
+ case 0:
+ cdb_len = 6;
+ break;
+ case 1:
+ case 2:
+ cdb_len = 10;
+ break;
+ case 3:
+ case 6:
+ case 7:
+ /* computed by buff_encode_visit */
+ break;
+ case 4:
+ cdb_len = 16;
+ break;
+ case 5:
+ cdb_len = 12;
+ break;
+ }
+
+ /*
+ * We should probably use csio_build_visit or something like that
+ * here, but it's easier to encode arguments as you go. The
+ * alternative would be skipping the CDB argument and then encoding
+ * it here, since we've got the data buffer argument by now.
+ */
+ bcopy(cdb, &ccb->csio.cdb_io.cdb_bytes, cdb_len);
+
+ cam_fill_csio(&ccb->csio,
+ /*retries*/ retry_count,
+ /*cbfcnp*/ NULL,
+ /*flags*/ flags,
+ /*tag_action*/ MSG_SIMPLE_Q_TAG,
+ /*data_ptr*/ data_ptr,
+ /*dxfer_len*/ data_bytes,
+ /*sense_len*/ SSD_FULL_SIZE,
+ /*cdb_len*/ cdb_len,
+ /*timeout*/ timeout ? timeout : 5000);
+
+ if (((retval = cam_send_ccb(device, ccb)) < 0)
+ || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
+ if (retval < 0)
+ warn("error sending command");
+ else
+ warnx("error sending command");
+
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+
+ error = 1;
+ goto scsicmd_bailout;
+ }
+
+
+ if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
+ && (arglist & CAM_ARG_CMD_IN)
+ && (data_bytes > 0)) {
+ if (fd_data == 0) {
+ buff_decode_visit(data_ptr, data_bytes, datastr,
+ arg_put, NULL);
+ fprintf(stdout, "\n");
+ } else {
+ ssize_t amt_written;
+ int amt_to_write = data_bytes;
+ u_int8_t *buf_ptr = data_ptr;
+
+ for (amt_written = 0; (amt_to_write > 0) &&
+ (amt_written =write(1, buf_ptr,amt_to_write))> 0;){
+ amt_to_write -= amt_written;
+ buf_ptr += amt_written;
+ }
+ if (amt_written == -1) {
+ warn("error writing data to stdout");
+ error = 1;
+ goto scsicmd_bailout;
+ } else if ((amt_written == 0)
+ && (amt_to_write > 0)) {
+ warnx("only wrote %u bytes out of %u",
+ data_bytes - amt_to_write, data_bytes);
+ }
+ }
+ }
+
+scsicmd_bailout:
+
+ if ((data_bytes > 0) && (data_ptr != NULL))
+ free(data_ptr);
+
+ cam_freeccb(ccb);
+
+ return(error);
+}
+
+static int
+camdebug(int argc, char **argv, char *combinedopt)
+{
+ int c, fd;
+ int bus = -1, target = -1, lun = -1;
+ char *tstr, *tmpstr = NULL;
+ union ccb ccb;
+ int error = 0;
+
+ bzero(&ccb, sizeof(union ccb));
+
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch(c) {
+ case 'I':
+ arglist |= CAM_ARG_DEBUG_INFO;
+ ccb.cdbg.flags |= CAM_DEBUG_INFO;
+ break;
+ case 'P':
+ arglist |= CAM_ARG_DEBUG_PERIPH;
+ ccb.cdbg.flags |= CAM_DEBUG_PERIPH;
+ break;
+ case 'S':
+ arglist |= CAM_ARG_DEBUG_SUBTRACE;
+ ccb.cdbg.flags |= CAM_DEBUG_SUBTRACE;
+ break;
+ case 'T':
+ arglist |= CAM_ARG_DEBUG_TRACE;
+ ccb.cdbg.flags |= CAM_DEBUG_TRACE;
+ break;
+ case 'X':
+ arglist |= CAM_ARG_DEBUG_XPT;
+ ccb.cdbg.flags |= CAM_DEBUG_XPT;
+ break;
+ case 'c':
+ arglist |= CAM_ARG_DEBUG_CDB;
+ ccb.cdbg.flags |= CAM_DEBUG_CDB;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((fd = open(XPT_DEVICE, O_RDWR)) < 0) {
+ warnx("error opening transport layer device %s", XPT_DEVICE);
+ warn("%s", XPT_DEVICE);
+ return(1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc <= 0) {
+ warnx("you must specify \"off\", \"all\" or a bus,");
+ warnx("bus:target, or bus:target:lun");
+ close(fd);
+ return(1);
+ }
+
+ tstr = *argv;
+
+ while (isspace(*tstr) && (*tstr != '\0'))
+ tstr++;
+
+ if (strncmp(tstr, "off", 3) == 0) {
+ ccb.cdbg.flags = CAM_DEBUG_NONE;
+ arglist &= ~(CAM_ARG_DEBUG_INFO|CAM_ARG_DEBUG_PERIPH|
+ CAM_ARG_DEBUG_TRACE|CAM_ARG_DEBUG_SUBTRACE|
+ CAM_ARG_DEBUG_XPT);
+ } else if (strncmp(tstr, "all", 3) != 0) {
+ tmpstr = (char *)strtok(tstr, ":");
+ if ((tmpstr != NULL) && (*tmpstr != '\0')){
+ bus = strtol(tmpstr, NULL, 0);
+ arglist |= CAM_ARG_BUS;
+ tmpstr = (char *)strtok(NULL, ":");
+ if ((tmpstr != NULL) && (*tmpstr != '\0')){
+ target = strtol(tmpstr, NULL, 0);
+ arglist |= CAM_ARG_TARGET;
+ tmpstr = (char *)strtok(NULL, ":");
+ if ((tmpstr != NULL) && (*tmpstr != '\0')){
+ lun = strtol(tmpstr, NULL, 0);
+ arglist |= CAM_ARG_LUN;
+ }
+ }
+ } else {
+ error = 1;
+ warnx("you must specify \"all\", \"off\", or a bus,");
+ warnx("bus:target, or bus:target:lun to debug");
+ }
+ }
+
+ if (error == 0) {
+
+ ccb.ccb_h.func_code = XPT_DEBUG;
+ ccb.ccb_h.path_id = bus;
+ ccb.ccb_h.target_id = target;
+ ccb.ccb_h.target_lun = lun;
+
+ if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
+ warn("CAMIOCOMMAND ioctl failed");
+ error = 1;
+ }
+
+ if (error == 0) {
+ if ((ccb.ccb_h.status & CAM_STATUS_MASK) ==
+ CAM_FUNC_NOTAVAIL) {
+ warnx("CAM debugging not available");
+ warnx("you need to put options CAMDEBUG in"
+ " your kernel config file!");
+ error = 1;
+ } else if ((ccb.ccb_h.status & CAM_STATUS_MASK) !=
+ CAM_REQ_CMP) {
+ warnx("XPT_DEBUG CCB failed with status %#x",
+ ccb.ccb_h.status);
+ error = 1;
+ } else {
+ if (ccb.cdbg.flags == CAM_DEBUG_NONE) {
+ fprintf(stderr,
+ "Debugging turned off\n");
+ } else {
+ fprintf(stderr,
+ "Debugging enabled for "
+ "%d:%d:%d\n",
+ bus, target, lun);
+ }
+ }
+ }
+ close(fd);
+ }
+
+ return(error);
+}
+
+static int
+tagcontrol(struct cam_device *device, int argc, char **argv,
+ char *combinedopt)
+{
+ int c;
+ union ccb *ccb;
+ int numtags = -1;
+ int retval = 0;
+ int quiet = 0;
+ char pathstr[1024];
+
+ ccb = cam_getccb(device);
+
+ if (ccb == NULL) {
+ warnx("tagcontrol: error allocating ccb");
+ return(1);
+ }
+
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch(c) {
+ case 'N':
+ numtags = strtol(optarg, NULL, 0);
+ if (numtags < 0) {
+ warnx("tag count %d is < 0", numtags);
+ retval = 1;
+ goto tagcontrol_bailout;
+ }
+ break;
+ case 'q':
+ quiet++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ cam_path_string(device, pathstr, sizeof(pathstr));
+
+ if (numtags >= 0) {
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_relsim) - sizeof(struct ccb_hdr));
+ ccb->ccb_h.func_code = XPT_REL_SIMQ;
+ ccb->crs.release_flags = RELSIM_ADJUST_OPENINGS;
+ ccb->crs.openings = numtags;
+
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ perror("error sending XPT_REL_SIMQ CCB");
+ retval = 1;
+ goto tagcontrol_bailout;
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ warnx("XPT_REL_SIMQ CCB failed");
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ retval = 1;
+ goto tagcontrol_bailout;
+ }
+
+
+ if (quiet == 0)
+ fprintf(stdout, "%stagged openings now %d\n",
+ pathstr, ccb->crs.openings);
+ }
+
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_getdevstats) - sizeof(struct ccb_hdr));
+
+ ccb->ccb_h.func_code = XPT_GDEV_STATS;
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ perror("error sending XPT_GDEV_STATS CCB");
+ retval = 1;
+ goto tagcontrol_bailout;
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ warnx("XPT_GDEV_STATS CCB failed");
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ retval = 1;
+ goto tagcontrol_bailout;
+ }
+
+ if (arglist & CAM_ARG_VERBOSE) {
+ fprintf(stdout, "%s", pathstr);
+ fprintf(stdout, "dev_openings %d\n", ccb->cgds.dev_openings);
+ fprintf(stdout, "%s", pathstr);
+ fprintf(stdout, "dev_active %d\n", ccb->cgds.dev_active);
+ fprintf(stdout, "%s", pathstr);
+ fprintf(stdout, "devq_openings %d\n", ccb->cgds.devq_openings);
+ fprintf(stdout, "%s", pathstr);
+ fprintf(stdout, "devq_queued %d\n", ccb->cgds.devq_queued);
+ fprintf(stdout, "%s", pathstr);
+ fprintf(stdout, "held %d\n", ccb->cgds.held);
+ fprintf(stdout, "%s", pathstr);
+ fprintf(stdout, "mintags %d\n", ccb->cgds.mintags);
+ fprintf(stdout, "%s", pathstr);
+ fprintf(stdout, "maxtags %d\n", ccb->cgds.maxtags);
+ } else {
+ if (quiet == 0) {
+ fprintf(stdout, "%s", pathstr);
+ fprintf(stdout, "device openings: ");
+ }
+ fprintf(stdout, "%d\n", ccb->cgds.dev_openings +
+ ccb->cgds.dev_active);
+ }
+
+tagcontrol_bailout:
+
+ cam_freeccb(ccb);
+ return(retval);
+}
+
+static void
+cts_print(struct cam_device *device, struct ccb_trans_settings *cts)
+{
+ char pathstr[1024];
+
+ cam_path_string(device, pathstr, sizeof(pathstr));
+
+ if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) {
+
+ fprintf(stdout, "%ssync parameter: %d\n", pathstr,
+ cts->sync_period);
+
+ if (cts->sync_offset != 0) {
+ u_int freq;
+
+ freq = scsi_calc_syncsrate(cts->sync_period);
+ fprintf(stdout, "%sfrequency: %d.%03dMHz\n", pathstr,
+ freq / 1000, freq % 1000);
+ }
+ }
+
+ if (cts->valid & CCB_TRANS_SYNC_OFFSET_VALID)
+ fprintf(stdout, "%soffset: %d\n", pathstr, cts->sync_offset);
+
+ if (cts->valid & CCB_TRANS_BUS_WIDTH_VALID)
+ fprintf(stdout, "%sbus width: %d bits\n", pathstr,
+ (0x01 << cts->bus_width) * 8);
+
+ if (cts->valid & CCB_TRANS_DISC_VALID)
+ fprintf(stdout, "%sdisconnection is %s\n", pathstr,
+ (cts->flags & CCB_TRANS_DISC_ENB) ? "enabled" :
+ "disabled");
+
+ if (cts->valid & CCB_TRANS_TQ_VALID)
+ fprintf(stdout, "%stagged queueing is %s\n", pathstr,
+ (cts->flags & CCB_TRANS_TAG_ENB) ? "enabled" :
+ "disabled");
+
+}
+
+/*
+ * Get a path inquiry CCB for the specified device.
+ */
+static int
+get_cpi(struct cam_device *device, struct ccb_pathinq *cpi)
+{
+ union ccb *ccb;
+ int retval = 0;
+
+ ccb = cam_getccb(device);
+
+ if (ccb == NULL) {
+ warnx("get_cpi: couldn't allocate CCB");
+ return(1);
+ }
+
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_pathinq) - sizeof(struct ccb_hdr));
+
+ ccb->ccb_h.func_code = XPT_PATH_INQ;
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ warn("get_cpi: error sending Path Inquiry CCB");
+
+ if (arglist & CAM_ARG_VERBOSE)
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+
+ retval = 1;
+
+ goto get_cpi_bailout;
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+
+ if (arglist & CAM_ARG_VERBOSE)
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+
+ retval = 1;
+
+ goto get_cpi_bailout;
+ }
+
+ bcopy(&ccb->cpi, cpi, sizeof(struct ccb_pathinq));
+
+get_cpi_bailout:
+
+ cam_freeccb(ccb);
+
+ return(retval);
+}
+
+static void
+cpi_print(struct ccb_pathinq *cpi)
+{
+ char adapter_str[1024];
+ int i;
+
+ snprintf(adapter_str, sizeof(adapter_str),
+ "%s%d:", cpi->dev_name, cpi->unit_number);
+
+ fprintf(stdout, "%s SIM/HBA version: %d\n", adapter_str,
+ cpi->version_num);
+
+ for (i = 1; i < 0xff; i = i << 1) {
+ const char *str;
+
+ if ((i & cpi->hba_inquiry) == 0)
+ continue;
+
+ fprintf(stdout, "%s supports ", adapter_str);
+
+ switch(i) {
+ case PI_MDP_ABLE:
+ str = "MDP message";
+ break;
+ case PI_WIDE_32:
+ str = "32 bit wide SCSI";
+ break;
+ case PI_WIDE_16:
+ str = "16 bit wide SCSI";
+ break;
+ case PI_SDTR_ABLE:
+ str = "SDTR message";
+ break;
+ case PI_LINKED_CDB:
+ str = "linked CDBs";
+ break;
+ case PI_TAG_ABLE:
+ str = "tag queue messages";
+ break;
+ case PI_SOFT_RST:
+ str = "soft reset alternative";
+ break;
+ default:
+ str = "unknown PI bit set";
+ break;
+ }
+ fprintf(stdout, "%s\n", str);
+ }
+
+ for (i = 1; i < 0xff; i = i << 1) {
+ const char *str;
+
+ if ((i & cpi->hba_misc) == 0)
+ continue;
+
+ fprintf(stdout, "%s ", adapter_str);
+
+ switch(i) {
+ case PIM_SCANHILO:
+ str = "bus scans from high ID to low ID";
+ break;
+ case PIM_NOREMOVE:
+ str = "removable devices not included in scan";
+ break;
+ case PIM_NOINITIATOR:
+ str = "initiator role not supported";
+ break;
+ case PIM_NOBUSRESET:
+ str = "user has disabled initial BUS RESET or"
+ " controller is in target/mixed mode";
+ break;
+ default:
+ str = "unknown PIM bit set";
+ break;
+ }
+ fprintf(stdout, "%s\n", str);
+ }
+
+ for (i = 1; i < 0xff; i = i << 1) {
+ const char *str;
+
+ if ((i & cpi->target_sprt) == 0)
+ continue;
+
+ fprintf(stdout, "%s supports ", adapter_str);
+ switch(i) {
+ case PIT_PROCESSOR:
+ str = "target mode processor mode";
+ break;
+ case PIT_PHASE:
+ str = "target mode phase cog. mode";
+ break;
+ case PIT_DISCONNECT:
+ str = "disconnects in target mode";
+ break;
+ case PIT_TERM_IO:
+ str = "terminate I/O message in target mode";
+ break;
+ case PIT_GRP_6:
+ str = "group 6 commands in target mode";
+ break;
+ case PIT_GRP_7:
+ str = "group 7 commands in target mode";
+ break;
+ default:
+ str = "unknown PIT bit set";
+ break;
+ }
+
+ fprintf(stdout, "%s\n", str);
+ }
+ fprintf(stdout, "%s HBA engine count: %d\n", adapter_str,
+ cpi->hba_eng_cnt);
+ fprintf(stdout, "%s maximum target: %d\n", adapter_str,
+ cpi->max_target);
+ fprintf(stdout, "%s maximum LUN: %d\n", adapter_str,
+ cpi->max_lun);
+ fprintf(stdout, "%s highest path ID in subsystem: %d\n",
+ adapter_str, cpi->hpath_id);
+ fprintf(stdout, "%s initiator ID: %d\n", adapter_str,
+ cpi->initiator_id);
+ fprintf(stdout, "%s SIM vendor: %s\n", adapter_str, cpi->sim_vid);
+ fprintf(stdout, "%s HBA vendor: %s\n", adapter_str, cpi->hba_vid);
+ fprintf(stdout, "%s bus ID: %d\n", adapter_str, cpi->bus_id);
+ fprintf(stdout, "%s base transfer speed: ", adapter_str);
+ if (cpi->base_transfer_speed > 1000)
+ fprintf(stdout, "%d.%03dMB/sec\n",
+ cpi->base_transfer_speed / 1000,
+ cpi->base_transfer_speed % 1000);
+ else
+ fprintf(stdout, "%dKB/sec\n",
+ (cpi->base_transfer_speed % 1000) * 1000);
+}
+
+static int
+get_print_cts(struct cam_device *device, int user_settings, int quiet,
+ struct ccb_trans_settings *cts)
+{
+ int retval;
+ union ccb *ccb;
+
+ retval = 0;
+ ccb = cam_getccb(device);
+
+ if (ccb == NULL) {
+ warnx("get_print_cts: error allocating ccb");
+ return(1);
+ }
+
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_trans_settings) - sizeof(struct ccb_hdr));
+
+ ccb->ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
+
+ if (user_settings == 0)
+ ccb->cts.flags = CCB_TRANS_CURRENT_SETTINGS;
+ else
+ ccb->cts.flags = CCB_TRANS_USER_SETTINGS;
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ perror("error sending XPT_GET_TRAN_SETTINGS CCB");
+ if (arglist & CAM_ARG_VERBOSE)
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ retval = 1;
+ goto get_print_cts_bailout;
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ warnx("XPT_GET_TRANS_SETTINGS CCB failed");
+ if (arglist & CAM_ARG_VERBOSE)
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ retval = 1;
+ goto get_print_cts_bailout;
+ }
+
+ if (quiet == 0)
+ cts_print(device, &ccb->cts);
+
+ if (cts != NULL)
+ bcopy(&ccb->cts, cts, sizeof(struct ccb_trans_settings));
+
+get_print_cts_bailout:
+
+ cam_freeccb(ccb);
+
+ return(retval);
+}
+
+static int
+ratecontrol(struct cam_device *device, int retry_count, int timeout,
+ int argc, char **argv, char *combinedopt)
+{
+ int c;
+ union ccb *ccb;
+ int user_settings = 0;
+ int retval = 0;
+ int disc_enable = -1, tag_enable = -1;
+ int offset = -1;
+ double syncrate = -1;
+ int bus_width = -1;
+ int quiet = 0;
+ int change_settings = 0, send_tur = 0;
+ struct ccb_pathinq cpi;
+
+ ccb = cam_getccb(device);
+
+ if (ccb == NULL) {
+ warnx("ratecontrol: error allocating ccb");
+ return(1);
+ }
+
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch(c){
+ case 'a':
+ send_tur = 1;
+ break;
+ case 'c':
+ user_settings = 0;
+ break;
+ case 'D':
+ if (strncasecmp(optarg, "enable", 6) == 0)
+ disc_enable = 1;
+ else if (strncasecmp(optarg, "disable", 7) == 0)
+ disc_enable = 0;
+ else {
+ warnx("-D argument \"%s\" is unknown", optarg);
+ retval = 1;
+ goto ratecontrol_bailout;
+ }
+ change_settings = 1;
+ break;
+ case 'O':
+ offset = strtol(optarg, NULL, 0);
+ if (offset < 0) {
+ warnx("offset value %d is < 0", offset);
+ retval = 1;
+ goto ratecontrol_bailout;
+ }
+ change_settings = 1;
+ break;
+ case 'q':
+ quiet++;
+ break;
+ case 'R':
+ syncrate = atof(optarg);
+
+ if (syncrate < 0) {
+ warnx("sync rate %f is < 0", syncrate);
+ retval = 1;
+ goto ratecontrol_bailout;
+ }
+ change_settings = 1;
+ break;
+ case 'T':
+ if (strncasecmp(optarg, "enable", 6) == 0)
+ tag_enable = 1;
+ else if (strncasecmp(optarg, "disable", 7) == 0)
+ tag_enable = 0;
+ else {
+ warnx("-T argument \"%s\" is unknown", optarg);
+ retval = 1;
+ goto ratecontrol_bailout;
+ }
+ change_settings = 1;
+ break;
+ case 'U':
+ user_settings = 1;
+ break;
+ case 'W':
+ bus_width = strtol(optarg, NULL, 0);
+ if (bus_width < 0) {
+ warnx("bus width %d is < 0", bus_width);
+ retval = 1;
+ goto ratecontrol_bailout;
+ }
+ change_settings = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_pathinq) - sizeof(struct ccb_hdr));
+
+ /*
+ * Grab path inquiry information, so we can determine whether
+ * or not the initiator is capable of the things that the user
+ * requests.
+ */
+ ccb->ccb_h.func_code = XPT_PATH_INQ;
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ perror("error sending XPT_PATH_INQ CCB");
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ retval = 1;
+ goto ratecontrol_bailout;
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ warnx("XPT_PATH_INQ CCB failed");
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ retval = 1;
+ goto ratecontrol_bailout;
+ }
+
+ bcopy(&ccb->cpi, &cpi, sizeof(struct ccb_pathinq));
+
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_trans_settings) - sizeof(struct ccb_hdr));
+
+ if (quiet == 0)
+ fprintf(stdout, "Current Parameters:\n");
+
+ retval = get_print_cts(device, user_settings, quiet, &ccb->cts);
+
+ if (retval != 0)
+ goto ratecontrol_bailout;
+
+ if (arglist & CAM_ARG_VERBOSE)
+ cpi_print(&cpi);
+
+ if (change_settings) {
+ if (disc_enable != -1) {
+ ccb->cts.valid |= CCB_TRANS_DISC_VALID;
+ if (disc_enable == 0)
+ ccb->cts.flags &= ~CCB_TRANS_DISC_ENB;
+ else
+ ccb->cts.flags |= CCB_TRANS_DISC_ENB;
+ } else
+ ccb->cts.valid &= ~CCB_TRANS_DISC_VALID;
+
+ if (tag_enable != -1) {
+ if ((cpi.hba_inquiry & PI_TAG_ABLE) == 0) {
+ warnx("HBA does not support tagged queueing, "
+ "so you cannot modify tag settings");
+ retval = 1;
+ goto ratecontrol_bailout;
+ }
+
+ ccb->cts.valid |= CCB_TRANS_TQ_VALID;
+
+ if (tag_enable == 0)
+ ccb->cts.flags &= ~CCB_TRANS_TAG_ENB;
+ else
+ ccb->cts.flags |= CCB_TRANS_TAG_ENB;
+ } else
+ ccb->cts.valid &= ~CCB_TRANS_TQ_VALID;
+
+ if (offset != -1) {
+ if ((cpi.hba_inquiry & PI_SDTR_ABLE) == 0) {
+ warnx("HBA at %s%d is not cable of changing "
+ "offset", cpi.dev_name,
+ cpi.unit_number);
+ retval = 1;
+ goto ratecontrol_bailout;
+ }
+ ccb->cts.valid |= CCB_TRANS_SYNC_OFFSET_VALID;
+ ccb->cts.sync_offset = offset;
+ } else
+ ccb->cts.valid &= ~CCB_TRANS_SYNC_OFFSET_VALID;
+
+ if (syncrate != -1) {
+ int prelim_sync_period;
+ u_int freq;
+
+ if ((cpi.hba_inquiry & PI_SDTR_ABLE) == 0) {
+ warnx("HBA at %s%d is not cable of changing "
+ "transfer rates", cpi.dev_name,
+ cpi.unit_number);
+ retval = 1;
+ goto ratecontrol_bailout;
+ }
+
+ ccb->cts.valid |= CCB_TRANS_SYNC_RATE_VALID;
+
+ /*
+ * The sync rate the user gives us is in MHz.
+ * We need to translate it into KHz for this
+ * calculation.
+ */
+ syncrate *= 1000;
+
+ /*
+ * Next, we calculate a "preliminary" sync period
+ * in tenths of a nanosecond.
+ */
+ if (syncrate == 0)
+ prelim_sync_period = 0;
+ else
+ prelim_sync_period = 10000000 / syncrate;
+
+ ccb->cts.sync_period =
+ scsi_calc_syncparam(prelim_sync_period);
+
+ freq = scsi_calc_syncsrate(ccb->cts.sync_period);
+ } else
+ ccb->cts.valid &= ~CCB_TRANS_SYNC_RATE_VALID;
+
+ /*
+ * The bus_width argument goes like this:
+ * 0 == 8 bit
+ * 1 == 16 bit
+ * 2 == 32 bit
+ * Therefore, if you shift the number of bits given on the
+ * command line right by 4, you should get the correct
+ * number.
+ */
+ if (bus_width != -1) {
+
+ /*
+ * We might as well validate things here with a
+ * decipherable error message, rather than what
+ * will probably be an indecipherable error message
+ * by the time it gets back to us.
+ */
+ if ((bus_width == 16)
+ && ((cpi.hba_inquiry & PI_WIDE_16) == 0)) {
+ warnx("HBA does not support 16 bit bus width");
+ retval = 1;
+ goto ratecontrol_bailout;
+ } else if ((bus_width == 32)
+ && ((cpi.hba_inquiry & PI_WIDE_32) == 0)) {
+ warnx("HBA does not support 32 bit bus width");
+ retval = 1;
+ goto ratecontrol_bailout;
+ } else if ((bus_width != 8)
+ && (bus_width != 16)
+ && (bus_width != 32)) {
+ warnx("Invalid bus width %d", bus_width);
+ retval = 1;
+ goto ratecontrol_bailout;
+ }
+
+ ccb->cts.valid |= CCB_TRANS_BUS_WIDTH_VALID;
+ ccb->cts.bus_width = bus_width >> 4;
+ } else
+ ccb->cts.valid &= ~CCB_TRANS_BUS_WIDTH_VALID;
+
+ ccb->ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ perror("error sending XPT_SET_TRAN_SETTINGS CCB");
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ retval = 1;
+ goto ratecontrol_bailout;
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ warnx("XPT_SET_TRANS_SETTINGS CCB failed");
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ retval = 1;
+ goto ratecontrol_bailout;
+ }
+ }
+
+ if (send_tur) {
+ retval = testunitready(device, retry_count, timeout,
+ (arglist & CAM_ARG_VERBOSE) ? 0 : 1);
+
+ /*
+ * If the TUR didn't succeed, just bail.
+ */
+ if (retval != 0) {
+ if (quiet == 0)
+ fprintf(stderr, "Test Unit Ready failed\n");
+ goto ratecontrol_bailout;
+ }
+
+ /*
+ * If the user wants things quiet, there's no sense in
+ * getting the transfer settings, if we're not going
+ * to print them.
+ */
+ if (quiet != 0)
+ goto ratecontrol_bailout;
+
+ fprintf(stdout, "New Parameters:\n");
+ retval = get_print_cts(device, user_settings, 0, NULL);
+ }
+
+ratecontrol_bailout:
+
+ cam_freeccb(ccb);
+ return(retval);
+}
+
+static int
+scsiformat(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int retry_count, int timeout)
+{
+ union ccb *ccb;
+ int c;
+ int ycount = 0, quiet = 0;
+ int error = 0, response = 0, retval = 0;
+ int use_timeout = 10800 * 1000;
+ int immediate = 1;
+ struct format_defect_list_header fh;
+ u_int8_t *data_ptr = NULL;
+ u_int32_t dxfer_len = 0;
+ u_int8_t byte2 = 0;
+ int num_warnings = 0;
+ int reportonly = 0;
+
+ ccb = cam_getccb(device);
+
+ if (ccb == NULL) {
+ warnx("scsiformat: error allocating ccb");
+ return(1);
+ }
+
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch(c) {
+ case 'q':
+ quiet++;
+ break;
+ case 'r':
+ reportonly = 1;
+ break;
+ case 'w':
+ immediate = 0;
+ break;
+ case 'y':
+ ycount++;
+ break;
+ }
+ }
+
+ if (reportonly)
+ goto doreport;
+
+ if (quiet == 0) {
+ fprintf(stdout, "You are about to REMOVE ALL DATA from the "
+ "following device:\n");
+
+ error = scsidoinquiry(device, argc, argv, combinedopt,
+ retry_count, timeout);
+
+ if (error != 0) {
+ warnx("scsiformat: error sending inquiry");
+ goto scsiformat_bailout;
+ }
+ }
+
+ if (ycount == 0) {
+
+ do {
+ char str[1024];
+
+ fprintf(stdout, "Are you SURE you want to do "
+ "this? (yes/no) ");
+
+ if (fgets(str, sizeof(str), stdin) != NULL) {
+
+ if (strncasecmp(str, "yes", 3) == 0)
+ response = 1;
+ else if (strncasecmp(str, "no", 2) == 0)
+ response = -1;
+ else {
+ fprintf(stdout, "Please answer"
+ " \"yes\" or \"no\"\n");
+ }
+ }
+ } while (response == 0);
+
+ if (response == -1) {
+ error = 1;
+ goto scsiformat_bailout;
+ }
+ }
+
+ if (timeout != 0)
+ use_timeout = timeout;
+
+ if (quiet == 0) {
+ fprintf(stdout, "Current format timeout is %d seconds\n",
+ use_timeout / 1000);
+ }
+
+ /*
+ * If the user hasn't disabled questions and didn't specify a
+ * timeout on the command line, ask them if they want the current
+ * timeout.
+ */
+ if ((ycount == 0)
+ && (timeout == 0)) {
+ char str[1024];
+ int new_timeout = 0;
+
+ fprintf(stdout, "Enter new timeout in seconds or press\n"
+ "return to keep the current timeout [%d] ",
+ use_timeout / 1000);
+
+ if (fgets(str, sizeof(str), stdin) != NULL) {
+ if (str[0] != '\0')
+ new_timeout = atoi(str);
+ }
+
+ if (new_timeout != 0) {
+ use_timeout = new_timeout * 1000;
+ fprintf(stdout, "Using new timeout value %d\n",
+ use_timeout / 1000);
+ }
+ }
+
+ /*
+ * Keep this outside the if block below to silence any unused
+ * variable warnings.
+ */
+ bzero(&fh, sizeof(fh));
+
+ /*
+ * If we're in immediate mode, we've got to include the format
+ * header
+ */
+ if (immediate != 0) {
+ fh.byte2 = FU_DLH_IMMED;
+ data_ptr = (u_int8_t *)&fh;
+ dxfer_len = sizeof(fh);
+ byte2 = FU_FMT_DATA;
+ } else if (quiet == 0) {
+ fprintf(stdout, "Formatting...");
+ fflush(stdout);
+ }
+
+ scsi_format_unit(&ccb->csio,
+ /* retries */ retry_count,
+ /* cbfcnp */ NULL,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* byte2 */ byte2,
+ /* ileave */ 0,
+ /* data_ptr */ data_ptr,
+ /* dxfer_len */ dxfer_len,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ use_timeout);
+
+ /* Disable freezing the device queue */
+ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ if (arglist & CAM_ARG_ERR_RECOVER)
+ ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+ if (((retval = cam_send_ccb(device, ccb)) < 0)
+ || ((immediate == 0)
+ && ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP))) {
+ const char errstr[] = "error sending format command";
+
+ if (retval < 0)
+ warn(errstr);
+ else
+ warnx(errstr);
+
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ error = 1;
+ goto scsiformat_bailout;
+ }
+
+ /*
+ * If we ran in non-immediate mode, we already checked for errors
+ * above and printed out any necessary information. If we're in
+ * immediate mode, we need to loop through and get status
+ * information periodically.
+ */
+ if (immediate == 0) {
+ if (quiet == 0) {
+ fprintf(stdout, "Format Complete\n");
+ }
+ goto scsiformat_bailout;
+ }
+
+doreport:
+ do {
+ cam_status status;
+
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+ /*
+ * There's really no need to do error recovery or
+ * retries here, since we're just going to sit in a
+ * loop and wait for the device to finish formatting.
+ */
+ scsi_test_unit_ready(&ccb->csio,
+ /* retries */ 0,
+ /* cbfcnp */ NULL,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ 5000);
+
+ /* Disable freezing the device queue */
+ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ retval = cam_send_ccb(device, ccb);
+
+ /*
+ * If we get an error from the ioctl, bail out. SCSI
+ * errors are expected.
+ */
+ if (retval < 0) {
+ warn("error sending CAMIOCOMMAND ioctl");
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ error = 1;
+ goto scsiformat_bailout;
+ }
+
+ status = ccb->ccb_h.status & CAM_STATUS_MASK;
+
+ if ((status != CAM_REQ_CMP)
+ && (status == CAM_SCSI_STATUS_ERROR)
+ && ((ccb->ccb_h.status & CAM_AUTOSNS_VALID) != 0)) {
+ struct scsi_sense_data *sense;
+ int error_code, sense_key, asc, ascq;
+
+ sense = &ccb->csio.sense_data;
+ scsi_extract_sense(sense, &error_code, &sense_key,
+ &asc, &ascq);
+
+ /*
+ * According to the SCSI-2 and SCSI-3 specs, a
+ * drive that is in the middle of a format should
+ * return NOT READY with an ASC of "logical unit
+ * not ready, format in progress". The sense key
+ * specific bytes will then be a progress indicator.
+ */
+ if ((sense_key == SSD_KEY_NOT_READY)
+ && (asc == 0x04) && (ascq == 0x04)) {
+ if ((sense->extra_len >= 10)
+ && ((sense->sense_key_spec[0] &
+ SSD_SCS_VALID) != 0)
+ && (quiet == 0)) {
+ int val;
+ u_int64_t percentage;
+
+ val = scsi_2btoul(
+ &sense->sense_key_spec[1]);
+ percentage = 10000 * val;
+
+ fprintf(stdout,
+ "\rFormatting: %ju.%02u %% "
+ "(%d/%d) done",
+ (uintmax_t)(percentage /
+ (0x10000 * 100)),
+ (unsigned)((percentage /
+ 0x10000) % 100),
+ val, 0x10000);
+ fflush(stdout);
+ } else if ((quiet == 0)
+ && (++num_warnings <= 1)) {
+ warnx("Unexpected SCSI Sense Key "
+ "Specific value returned "
+ "during format:");
+ scsi_sense_print(device, &ccb->csio,
+ stderr);
+ warnx("Unable to print status "
+ "information, but format will "
+ "proceed.");
+ warnx("will exit when format is "
+ "complete");
+ }
+ sleep(1);
+ } else {
+ warnx("Unexpected SCSI error during format");
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ error = 1;
+ goto scsiformat_bailout;
+ }
+
+ } else if (status != CAM_REQ_CMP) {
+ warnx("Unexpected CAM status %#x", status);
+ if (arglist & CAM_ARG_VERBOSE)
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ error = 1;
+ goto scsiformat_bailout;
+ }
+
+ } while((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP);
+
+ if (quiet == 0)
+ fprintf(stdout, "\nFormat Complete\n");
+
+scsiformat_bailout:
+
+ cam_freeccb(ccb);
+
+ return(error);
+}
+#endif /* MINIMALISTIC */
+
+void
+usage(int verbose)
+{
+ fprintf(verbose ? stdout : stderr,
+"usage: camcontrol <command> [device id][generic args][command args]\n"
+" camcontrol devlist [-v]\n"
+#ifndef MINIMALISTIC
+" camcontrol periphlist [dev_id][-n dev_name] [-u unit]\n"
+" camcontrol tur [dev_id][generic args]\n"
+" camcontrol inquiry [dev_id][generic args] [-D] [-S] [-R]\n"
+" camcontrol start [dev_id][generic args]\n"
+" camcontrol stop [dev_id][generic args]\n"
+" camcontrol load [dev_id][generic args]\n"
+" camcontrol eject [dev_id][generic args]\n"
+#endif /* MINIMALISTIC */
+" camcontrol rescan <all | bus[:target:lun]>\n"
+" camcontrol reset <all | bus[:target:lun]>\n"
+#ifndef MINIMALISTIC
+" camcontrol defects [dev_id][generic args] <-f format> [-P][-G]\n"
+" camcontrol modepage [dev_id][generic args] <-m page | -l>\n"
+" [-P pagectl][-e | -b][-d]\n"
+" camcontrol cmd [dev_id][generic args] <-c cmd [args]>\n"
+" [-i len fmt|-o len fmt [args]]\n"
+" camcontrol debug [-I][-P][-T][-S][-X][-c]\n"
+" <all|bus[:target[:lun]]|off>\n"
+" camcontrol tags [dev_id][generic args] [-N tags] [-q] [-v]\n"
+" camcontrol negotiate [dev_id][generic args] [-a][-c]\n"
+" [-D <enable|disable>][-O offset][-q]\n"
+" [-R syncrate][-v][-T <enable|disable>]\n"
+" [-U][-W bus_width]\n"
+" camcontrol format [dev_id][generic args][-q][-r][-w][-y]\n"
+#endif /* MINIMALISTIC */
+" camcontrol help\n");
+ if (!verbose)
+ return;
+#ifndef MINIMALISTIC
+ fprintf(stdout,
+"Specify one of the following options:\n"
+"devlist list all CAM devices\n"
+"periphlist list all CAM peripheral drivers attached to a device\n"
+"tur send a test unit ready to the named device\n"
+"inquiry send a SCSI inquiry command to the named device\n"
+"start send a Start Unit command to the device\n"
+"stop send a Stop Unit command to the device\n"
+"load send a Start Unit command to the device with the load bit set\n"
+"eject send a Stop Unit command to the device with the eject bit set\n"
+"rescan rescan all busses, the given bus, or bus:target:lun\n"
+"reset reset all busses, the given bus, or bus:target:lun\n"
+"defects read the defect list of the specified device\n"
+"modepage display or edit (-e) the given mode page\n"
+"cmd send the given scsi command, may need -i or -o as well\n"
+"debug turn debugging on/off for a bus, target, or lun, or all devices\n"
+"tags report or set the number of transaction slots for a device\n"
+"negotiate report or set device negotiation parameters\n"
+"format send the SCSI FORMAT UNIT command to the named device\n"
+"help this message\n"
+"Device Identifiers:\n"
+"bus:target specify the bus and target, lun defaults to 0\n"
+"bus:target:lun specify the bus, target and lun\n"
+"deviceUNIT specify the device name, like \"da4\" or \"cd2\"\n"
+"Generic arguments:\n"
+"-v be verbose, print out sense information\n"
+"-t timeout command timeout in seconds, overrides default timeout\n"
+"-n dev_name specify device name, e.g. \"da\", \"cd\"\n"
+"-u unit specify unit number, e.g. \"0\", \"5\"\n"
+"-E have the kernel attempt to perform SCSI error recovery\n"
+"-C count specify the SCSI command retry count (needs -E to work)\n"
+"modepage arguments:\n"
+"-l list all available mode pages\n"
+"-m page specify the mode page to view or edit\n"
+"-e edit the specified mode page\n"
+"-b force view to binary mode\n"
+"-d disable block descriptors for mode sense\n"
+"-P pgctl page control field 0-3\n"
+"defects arguments:\n"
+"-f format specify defect list format (block, bfi or phys)\n"
+"-G get the grown defect list\n"
+"-P get the permanant defect list\n"
+"inquiry arguments:\n"
+"-D get the standard inquiry data\n"
+"-S get the serial number\n"
+"-R get the transfer rate, etc.\n"
+"cmd arguments:\n"
+"-c cdb [args] specify the SCSI CDB\n"
+"-i len fmt specify input data and input data format\n"
+"-o len fmt [args] specify output data and output data fmt\n"
+"debug arguments:\n"
+"-I CAM_DEBUG_INFO -- scsi commands, errors, data\n"
+"-T CAM_DEBUG_TRACE -- routine flow tracking\n"
+"-S CAM_DEBUG_SUBTRACE -- internal routine command flow\n"
+"-c CAM_DEBUG_CDB -- print out SCSI CDBs only\n"
+"tags arguments:\n"
+"-N tags specify the number of tags to use for this device\n"
+"-q be quiet, don't report the number of tags\n"
+"-v report a number of tag-related parameters\n"
+"negotiate arguments:\n"
+"-a send a test unit ready after negotiation\n"
+"-c report/set current negotiation settings\n"
+"-D <arg> \"enable\" or \"disable\" disconnection\n"
+"-O offset set command delay offset\n"
+"-q be quiet, don't report anything\n"
+"-R syncrate synchronization rate in MHz\n"
+"-T <arg> \"enable\" or \"disable\" tagged queueing\n"
+"-U report/set user negotiation settings\n"
+"-W bus_width set the bus width in bits (8, 16 or 32)\n"
+"-v also print a Path Inquiry CCB for the controller\n"
+"format arguments:\n"
+"-q be quiet, don't print status messages\n"
+"-r run in report only mode\n"
+"-w don't send immediate format command\n"
+"-y don't ask any questions\n");
+#endif /* MINIMALISTIC */
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ char *device = NULL;
+ int unit = 0;
+ struct cam_device *cam_dev = NULL;
+ int timeout = 0, retry_count = 1;
+ camcontrol_optret optreturn;
+ char *tstr;
+ const char *mainopt = "C:En:t:u:v";
+ const char *subopt = NULL;
+ char combinedopt[256];
+ int error = 0, optstart = 2;
+ int devopen = 1;
+#ifndef MINIMALISTIC
+ int bus, target, lun;
+#endif /* MINIMALISTIC */
+
+ cmdlist = CAM_CMD_NONE;
+ arglist = CAM_ARG_NONE;
+
+ if (argc < 2) {
+ usage(0);
+ exit(1);
+ }
+
+ /*
+ * Get the base option.
+ */
+ optreturn = getoption(argv[1], &cmdlist, &arglist, &subopt);
+
+ if (optreturn == CC_OR_AMBIGUOUS) {
+ warnx("ambiguous option %s", argv[1]);
+ usage(0);
+ exit(1);
+ } else if (optreturn == CC_OR_NOT_FOUND) {
+ warnx("option %s not found", argv[1]);
+ usage(0);
+ exit(1);
+ }
+
+ /*
+ * Ahh, getopt(3) is a pain.
+ *
+ * This is a gross hack. There really aren't many other good
+ * options (excuse the pun) for parsing options in a situation like
+ * this. getopt is kinda braindead, so you end up having to run
+ * through the options twice, and give each invocation of getopt
+ * the option string for the other invocation.
+ *
+ * You would think that you could just have two groups of options.
+ * The first group would get parsed by the first invocation of
+ * getopt, and the second group would get parsed by the second
+ * invocation of getopt. It doesn't quite work out that way. When
+ * the first invocation of getopt finishes, it leaves optind pointing
+ * to the argument _after_ the first argument in the second group.
+ * So when the second invocation of getopt comes around, it doesn't
+ * recognize the first argument it gets and then bails out.
+ *
+ * A nice alternative would be to have a flag for getopt that says
+ * "just keep parsing arguments even when you encounter an unknown
+ * argument", but there isn't one. So there's no real clean way to
+ * easily parse two sets of arguments without having one invocation
+ * of getopt know about the other.
+ *
+ * Without this hack, the first invocation of getopt would work as
+ * long as the generic arguments are first, but the second invocation
+ * (in the subfunction) would fail in one of two ways. In the case
+ * where you don't set optreset, it would fail because optind may be
+ * pointing to the argument after the one it should be pointing at.
+ * In the case where you do set optreset, and reset optind, it would
+ * fail because getopt would run into the first set of options, which
+ * it doesn't understand.
+ *
+ * All of this would "sort of" work if you could somehow figure out
+ * whether optind had been incremented one option too far. The
+ * mechanics of that, however, are more daunting than just giving
+ * both invocations all of the expect options for either invocation.
+ *
+ * Needless to say, I wouldn't mind if someone invented a better
+ * (non-GPL!) command line parsing interface than getopt. I
+ * wouldn't mind if someone added more knobs to getopt to make it
+ * work better. Who knows, I may talk myself into doing it someday,
+ * if the standards weenies let me. As it is, it just leads to
+ * hackery like this and causes people to avoid it in some cases.
+ *
+ * KDM, September 8th, 1998
+ */
+ if (subopt != NULL)
+ sprintf(combinedopt, "%s%s", mainopt, subopt);
+ else
+ sprintf(combinedopt, "%s", mainopt);
+
+ /*
+ * For these options we do not parse optional device arguments and
+ * we do not open a passthrough device.
+ */
+ if ((cmdlist == CAM_CMD_RESCAN)
+ || (cmdlist == CAM_CMD_RESET)
+ || (cmdlist == CAM_CMD_DEVTREE)
+ || (cmdlist == CAM_CMD_USAGE)
+ || (cmdlist == CAM_CMD_DEBUG))
+ devopen = 0;
+
+#ifndef MINIMALISTIC
+ if ((devopen == 1)
+ && (argc > 2 && argv[2][0] != '-')) {
+ char name[30];
+ int rv;
+
+ /*
+ * First catch people who try to do things like:
+ * camcontrol tur /dev/da0
+ * camcontrol doesn't take device nodes as arguments.
+ */
+ if (argv[2][0] == '/') {
+ warnx("%s is not a valid device identifier", argv[2]);
+ errx(1, "please read the camcontrol(8) man page");
+ } else if (isdigit(argv[2][0])) {
+ /* device specified as bus:target[:lun] */
+ rv = parse_btl(argv[2], &bus, &target, &lun, &arglist);
+ if (rv < 2)
+ errx(1, "numeric device specification must "
+ "be either bus:target, or "
+ "bus:target:lun");
+ /* default to 0 if lun was not specified */
+ if ((arglist & CAM_ARG_LUN) == 0) {
+ lun = 0;
+ arglist |= CAM_ARG_LUN;
+ }
+ optstart++;
+ } else {
+ if (cam_get_device(argv[2], name, sizeof name, &unit)
+ == -1)
+ errx(1, "%s", cam_errbuf);
+ device = strdup(name);
+ arglist |= CAM_ARG_DEVICE | CAM_ARG_UNIT;
+ optstart++;
+ }
+ }
+#endif /* MINIMALISTIC */
+ /*
+ * Start getopt processing at argv[2/3], since we've already
+ * accepted argv[1..2] as the command name, and as a possible
+ * device name.
+ */
+ optind = optstart;
+
+ /*
+ * Now we run through the argument list looking for generic
+ * options, and ignoring options that possibly belong to
+ * subfunctions.
+ */
+ while ((c = getopt(argc, argv, combinedopt))!= -1){
+ switch(c) {
+ case 'C':
+ retry_count = strtol(optarg, NULL, 0);
+ if (retry_count < 0)
+ errx(1, "retry count %d is < 0",
+ retry_count);
+ arglist |= CAM_ARG_RETRIES;
+ break;
+ case 'E':
+ arglist |= CAM_ARG_ERR_RECOVER;
+ break;
+ case 'n':
+ arglist |= CAM_ARG_DEVICE;
+ tstr = optarg;
+ while (isspace(*tstr) && (*tstr != '\0'))
+ tstr++;
+ device = (char *)strdup(tstr);
+ break;
+ case 't':
+ timeout = strtol(optarg, NULL, 0);
+ if (timeout < 0)
+ errx(1, "invalid timeout %d", timeout);
+ /* Convert the timeout from seconds to ms */
+ timeout *= 1000;
+ arglist |= CAM_ARG_TIMEOUT;
+ break;
+ case 'u':
+ arglist |= CAM_ARG_UNIT;
+ unit = strtol(optarg, NULL, 0);
+ break;
+ case 'v':
+ arglist |= CAM_ARG_VERBOSE;
+ break;
+ default:
+ break;
+ }
+ }
+
+#ifndef MINIMALISTIC
+ /*
+ * For most commands we'll want to open the passthrough device
+ * associated with the specified device. In the case of the rescan
+ * commands, we don't use a passthrough device at all, just the
+ * transport layer device.
+ */
+ if (devopen == 1) {
+ if (((arglist & (CAM_ARG_BUS|CAM_ARG_TARGET)) == 0)
+ && (((arglist & CAM_ARG_DEVICE) == 0)
+ || ((arglist & CAM_ARG_UNIT) == 0))) {
+ errx(1, "subcommand \"%s\" requires a valid device "
+ "identifier", argv[1]);
+ }
+
+ if ((cam_dev = ((arglist & (CAM_ARG_BUS | CAM_ARG_TARGET))?
+ cam_open_btl(bus, target, lun, O_RDWR, NULL) :
+ cam_open_spec_device(device,unit,O_RDWR,NULL)))
+ == NULL)
+ errx(1,"%s", cam_errbuf);
+ }
+#endif /* MINIMALISTIC */
+
+ /*
+ * Reset optind to 2, and reset getopt, so these routines can parse
+ * the arguments again.
+ */
+ optind = optstart;
+ optreset = 1;
+
+ switch(cmdlist) {
+#ifndef MINIMALISTIC
+ case CAM_CMD_DEVLIST:
+ error = getdevlist(cam_dev);
+ break;
+#endif /* MINIMALISTIC */
+ case CAM_CMD_DEVTREE:
+ error = getdevtree();
+ break;
+#ifndef MINIMALISTIC
+ case CAM_CMD_TUR:
+ error = testunitready(cam_dev, retry_count, timeout, 0);
+ break;
+ case CAM_CMD_INQUIRY:
+ error = scsidoinquiry(cam_dev, argc, argv, combinedopt,
+ retry_count, timeout);
+ break;
+ case CAM_CMD_STARTSTOP:
+ error = scsistart(cam_dev, arglist & CAM_ARG_START_UNIT,
+ arglist & CAM_ARG_EJECT, retry_count,
+ timeout);
+ break;
+#endif /* MINIMALISTIC */
+ case CAM_CMD_RESCAN:
+ error = dorescan_or_reset(argc, argv, 1);
+ break;
+ case CAM_CMD_RESET:
+ error = dorescan_or_reset(argc, argv, 0);
+ break;
+#ifndef MINIMALISTIC
+ case CAM_CMD_READ_DEFECTS:
+ error = readdefects(cam_dev, argc, argv, combinedopt,
+ retry_count, timeout);
+ break;
+ case CAM_CMD_MODE_PAGE:
+ modepage(cam_dev, argc, argv, combinedopt,
+ retry_count, timeout);
+ break;
+ case CAM_CMD_SCSI_CMD:
+ error = scsicmd(cam_dev, argc, argv, combinedopt,
+ retry_count, timeout);
+ break;
+ case CAM_CMD_DEBUG:
+ error = camdebug(argc, argv, combinedopt);
+ break;
+ case CAM_CMD_TAG:
+ error = tagcontrol(cam_dev, argc, argv, combinedopt);
+ break;
+ case CAM_CMD_RATE:
+ error = ratecontrol(cam_dev, retry_count, timeout,
+ argc, argv, combinedopt);
+ break;
+ case CAM_CMD_FORMAT:
+ error = scsiformat(cam_dev, argc, argv,
+ combinedopt, retry_count, timeout);
+ break;
+#endif /* MINIMALISTIC */
+ case CAM_CMD_USAGE:
+ usage(1);
+ break;
+ default:
+ usage(0);
+ error = 1;
+ break;
+ }
+
+ if (cam_dev != NULL)
+ cam_close_device(cam_dev);
+
+ exit(error);
+}
diff --git a/sbin/camcontrol/camcontrol.h b/sbin/camcontrol/camcontrol.h
new file mode 100644
index 0000000..eebbe85
--- /dev/null
+++ b/sbin/camcontrol/camcontrol.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1998 Kenneth D. Merry.
+ * 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _CAMCONTROL_H
+#define _CAMCONTROL_H
+/*
+ * get_hook: Structure for evaluating args in a callback.
+ */
+struct get_hook
+{
+ int argc;
+ char **argv;
+ int got;
+};
+
+void mode_sense(struct cam_device *device, int mode_page, int page_control,
+ int dbd, int retry_count, int timeout, u_int8_t *data,
+ int datalen);
+void mode_select(struct cam_device *device, int save_pages, int retry_count,
+ int timeout, u_int8_t *data, int datalen);
+void mode_edit(struct cam_device *device, int page, int page_control, int dbd,
+ int edit, int binary, int retry_count, int timeout);
+void mode_list(struct cam_device *device, int page_control, int dbd,
+ int retry_count, int timeout);
+char *cget(void *hook, char *name);
+int iget(void *hook, char *name);
+void arg_put(void *hook, int letter, void *arg, int count, char *name);
+void usage(int verbose);
+#endif /* _CAMCONTROL_H */
diff --git a/sbin/camcontrol/modeedit.c b/sbin/camcontrol/modeedit.c
new file mode 100644
index 0000000..3fb9587
--- /dev/null
+++ b/sbin/camcontrol/modeedit.c
@@ -0,0 +1,909 @@
+/*-
+ * Copyright (c) 2000 Kelly Yancey <kbyanc@posi.net>
+ * Derived from work done by Julian Elischer <julian@tfs.com,
+ * julian@dialix.oz.au>, 1993, and Peter Dufault <dufault@hda.com>, 1994.
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/queue.h>
+#include <sys/types.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <cam/scsi/scsi_all.h>
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <camlib.h>
+#include "camcontrol.h"
+
+int verbose = 0;
+
+#define DEFAULT_SCSI_MODE_DB "/usr/share/misc/scsi_modes"
+#define DEFAULT_EDITOR "vi"
+#define MAX_FORMAT_SPEC 4096 /* Max CDB format specifier. */
+#define MAX_PAGENUM_LEN 10 /* Max characters in page num. */
+#define MAX_PAGENAME_LEN 64 /* Max characters in page name. */
+#define PAGEDEF_START '{' /* Page definition delimiter. */
+#define PAGEDEF_END '}' /* Page definition delimiter. */
+#define PAGENAME_START '"' /* Page name delimiter. */
+#define PAGENAME_END '"' /* Page name delimiter. */
+#define PAGEENTRY_END ';' /* Page entry terminator (optional). */
+#define MAX_COMMAND_SIZE 255 /* Mode/Log sense data buffer size. */
+#define PAGE_CTRL_SHIFT 6 /* Bit offset to page control field. */
+
+
+/* Macros for working with mode pages. */
+#define MODE_PAGE_HEADER(mh) \
+ (struct scsi_mode_page_header *)find_mode_page_6(mh)
+
+#define MODE_PAGE_DATA(mph) \
+ (u_int8_t *)(mph) + sizeof(struct scsi_mode_page_header)
+
+
+struct editentry {
+ STAILQ_ENTRY(editentry) link;
+ char *name;
+ char type;
+ int editable;
+ int size;
+ union {
+ int ivalue;
+ char *svalue;
+ } value;
+};
+STAILQ_HEAD(, editentry) editlist; /* List of page entries. */
+int editlist_changed = 0; /* Whether any entries were changed. */
+
+struct pagename {
+ SLIST_ENTRY(pagename) link;
+ int pagenum;
+ char *name;
+};
+SLIST_HEAD(, pagename) namelist; /* Page number to name mappings. */
+
+static char format[MAX_FORMAT_SPEC]; /* Buffer for scsi cdb format def. */
+
+static FILE *edit_file = NULL; /* File handle for edit file. */
+static char edit_path[] = "/tmp/camXXXXXX";
+
+
+/* Function prototypes. */
+static void editentry_create(void *hook, int letter, void *arg,
+ int count, char *name);
+static void editentry_update(void *hook, int letter, void *arg,
+ int count, char *name);
+static int editentry_save(void *hook, char *name);
+static struct editentry *editentry_lookup(char *name);
+static int editentry_set(char *name, char *newvalue,
+ int editonly);
+static void editlist_populate(struct cam_device *device,
+ int modepage, int page_control,
+ int dbd, int retries, int timeout);
+static void editlist_save(struct cam_device *device, int modepage,
+ int page_control, int dbd, int retries,
+ int timeout);
+static void nameentry_create(int pagenum, char *name);
+static struct pagename *nameentry_lookup(int pagenum);
+static int load_format(const char *pagedb_path, int page);
+static int modepage_write(FILE *file, int editonly);
+static int modepage_read(FILE *file);
+static void modepage_edit(void);
+static void modepage_dump(struct cam_device *device, int page,
+ int page_control, int dbd, int retries,
+ int timeout);
+static void cleanup_editfile(void);
+
+
+#define returnerr(code) do { \
+ errno = code; \
+ return (-1); \
+} while (0)
+
+
+#define RTRIM(string) do { \
+ int _length; \
+ while (isspace(string[_length = strlen(string) - 1])) \
+ string[_length] = '\0'; \
+} while (0)
+
+
+static void
+editentry_create(void *hook __unused, int letter, void *arg, int count,
+ char *name)
+{
+ struct editentry *newentry; /* Buffer to hold new entry. */
+
+ /* Allocate memory for the new entry and a copy of the entry name. */
+ if ((newentry = malloc(sizeof(struct editentry))) == NULL ||
+ (newentry->name = strdup(name)) == NULL)
+ err(EX_OSERR, NULL);
+
+ /* Trim any trailing whitespace for the entry name. */
+ RTRIM(newentry->name);
+
+ newentry->editable = (arg != NULL);
+ newentry->type = letter;
+ newentry->size = count; /* Placeholder; not accurate. */
+ newentry->value.svalue = NULL;
+
+ STAILQ_INSERT_TAIL(&editlist, newentry, link);
+}
+
+static void
+editentry_update(void *hook __unused, int letter, void *arg, int count,
+ char *name)
+{
+ struct editentry *dest; /* Buffer to hold entry to update. */
+
+ dest = editentry_lookup(name);
+ assert(dest != NULL);
+
+ dest->type = letter;
+ dest->size = count; /* We get the real size now. */
+
+ switch (dest->type) {
+ case 'i': /* Byte-sized integral type. */
+ case 'b': /* Bit-sized integral types. */
+ case 't':
+ dest->value.ivalue = (intptr_t)arg;
+ break;
+
+ case 'c': /* Character array. */
+ case 'z': /* Null-padded string. */
+ editentry_set(name, (char *)arg, 0);
+ break;
+ default:
+ ; /* NOTREACHED */
+ }
+}
+
+static int
+editentry_save(void *hook __unused, char *name)
+{
+ struct editentry *src; /* Entry value to save. */
+
+ src = editentry_lookup(name);
+ assert(src != NULL);
+
+ switch (src->type) {
+ case 'i': /* Byte-sized integral type. */
+ case 'b': /* Bit-sized integral types. */
+ case 't':
+ return (src->value.ivalue);
+ /* NOTREACHED */
+
+ case 'c': /* Character array. */
+ case 'z': /* Null-padded string. */
+ return ((intptr_t)src->value.svalue);
+ /* NOTREACHED */
+
+ default:
+ ; /* NOTREACHED */
+ }
+
+ return (0); /* This should never happen. */
+}
+
+static struct editentry *
+editentry_lookup(char *name)
+{
+ struct editentry *scan;
+
+ assert(name != NULL);
+
+ STAILQ_FOREACH(scan, &editlist, link) {
+ if (strcasecmp(scan->name, name) == 0)
+ return (scan);
+ }
+
+ /* Not found during list traversal. */
+ return (NULL);
+}
+
+static int
+editentry_set(char *name, char *newvalue, int editonly)
+{
+ struct editentry *dest; /* Modepage entry to update. */
+ char *cval; /* Pointer to new string value. */
+ char *convertend; /* End-of-conversion pointer. */
+ int ival; /* New integral value. */
+ int resolution; /* Resolution in bits for integer conversion. */
+
+/*
+ * Macro to determine the maximum value of the given size for the current
+ * resolution.
+ * XXX Lovely x86's optimize out the case of shifting by 32 and gcc doesn't
+ * currently workaround it (even for int64's), so we have to kludge it.
+ */
+#define RESOLUTION_MAX(size) ((resolution * (size) == 32)? \
+ (int)0xffffffff: (1 << (resolution * (size))) - 1)
+
+ assert(newvalue != NULL);
+ if (*newvalue == '\0')
+ return (0); /* Nothing to do. */
+
+ if ((dest = editentry_lookup(name)) == NULL)
+ returnerr(ENOENT);
+ if (!dest->editable && editonly)
+ returnerr(EPERM);
+
+ switch (dest->type) {
+ case 'i': /* Byte-sized integral type. */
+ case 'b': /* Bit-sized integral types. */
+ case 't':
+ /* Convert the value string to an integer. */
+ resolution = (dest->type == 'i')? 8: 1;
+ ival = (int)strtol(newvalue, &convertend, 0);
+ if (*convertend != '\0')
+ returnerr(EINVAL);
+ if (ival > RESOLUTION_MAX(dest->size) || ival < 0) {
+ int newival = (ival < 0)? 0: RESOLUTION_MAX(dest->size);
+ warnx("value %d is out of range for entry %s; clipping "
+ "to %d", ival, name, newival);
+ ival = newival;
+ }
+ if (dest->value.ivalue != ival)
+ editlist_changed = 1;
+ dest->value.ivalue = ival;
+ break;
+
+ case 'c': /* Character array. */
+ case 'z': /* Null-padded string. */
+ if ((cval = malloc(dest->size + 1)) == NULL)
+ err(EX_OSERR, NULL);
+ bzero(cval, dest->size + 1);
+ strncpy(cval, newvalue, dest->size);
+ if (dest->type == 'z') {
+ /* Convert trailing spaces to nulls. */
+ char *convertend2;
+
+ for (convertend2 = cval + dest->size;
+ convertend2 >= cval; convertend2--) {
+ if (*convertend2 == ' ')
+ *convertend2 = '\0';
+ else if (*convertend2 != '\0')
+ break;
+ }
+ }
+ if (strncmp(dest->value.svalue, cval, dest->size) == 0) {
+ /* Nothing changed, free the newly allocated string. */
+ free(cval);
+ break;
+ }
+ if (dest->value.svalue != NULL) {
+ /* Free the current string buffer. */
+ free(dest->value.svalue);
+ dest->value.svalue = NULL;
+ }
+ dest->value.svalue = cval;
+ editlist_changed = 1;
+ break;
+
+ default:
+ ; /* NOTREACHED */
+ }
+
+ return (0);
+#undef RESOLUTION_MAX
+}
+
+static void
+nameentry_create(int pagenum, char *name) {
+ struct pagename *newentry;
+
+ if (pagenum < 0 || name == NULL || name[0] == '\0')
+ return;
+
+ /* Allocate memory for the new entry and a copy of the entry name. */
+ if ((newentry = malloc(sizeof(struct pagename))) == NULL ||
+ (newentry->name = strdup(name)) == NULL)
+ err(EX_OSERR, NULL);
+
+ /* Trim any trailing whitespace for the page name. */
+ RTRIM(newentry->name);
+
+ newentry->pagenum = pagenum;
+ SLIST_INSERT_HEAD(&namelist, newentry, link);
+}
+
+static struct pagename *
+nameentry_lookup(int pagenum) {
+ struct pagename *scan;
+
+ SLIST_FOREACH(scan, &namelist, link) {
+ if (pagenum == scan->pagenum)
+ return (scan);
+ }
+
+ /* Not found during list traversal. */
+ return (NULL);
+}
+
+static int
+load_format(const char *pagedb_path, int page)
+{
+ FILE *pagedb;
+ char str_pagenum[MAX_PAGENUM_LEN];
+ char str_pagename[MAX_PAGENAME_LEN];
+ int pagenum;
+ int depth; /* Quoting depth. */
+ int found;
+ int lineno;
+ enum { LOCATE, PAGENAME, PAGEDEF } state;
+ int ch;
+ char c;
+
+#define SETSTATE_LOCATE do { \
+ str_pagenum[0] = '\0'; \
+ str_pagename[0] = '\0'; \
+ pagenum = -1; \
+ state = LOCATE; \
+} while (0)
+
+#define SETSTATE_PAGENAME do { \
+ str_pagename[0] = '\0'; \
+ state = PAGENAME; \
+} while (0)
+
+#define SETSTATE_PAGEDEF do { \
+ format[0] = '\0'; \
+ state = PAGEDEF; \
+} while (0)
+
+#define UPDATE_LINENO do { \
+ if (c == '\n') \
+ lineno++; \
+} while (0)
+
+#define BUFFERFULL(buffer) (strlen(buffer) + 1 >= sizeof(buffer))
+
+ if ((pagedb = fopen(pagedb_path, "r")) == NULL)
+ returnerr(ENOENT);
+
+ SLIST_INIT(&namelist);
+
+ depth = 0;
+ lineno = 0;
+ found = 0;
+ SETSTATE_LOCATE;
+ while ((ch = fgetc(pagedb)) != EOF) {
+
+ /* Keep a line count to make error messages more useful. */
+ UPDATE_LINENO;
+
+ /* Skip over comments anywhere in the mode database. */
+ if (ch == '#') {
+ do {
+ ch = fgetc(pagedb);
+ } while (ch != '\n' && ch != EOF);
+ UPDATE_LINENO;
+ continue;
+ }
+ c = ch;
+
+ /* Strip out newline characters. */
+ if (c == '\n')
+ continue;
+
+ /* Keep track of the nesting depth for braces. */
+ if (c == PAGEDEF_START)
+ depth++;
+ else if (c == PAGEDEF_END) {
+ depth--;
+ if (depth < 0) {
+ errx(EX_OSFILE, "%s:%d: %s", pagedb_path,
+ lineno, "mismatched bracket");
+ }
+ }
+
+ switch (state) {
+ case LOCATE:
+ /*
+ * Locate the page the user is interested in, skipping
+ * all others.
+ */
+ if (isspace(c)) {
+ /* Ignore all whitespace between pages. */
+ break;
+ } else if (depth == 0 && c == PAGEENTRY_END) {
+ /*
+ * A page entry terminator will reset page
+ * scanning (useful for assigning names to
+ * modes without providing a mode definition).
+ */
+ /* Record the name of this page. */
+ pagenum = strtol(str_pagenum, NULL, 0);
+ nameentry_create(pagenum, str_pagename);
+ SETSTATE_LOCATE;
+ } else if (depth == 0 && c == PAGENAME_START) {
+ SETSTATE_PAGENAME;
+ } else if (c == PAGEDEF_START) {
+ pagenum = strtol(str_pagenum, NULL, 0);
+ if (depth == 1) {
+ /* Record the name of this page. */
+ nameentry_create(pagenum, str_pagename);
+ /*
+ * Only record the format if this is
+ * the page we are interested in.
+ */
+ if (page == pagenum && !found)
+ SETSTATE_PAGEDEF;
+ }
+ } else if (c == PAGEDEF_END) {
+ /* Reset the processor state. */
+ SETSTATE_LOCATE;
+ } else if (depth == 0 && ! BUFFERFULL(str_pagenum)) {
+ strncat(str_pagenum, &c, 1);
+ } else if (depth == 0) {
+ errx(EX_OSFILE, "%s:%d: %s %zd %s", pagedb_path,
+ lineno, "page identifier exceeds",
+ sizeof(str_pagenum) - 1, "characters");
+ }
+ break;
+
+ case PAGENAME:
+ if (c == PAGENAME_END) {
+ /*
+ * Return to LOCATE state without resetting the
+ * page number buffer.
+ */
+ state = LOCATE;
+ } else if (! BUFFERFULL(str_pagename)) {
+ strncat(str_pagename, &c, 1);
+ } else {
+ errx(EX_OSFILE, "%s:%d: %s %zd %s", pagedb_path,
+ lineno, "page name exceeds",
+ sizeof(str_pagenum) - 1, "characters");
+ }
+ break;
+
+ case PAGEDEF:
+ /*
+ * Transfer the page definition into a format buffer
+ * suitable for use with CDB encoding/decoding routines.
+ */
+ if (depth == 0) {
+ found = 1;
+ SETSTATE_LOCATE;
+ } else if (! BUFFERFULL(format)) {
+ strncat(format, &c, 1);
+ } else {
+ errx(EX_OSFILE, "%s:%d: %s %zd %s", pagedb_path,
+ lineno, "page definition exceeds",
+ sizeof(format) - 1, "characters");
+ }
+ break;
+
+ default:
+ ; /* NOTREACHED */
+ }
+
+ /* Repeat processing loop with next character. */
+ }
+
+ if (ferror(pagedb))
+ err(EX_OSFILE, "%s", pagedb_path);
+
+ /* Close the SCSI page database. */
+ fclose(pagedb);
+
+ if (!found) /* Never found a matching page. */
+ returnerr(ESRCH);
+
+ return (0);
+}
+
+static void
+editlist_populate(struct cam_device *device, int modepage, int page_control,
+ int dbd, int retries, int timeout)
+{
+ u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */
+ u_int8_t *mode_pars; /* Pointer to modepage params. */
+ struct scsi_mode_header_6 *mh; /* Location of mode header. */
+ struct scsi_mode_page_header *mph;
+
+ STAILQ_INIT(&editlist);
+
+ /* Fetch changeable values; use to build initial editlist. */
+ mode_sense(device, modepage, 1, dbd, retries, timeout, data,
+ sizeof(data));
+
+ mh = (struct scsi_mode_header_6 *)data;
+ mph = MODE_PAGE_HEADER(mh);
+ mode_pars = MODE_PAGE_DATA(mph);
+
+ /* Decode the value data, creating edit_entries for each value. */
+ buff_decode_visit(mode_pars, mh->data_length, format,
+ editentry_create, 0);
+
+ /* Fetch the current/saved values; use to set editentry values. */
+ mode_sense(device, modepage, page_control, dbd, retries, timeout, data,
+ sizeof(data));
+ buff_decode_visit(mode_pars, mh->data_length, format,
+ editentry_update, 0);
+}
+
+static void
+editlist_save(struct cam_device *device, int modepage, int page_control,
+ int dbd, int retries, int timeout)
+{
+ u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */
+ u_int8_t *mode_pars; /* Pointer to modepage params. */
+ struct scsi_mode_header_6 *mh; /* Location of mode header. */
+ struct scsi_mode_page_header *mph;
+
+ /* Make sure that something changed before continuing. */
+ if (! editlist_changed)
+ return;
+
+ /*
+ * Preload the CDB buffer with the current mode page data.
+ * XXX If buff_encode_visit would return the number of bytes encoded
+ * we *should* use that to build a header from scratch. As it is
+ * now, we need mode_sense to find out the page length.
+ */
+ mode_sense(device, modepage, page_control, dbd, retries, timeout, data,
+ sizeof(data));
+
+ /* Initial headers & offsets. */
+ mh = (struct scsi_mode_header_6 *)data;
+ mph = MODE_PAGE_HEADER(mh);
+ mode_pars = MODE_PAGE_DATA(mph);
+
+ /* Encode the value data to be passed back to the device. */
+ buff_encode_visit(mode_pars, mh->data_length, format,
+ editentry_save, 0);
+
+ /* Eliminate block descriptors. */
+ bcopy(mph, ((u_int8_t *)mh) + sizeof(*mh),
+ sizeof(*mph) + mph->page_length);
+
+ /* Recalculate headers & offsets. */
+ mh->blk_desc_len = 0; /* No block descriptors. */
+ mh->dev_spec = 0; /* Clear device-specific parameters. */
+ mph = MODE_PAGE_HEADER(mh);
+ mode_pars = MODE_PAGE_DATA(mph);
+
+ mph->page_code &= SMS_PAGE_CODE;/* Isolate just the page code. */
+ mh->data_length = 0; /* Reserved for MODE SELECT command. */
+
+ /*
+ * Write the changes back to the device. If the user editted control
+ * page 3 (saved values) then request the changes be permanently
+ * recorded.
+ */
+ mode_select(device,
+ (page_control << PAGE_CTRL_SHIFT == SMS_PAGE_CTRL_SAVED),
+ retries, timeout, (u_int8_t *)mh,
+ sizeof(*mh) + mh->blk_desc_len + sizeof(*mph) + mph->page_length);
+}
+
+static int
+modepage_write(FILE *file, int editonly)
+{
+ struct editentry *scan;
+ int written = 0;
+
+ STAILQ_FOREACH(scan, &editlist, link) {
+ if (scan->editable || !editonly) {
+ written++;
+ if (scan->type == 'c' || scan->type == 'z') {
+ fprintf(file, "%s: %s\n", scan->name,
+ scan->value.svalue);
+ } else {
+ fprintf(file, "%s: %d\n", scan->name,
+ scan->value.ivalue);
+ }
+ }
+ }
+ return (written);
+}
+
+static int
+modepage_read(FILE *file)
+{
+ char *buffer; /* Pointer to dynamic line buffer. */
+ char *line; /* Pointer to static fgetln buffer. */
+ char *name; /* Name portion of the line buffer. */
+ char *value; /* Value portion of line buffer. */
+ size_t length; /* Length of static fgetln buffer. */
+
+#define ABORT_READ(message, param) do { \
+ warnx(message, param); \
+ free(buffer); \
+ returnerr(EAGAIN); \
+} while (0)
+
+ while ((line = fgetln(file, &length)) != NULL) {
+ /* Trim trailing whitespace (including optional newline). */
+ while (length > 0 && isspace(line[length - 1]))
+ length--;
+
+ /* Allocate a buffer to hold the line + terminating null. */
+ if ((buffer = malloc(length + 1)) == NULL)
+ err(EX_OSERR, NULL);
+ memcpy(buffer, line, length);
+ buffer[length] = '\0';
+
+ /* Strip out comments. */
+ if ((value = strchr(buffer, '#')) != NULL)
+ *value = '\0';
+
+ /* The name is first in the buffer. Trim whitespace.*/
+ name = buffer;
+ RTRIM(name);
+ while (isspace(*name))
+ name++;
+
+ /* Skip empty lines. */
+ if (strlen(name) == 0)
+ continue;
+
+ /* The name ends at the colon; the value starts there. */
+ if ((value = strrchr(buffer, ':')) == NULL)
+ ABORT_READ("no value associated with %s", name);
+ *value = '\0'; /* Null-terminate name. */
+ value++; /* Value starts afterwards. */
+
+ /* Trim leading and trailing whitespace. */
+ RTRIM(value);
+ while (isspace(*value))
+ value++;
+
+ /* Make sure there is a value left. */
+ if (strlen(value) == 0)
+ ABORT_READ("no value associated with %s", name);
+
+ /* Update our in-memory copy of the modepage entry value. */
+ if (editentry_set(name, value, 1) != 0) {
+ if (errno == ENOENT) {
+ /* No entry by the name. */
+ ABORT_READ("no such modepage entry \"%s\"",
+ name);
+ } else if (errno == EINVAL) {
+ /* Invalid value. */
+ ABORT_READ("Invalid value for entry \"%s\"",
+ name);
+ } else if (errno == ERANGE) {
+ /* Value out of range for entry type. */
+ ABORT_READ("value out of range for %s", name);
+ } else if (errno == EPERM) {
+ /* Entry is not editable; not fatal. */
+ warnx("modepage entry \"%s\" is read-only; "
+ "skipping.", name);
+ }
+ }
+
+ free(buffer);
+ }
+ return (ferror(file)? -1: 0);
+
+#undef ABORT_READ
+}
+
+static void
+modepage_edit(void)
+{
+ const char *editor;
+ char *commandline;
+ int fd;
+ int written;
+
+ if (!isatty(fileno(stdin))) {
+ /* Not a tty, read changes from stdin. */
+ modepage_read(stdin);
+ return;
+ }
+
+ /* Lookup editor to invoke. */
+ if ((editor = getenv("EDITOR")) == NULL)
+ editor = DEFAULT_EDITOR;
+
+ /* Create temp file for editor to modify. */
+ if ((fd = mkstemp(edit_path)) == -1)
+ errx(EX_CANTCREAT, "mkstemp failed");
+
+ atexit(cleanup_editfile);
+
+ if ((edit_file = fdopen(fd, "w")) == NULL)
+ err(EX_NOINPUT, "%s", edit_path);
+
+ written = modepage_write(edit_file, 1);
+
+ fclose(edit_file);
+ edit_file = NULL;
+
+ if (written == 0) {
+ warnx("no editable entries");
+ cleanup_editfile();
+ return;
+ }
+
+ /*
+ * Allocate memory to hold the command line (the 2 extra characters
+ * are to hold the argument separator (a space), and the terminating
+ * null character.
+ */
+ commandline = malloc(strlen(editor) + strlen(edit_path) + 2);
+ if (commandline == NULL)
+ err(EX_OSERR, NULL);
+ sprintf(commandline, "%s %s", editor, edit_path);
+
+ /* Invoke the editor on the temp file. */
+ if (system(commandline) == -1)
+ err(EX_UNAVAILABLE, "could not invoke %s", editor);
+ free(commandline);
+
+ if ((edit_file = fopen(edit_path, "r")) == NULL)
+ err(EX_NOINPUT, "%s", edit_path);
+
+ /* Read any changes made to the temp file. */
+ modepage_read(edit_file);
+
+ cleanup_editfile();
+}
+
+static void
+modepage_dump(struct cam_device *device, int page, int page_control, int dbd,
+ int retries, int timeout)
+{
+ u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */
+ u_int8_t *mode_pars; /* Pointer to modepage params. */
+ struct scsi_mode_header_6 *mh; /* Location of mode header. */
+ struct scsi_mode_page_header *mph;
+ int indx; /* Index for scanning mode params. */
+
+ mode_sense(device, page, page_control, dbd, retries, timeout, data,
+ sizeof(data));
+
+ mh = (struct scsi_mode_header_6 *)data;
+ mph = MODE_PAGE_HEADER(mh);
+ mode_pars = MODE_PAGE_DATA(mph);
+
+ /* Print the raw mode page data with newlines each 8 bytes. */
+ for (indx = 0; indx < mph->page_length; indx++) {
+ printf("%02x%c",mode_pars[indx],
+ (((indx + 1) % 8) == 0) ? '\n' : ' ');
+ }
+ putchar('\n');
+}
+
+static void
+cleanup_editfile(void)
+{
+ if (edit_file == NULL)
+ return;
+ if (fclose(edit_file) != 0 || unlink(edit_path) != 0)
+ warn("%s", edit_path);
+ edit_file = NULL;
+}
+
+void
+mode_edit(struct cam_device *device, int page, int page_control, int dbd,
+ int edit, int binary, int retry_count, int timeout)
+{
+ const char *pagedb_path; /* Path to modepage database. */
+
+ if (edit && binary)
+ errx(EX_USAGE, "cannot edit in binary mode.");
+
+ if (! binary) {
+ if ((pagedb_path = getenv("SCSI_MODES")) == NULL)
+ pagedb_path = DEFAULT_SCSI_MODE_DB;
+
+ if (load_format(pagedb_path, page) != 0 && (edit || verbose)) {
+ if (errno == ENOENT) {
+ /* Modepage database file not found. */
+ warn("cannot open modepage database \"%s\"",
+ pagedb_path);
+ } else if (errno == ESRCH) {
+ /* Modepage entry not found in database. */
+ warnx("modepage %d not found in database"
+ "\"%s\"", page, pagedb_path);
+ }
+ /* We can recover in display mode, otherwise we exit. */
+ if (!edit) {
+ warnx("reverting to binary display only");
+ binary = 1;
+ } else
+ exit(EX_OSFILE);
+ }
+
+ editlist_populate(device, page, page_control, dbd, retry_count,
+ timeout);
+ }
+
+ if (edit) {
+ if (page_control << PAGE_CTRL_SHIFT != SMS_PAGE_CTRL_CURRENT &&
+ page_control << PAGE_CTRL_SHIFT != SMS_PAGE_CTRL_SAVED)
+ errx(EX_USAGE, "it only makes sense to edit page 0 "
+ "(current) or page 3 (saved values)");
+ modepage_edit();
+ editlist_save(device, page, page_control, dbd, retry_count,
+ timeout);
+ } else if (binary || STAILQ_EMPTY(&editlist)) {
+ /* Display without formatting information. */
+ modepage_dump(device, page, page_control, dbd, retry_count,
+ timeout);
+ } else {
+ /* Display with format. */
+ modepage_write(stdout, 0);
+ }
+}
+
+void
+mode_list(struct cam_device *device, int page_control, int dbd,
+ int retry_count, int timeout)
+{
+ u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */
+ u_int8_t *mode_pars; /* Pointer to modepage params. */
+ struct scsi_mode_header_6 *mh; /* Location of mode header. */
+ struct scsi_mode_page_header *mph;
+ struct pagename *nameentry;
+ const char *pagedb_path;
+ int len;
+
+ if ((pagedb_path = getenv("SCSI_MODES")) == NULL)
+ pagedb_path = DEFAULT_SCSI_MODE_DB;
+
+ if (load_format(pagedb_path, 0) != 0 && verbose && errno == ENOENT) {
+ /* Modepage database file not found. */
+ warn("cannot open modepage database \"%s\"", pagedb_path);
+ }
+
+ /* Build the list of all mode pages by querying the "all pages" page. */
+ mode_sense(device, SMS_ALL_PAGES_PAGE, page_control, dbd, retry_count,
+ timeout, data, sizeof(data));
+
+ mh = (struct scsi_mode_header_6 *)data;
+ len = mh->blk_desc_len; /* Skip block descriptors. */
+ /* Iterate through the pages in the reply. */
+ while (len < mh->data_length) {
+ /* Locate the next mode page header. */
+ mph = (struct scsi_mode_page_header *)
+ ((intptr_t)mh + sizeof(*mh) + len);
+ mode_pars = MODE_PAGE_DATA(mph);
+
+ mph->page_code &= SMS_PAGE_CODE;
+ nameentry = nameentry_lookup(mph->page_code);
+
+ if (nameentry == NULL || nameentry->name == NULL)
+ printf("0x%02x\n", mph->page_code);
+ else
+ printf("0x%02x\t%s\n", mph->page_code,
+ nameentry->name);
+ len += mph->page_length + sizeof(*mph);
+ }
+}
diff --git a/sbin/camcontrol/util.c b/sbin/camcontrol/util.c
new file mode 100644
index 0000000..67f4e4f
--- /dev/null
+++ b/sbin/camcontrol/util.c
@@ -0,0 +1,156 @@
+/*
+ * Written By Julian ELischer
+ * Copyright julian Elischer 1993.
+ * Permission is granted to use or redistribute this file in any way as long
+ * as this notice remains. Julian Elischer does not guarantee that this file
+ * is totally correct for any given task and users of this file must
+ * accept responsibility for any damage that occurs from the application of this
+ * file.
+ *
+ * (julian@tfs.com julian@dialix.oz.au)
+ *
+ * User SCSI hooks added by Peter Dufault:
+ *
+ * Copyright (c) 1994 HD Associates
+ * (contact: dufault@hda.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.
+ * 3. The name of HD Associates
+ * may not be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES ``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 HD ASSOCIATES 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.
+ */
+/*
+ * Taken from the original scsi(8) program.
+ * from: scsi.c,v 1.17 1998/01/12 07:57:57 charnier Exp $";
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stdint.h>
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <camlib.h>
+#include "camcontrol.h"
+
+int verbose;
+
+/* iget: Integer argument callback
+ */
+int
+iget(void *hook, char *name)
+{
+ struct get_hook *h = (struct get_hook *)hook;
+ int arg;
+
+ if (h->got >= h->argc)
+ {
+ fprintf(stderr, "Expecting an integer argument.\n");
+ usage(0);
+ exit(1);
+ }
+ arg = strtol(h->argv[h->got], 0, 0);
+ h->got++;
+
+ if (verbose && name && *name)
+ printf("%s: %d\n", name, arg);
+
+ return arg;
+}
+
+/* cget: char * argument callback
+ */
+char *
+cget(void *hook, char *name)
+{
+ struct get_hook *h = (struct get_hook *)hook;
+ char *arg;
+
+ if (h->got >= h->argc)
+ {
+ fprintf(stderr, "Expecting a character pointer argument.\n");
+ usage(0);
+ exit(1);
+ }
+ arg = h->argv[h->got];
+ h->got++;
+
+ if (verbose && name)
+ printf("cget: %s: %s", name, arg);
+
+ return arg;
+}
+
+/* arg_put: "put argument" callback
+ */
+void
+arg_put(void *hook __unused, int letter, void *arg, int count, char *name)
+{
+ if (verbose && name && *name)
+ printf("%s: ", name);
+
+ switch(letter)
+ {
+ case 'i':
+ case 'b':
+ printf("%jd ", (intmax_t)(intptr_t)arg);
+ break;
+
+ case 'c':
+ case 'z':
+ {
+ char *p;
+
+ p = malloc(count + 1);
+ if (p == NULL) {
+ fprintf(stderr, "can't malloc memory for p\n");
+ exit(1);
+ }
+
+ bzero(p, count +1);
+ strncpy(p, (char *)arg, count);
+ if (letter == 'z')
+ {
+ int i;
+ for (i = count - 1; i >= 0; i--)
+ if (p[i] == ' ')
+ p[i] = 0;
+ else
+ break;
+ }
+ printf("%s ", p);
+
+ free(p);
+ }
+
+ break;
+
+ default:
+ printf("Unknown format letter: '%c'\n", letter);
+ }
+ if (verbose)
+ putchar('\n');
+}
diff --git a/sbin/ccdconfig/Makefile b/sbin/ccdconfig/Makefile
new file mode 100644
index 0000000..ac75584
--- /dev/null
+++ b/sbin/ccdconfig/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= ccdconfig
+WARNS?= 6
+MAN= ccdconfig.8
+
+DPADD= ${LIBGEOM}
+LDADD= -lgeom
+
+.include <bsd.prog.mk>
diff --git a/sbin/ccdconfig/ccdconfig.8 b/sbin/ccdconfig/ccdconfig.8
new file mode 100644
index 0000000..4ba08be
--- /dev/null
+++ b/sbin/ccdconfig/ccdconfig.8
@@ -0,0 +1,235 @@
+.\" $NetBSD: ccdconfig.8,v 1.1.2.1 1995/11/11 02:43:33 thorpej Exp $
+.\"
+.\" Copyright (c) 1995 Jason R. Thorpe.
+.\" 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.
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 17, 1995
+.Dt CCDCONFIG 8
+.Os
+.Sh NAME
+.Nm ccdconfig
+.Nd configuration utility for the concatenated disk driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl cv
+.Ar ccd
+.Ar ileave
+.Op Ar flags
+.Ar dev ...
+.Nm
+.Fl C
+.Op Fl v
+.Op Fl f Ar config_file
+.Nm
+.Fl u
+.Op Fl v
+.Ar ccd ...
+.Nm
+.Fl U
+.Op Fl v
+.Op Fl f Ar config_file
+.Nm
+.Fl g
+.Op Ar ccd ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to dynamically configure and unconfigure concatenated disk
+devices, or ccds.
+For more information about the ccd, see
+.Xr ccd 4 .
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl c
+Configure a ccd.
+This is the default behavior of
+.Nm .
+.It Fl C
+Configure all ccd devices listed in the ccd configuration file.
+.It Fl f Ar config_file
+When configuring or unconfiguring all devices, read the file
+.Pa config_file
+instead of the default
+.Pa /etc/ccd.conf .
+.It Fl g
+Dump the current ccd configuration in a format suitable for use as the
+ccd configuration file.
+If no arguments are specified, every configured
+ccd is dumped.
+Otherwise, the configuration of each listed ccd is dumped.
+.It Fl u
+Unconfigure a ccd.
+.It Fl U
+Unconfigure all ccd devices listed the ccd configuration file.
+.It Fl v
+Cause
+.Nm
+to be verbose.
+.El
+.Pp
+A ccd is described on the command line and in the ccd configuration
+file by the name of the ccd, the interleave factor, the ccd configuration
+flags, and a list of one or more devices.
+The flags may be represented
+as a decimal number, a hexadecimal number, a comma-separated list
+of strings, or the word
+.Dq none .
+The flags are as follows:
+.Bd -literal -offset indent
+CCDF_UNIFORM 0x02 Use uniform interleave
+CCDF_MIRROR 0x04 Support mirroring
+.Ed
+.Pp
+The format in the
+configuration file appears exactly as if it were entered on the command line.
+Note that on the command line and in the configuration file, the
+.Pa flags
+argument is optional.
+.Bd -literal -offset indent
+#
+# /etc/ccd.conf
+# Configuration file for concatenated disk devices
+#
+
+# ccd ileave flags component devices
+ccd0 16 none /dev/da2s1 /dev/da3s1
+.Ed
+.Pp
+The component devices need to name partitions of type
+.Li FS_BSDFFS
+(or
+.Dq 4.2BSD
+as shown by
+.Xr disklabel 8 ) .
+.Sh FILES
+.Bl -tag -width /etc/ccd.conf -compact
+.It Pa /etc/ccd.conf
+default ccd configuration file
+.El
+.Sh EXAMPLES
+A number of
+.Nm
+examples are shown below.
+The arguments passed
+to
+.Nm
+are exactly the same as you might place in the
+.Pa /etc/ccd.conf
+configuration file.
+The first example creates a 4-disk stripe out of
+four scsi disk partitions.
+The stripe uses a 64 sector interleave.
+The second example is an example of a complex stripe/mirror combination.
+It reads as a two disk stripe of da4 and da5 which is mirrored
+to a two disk stripe of da6 and da7.
+The last example is a simple
+mirror.
+The 2nd slice of /dev/da8 is mirrored with the 3rd slice of /dev/da9
+and assigned to ccd0.
+.Pp
+.Bd -literal
+# ccdconfig ccd0 64 none /dev/da0s1 /dev/da1s1 /dev/da2s1 /dev/da3s1
+# ccdconfig ccd0 128 CCDF_MIRROR /dev/da4 /dev/da5 /dev/da6 /dev/da7
+# ccdconfig ccd0 128 CCDF_MIRROR /dev/da8s2 /dev/da9s3
+.Ed
+.Pp
+When you create a new ccd disk you generally want to
+.Xr fdisk 8
+and
+.Xr disklabel 8
+it before doing anything else.
+Once you create the initial label you can
+edit it, adding additional partitions.
+The label itself takes up the first
+16 sectors of the ccd disk.
+If all you are doing is creating file systems
+with newfs, you do not have to worry about this as newfs will skip the
+label area.
+However, if you intend to
+.Xr dd 1
+to or from a ccd partition it is usually a good idea to construct the
+partition such that it does not overlap the label area.
+For example, if
+you have A ccd disk with 10000 sectors you might create a 'd' partition
+with offset 16 and size 9984.
+.Pp
+.Bd -literal
+# disklabel ccd0 > /tmp/disklabel.ccd0
+# disklabel -Rr ccd0 /tmp/disklabel.ccd0
+# disklabel -e ccd0
+.Ed
+.Pp
+The disklabeling of a ccd disk is usually a one-time affair.
+If you reboot the machine and reconfigure the ccd disk,
+the disklabel you
+had created before will still be there and not require reinitialization.
+Beware that changing any ccd parameters: interleave, flags, or the
+device list making up the ccd disk, will usually destroy any prior
+data on that ccd disk.
+If this occurs it is usually a good idea to
+reinitialize the label before [re]constructing your ccd disk.
+.Sh RECOVERY
+An error on a ccd disk is usually unrecoverable unless you are using the
+mirroring option.
+But mirroring has its own perils: It assumes that
+both copies of the data at any given sector are the same.
+This holds true
+until a write error occurs or until you replace either side of the mirror.
+This is a poor-man's mirroring implementation.
+It works well enough that if
+you begin to get disk errors you should be able to backup the ccd disk,
+replace the broken hardware, and then regenerate the ccd disk.
+If you need
+more than this you should look into external hardware RAID SCSI boxes,
+RAID controllers (see GENERIC),
+or software RAID systems such as
+.Xr geom 8
+and
+.Xr vinum 8 .
+.Sh SEE ALSO
+.Xr dd 1 ,
+.Xr ccd 4 ,
+.Xr disklabel 8 ,
+.Xr fdisk 8 ,
+.Xr rc 8 ,
+.Xr vinum 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Nx 1.0a .
+.Sh BUGS
+The initial disklabel returned by
+.Xr ccd 4
+specifies only 3 partitions.
+One needs to change the number of partitions to 8 using
+.Dq Nm disklabel Fl e
+to get the usual
+.Bx
+expectations.
diff --git a/sbin/ccdconfig/ccdconfig.c b/sbin/ccdconfig/ccdconfig.c
new file mode 100644
index 0000000..0c584ff
--- /dev/null
+++ b/sbin/ccdconfig/ccdconfig.c
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2003 Poul-Henning Kamp
+ * Copyright (c) 1995 Jason R. Thorpe.
+ * 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 for the NetBSD Project
+ * by Jason R. Thorpe.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgeom.h>
+
+#define CCDF_UNIFORM 0x02 /* use LCCD of sizes for uniform interleave */
+#define CCDF_MIRROR 0x04 /* use mirroring */
+
+#include "pathnames.h"
+
+static int lineno = 0;
+static int verbose = 0;
+static const char *ccdconf = _PATH_CCDCONF;
+
+struct flagval {
+ const char *fv_flag;
+ int fv_val;
+} flagvaltab[] = {
+ { "CCDF_UNIFORM", CCDF_UNIFORM },
+ { "uniform", CCDF_UNIFORM },
+ { "CCDF_MIRROR", CCDF_MIRROR },
+ { "mirror", CCDF_MIRROR },
+ { "none", 0 },
+ { NULL, 0 },
+};
+
+#define CCD_CONFIG 0 /* configure a device */
+#define CCD_CONFIGALL 1 /* configure all devices */
+#define CCD_UNCONFIG 2 /* unconfigure a device */
+#define CCD_UNCONFIGALL 3 /* unconfigure all devices */
+#define CCD_DUMP 4 /* dump a ccd's configuration */
+
+static int do_single(int, char **, int);
+static int do_all(int);
+static int dump_ccd(int, char **);
+static int flags_to_val(char *);
+static int resolve_ccdname(char *);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int ch, options = 0, action = CCD_CONFIG;
+
+ while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) {
+ switch (ch) {
+ case 'c':
+ action = CCD_CONFIG;
+ ++options;
+ break;
+
+ case 'C':
+ action = CCD_CONFIGALL;
+ ++options;
+ break;
+
+ case 'f':
+ ccdconf = optarg;
+ break;
+
+ case 'g':
+ action = CCD_DUMP;
+ break;
+
+ case 'u':
+ action = CCD_UNCONFIG;
+ ++options;
+ break;
+
+ case 'U':
+ action = CCD_UNCONFIGALL;
+ ++options;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (options > 1)
+ usage();
+
+ if (modfind("g_ccd") < 0) {
+ /* Not present in kernel, try loading it */
+ if (kldload("geom_ccd") < 0 || modfind("g_ccd") < 0)
+ warn("geom_ccd module not available!");
+ }
+
+ switch (action) {
+ case CCD_CONFIG:
+ case CCD_UNCONFIG:
+ exit(do_single(argc, argv, action));
+ /* NOTREACHED */
+
+ case CCD_CONFIGALL:
+ case CCD_UNCONFIGALL:
+ exit(do_all(action));
+ /* NOTREACHED */
+
+ case CCD_DUMP:
+ exit(dump_ccd(argc, argv));
+ /* NOTREACHED */
+ }
+ /* NOTREACHED */
+ return (0);
+}
+
+static int
+do_single(int argc, char **argv, int action)
+{
+ char *cp, *cp2;
+ int ccd, noflags = 0, i, ileave, flags = 0;
+ struct gctl_req *grq;
+ char const *errstr;
+ char buf1[BUFSIZ];
+ int ex;
+
+ /*
+ * If unconfiguring, all arguments are treated as ccds.
+ */
+ if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
+ ex = 0;
+ for (i = 0; argc != 0; ) {
+ cp = *argv++; --argc;
+ if ((ccd = resolve_ccdname(cp)) < 0) {
+ warnx("invalid ccd name: %s", cp);
+ i = 1;
+ continue;
+ }
+ grq = gctl_get_handle();
+ gctl_ro_param(grq, "verb", -1, "destroy geom");
+ gctl_ro_param(grq, "class", -1, "CCD");
+ sprintf(buf1, "ccd%d", ccd);
+ gctl_ro_param(grq, "geom", -1, buf1);
+ errstr = gctl_issue(grq);
+ if (errstr == NULL) {
+ if (verbose)
+ printf("%s unconfigured\n", cp);
+ gctl_free(grq);
+ continue;
+ }
+ warnx(
+ "%s\nor possibly kernel and ccdconfig out of sync",
+ errstr);
+ ex = 1;
+ }
+ return (ex);
+ }
+
+ /* Make sure there are enough arguments. */
+ if (argc < 4) {
+ if (argc == 3) {
+ /* Assume that no flags are specified. */
+ noflags = 1;
+ } else {
+ if (action == CCD_CONFIGALL) {
+ warnx("%s: bad line: %d", ccdconf, lineno);
+ return (1);
+ } else
+ usage();
+ }
+ }
+
+ /* First argument is the ccd to configure. */
+ cp = *argv++; --argc;
+ if ((ccd = resolve_ccdname(cp)) < 0) {
+ warnx("invalid ccd name: %s", cp);
+ return (1);
+ }
+
+ /* Next argument is the interleave factor. */
+ cp = *argv++; --argc;
+ errno = 0; /* to check for ERANGE */
+ ileave = (int)strtol(cp, &cp2, 10);
+ if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
+ warnx("invalid interleave factor: %s", cp);
+ return (1);
+ }
+
+ if (noflags == 0) {
+ /* Next argument is the ccd configuration flags. */
+ cp = *argv++; --argc;
+ if ((flags = flags_to_val(cp)) < 0) {
+ warnx("invalid flags argument: %s", cp);
+ return (1);
+ }
+ }
+ grq = gctl_get_handle();
+ gctl_ro_param(grq, "verb", -1, "create geom");
+ gctl_ro_param(grq, "class", -1, "CCD");
+ gctl_ro_param(grq, "unit", sizeof(ccd), &ccd);
+ gctl_ro_param(grq, "ileave", sizeof(ileave), &ileave);
+ if (flags & CCDF_UNIFORM)
+ gctl_ro_param(grq, "uniform", -1, "");
+ if (flags & CCDF_MIRROR)
+ gctl_ro_param(grq, "mirror", -1, "");
+ gctl_ro_param(grq, "nprovider", sizeof(argc), &argc);
+ for (i = 0; i < argc; i++) {
+ sprintf(buf1, "provider%d", i);
+ cp = argv[i];
+ if (!strncmp(cp, _PATH_DEV, strlen(_PATH_DEV)))
+ cp += strlen(_PATH_DEV);
+ gctl_ro_param(grq, buf1, -1, cp);
+ }
+ gctl_rw_param(grq, "output", sizeof(buf1), buf1);
+ errstr = gctl_issue(grq);
+ if (errstr == NULL) {
+ if (verbose) {
+ printf("%s", buf1);
+ }
+ gctl_free(grq);
+ return (0);
+ }
+ warnx(
+ "%s\nor possibly kernel and ccdconfig out of sync",
+ errstr);
+ return (1);
+}
+
+static int
+do_all(int action)
+{
+ FILE *f;
+ char line[_POSIX2_LINE_MAX];
+ char *cp, **argv;
+ int argc, rval;
+ gid_t egid;
+
+ rval = 0;
+ egid = getegid();
+ setegid(getgid());
+ if ((f = fopen(ccdconf, "r")) == NULL) {
+ setegid(egid);
+ warn("fopen: %s", ccdconf);
+ return (1);
+ }
+ setegid(egid);
+
+ while (fgets(line, sizeof(line), f) != NULL) {
+ argc = 0;
+ argv = NULL;
+ ++lineno;
+ if ((cp = strrchr(line, '\n')) != NULL)
+ *cp = '\0';
+
+ /* Break up the line and pass it's contents to do_single(). */
+ if (line[0] == '\0')
+ goto end_of_line;
+ for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) {
+ if (*cp == '#')
+ break;
+ if ((argv = realloc(argv,
+ sizeof(char *) * ++argc)) == NULL) {
+ warnx("no memory to configure ccds");
+ return (1);
+ }
+ argv[argc - 1] = cp;
+ /*
+ * If our action is to unconfigure all, then pass
+ * just the first token to do_single() and ignore
+ * the rest. Since this will be encountered on
+ * our first pass through the line, the Right
+ * Thing will happen.
+ */
+ if (action == CCD_UNCONFIGALL) {
+ if (do_single(argc, argv, action))
+ rval = 1;
+ goto end_of_line;
+ }
+ }
+ if (argc != 0)
+ if (do_single(argc, argv, action))
+ rval = 1;
+
+ end_of_line:
+ if (argv != NULL)
+ free(argv);
+ }
+
+ (void)fclose(f);
+ return (rval);
+}
+
+static int
+resolve_ccdname(char *name)
+{
+
+ if (!strncmp(name, _PATH_DEV, strlen(_PATH_DEV)))
+ name += strlen(_PATH_DEV);
+ if (strncmp(name, "ccd", 3))
+ return -1;
+ name += 3;
+ if (!isdigit(*name))
+ return -1;
+ return (strtoul(name, NULL, 10));
+}
+
+static int
+dumpout(int unit)
+{
+ static int v;
+ struct gctl_req *grq;
+ int ncp;
+ char *cp;
+ char const *errstr;
+
+ grq = gctl_get_handle();
+ ncp = 65536;
+ cp = malloc(ncp);
+ gctl_ro_param(grq, "verb", -1, "list");
+ gctl_ro_param(grq, "class", -1, "CCD");
+ gctl_ro_param(grq, "unit", sizeof(unit), &unit);
+ gctl_rw_param(grq, "output", ncp, cp);
+ errstr = gctl_issue(grq);
+ if (errstr != NULL)
+ errx(1, "%s\nor possibly kernel and ccdconfig out of sync",
+ errstr);
+ if (strlen(cp) == 0)
+ errx(1, "ccd%d not configured", unit);
+ if (verbose && !v) {
+ printf("# ccd\t\tileave\tflags\tcomponent devices\n");
+ v = 1;
+ }
+ printf("%s", cp);
+ free(cp);
+ return (0);
+}
+
+static int
+dump_ccd(int argc, char **argv)
+{
+ int i, error;
+
+ if (argc == 0) {
+ error = dumpout(-1);
+ } else {
+ error = 0;
+ for (i = 0; error == 0 && i < argc; i++)
+ error = dumpout(resolve_ccdname(argv[i]));
+ }
+ return (error);
+}
+
+static int
+flags_to_val(char *flags)
+{
+ char *cp, *tok;
+ int i, tmp, val;
+
+ errno = 0; /* to check for ERANGE */
+ val = (int)strtol(flags, &cp, 0);
+ if ((errno != ERANGE) && (*cp == '\0')) {
+ if (val & ~(CCDF_UNIFORM|CCDF_MIRROR))
+ return (-1);
+ return (val);
+ }
+
+ /* Check for values represented by strings. */
+ if ((cp = strdup(flags)) == NULL)
+ err(1, "no memory to parse flags");
+ tmp = 0;
+ for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) {
+ for (i = 0; flagvaltab[i].fv_flag != NULL; ++i)
+ if (strcmp(tok, flagvaltab[i].fv_flag) == 0)
+ break;
+ if (flagvaltab[i].fv_flag == NULL) {
+ free(cp);
+ return (-1);
+ }
+ tmp |= flagvaltab[i].fv_val;
+ }
+
+ /* If we get here, the string was ok. */
+ free(cp);
+ return (tmp);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
+ "usage: ccdconfig [-cv] ccd ileave [flags] dev ...",
+ " ccdconfig -C [-v] [-f config_file]",
+ " ccdconfig -u [-v] ccd ...",
+ " ccdconfig -U [-v] [-f config_file]",
+ " ccdconfig -g [ccd ...]");
+ exit(1);
+}
diff --git a/sbin/ccdconfig/pathnames.h b/sbin/ccdconfig/pathnames.h
new file mode 100644
index 0000000..538cfed
--- /dev/null
+++ b/sbin/ccdconfig/pathnames.h
@@ -0,0 +1,38 @@
+/* $NetBSD: pathnames.h,v 1.1 1995/08/17 16:37:20 thorpej Exp $ */
+
+/*
+ * Copyright (c) 1995 Jason R. Thorpe.
+ * 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 for the NetBSD Project
+ * by Jason R. Thorpe.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#define _PATH_CCDCONF "/etc/ccd.conf"
+#define _PATH_CCDCTL "ccd.ctl"
diff --git a/sbin/clri/Makefile b/sbin/clri/Makefile
new file mode 100644
index 0000000..c68c312
--- /dev/null
+++ b/sbin/clri/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= clri
+MAN= clri.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/clri/clri.8 b/sbin/clri/clri.8
new file mode 100644
index 0000000..c3c3796
--- /dev/null
+++ b/sbin/clri/clri.8
@@ -0,0 +1,76 @@
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)clri.8 8.2 (Berkeley) 4/19/94
+.\" $FreeBSD$
+.\"
+.Dd April 19, 1994
+.Dt CLRI 8
+.Os
+.Sh NAME
+.Nm clri
+.Nd clear an inode
+.Sh SYNOPSIS
+.Nm
+.Ar special_device inode_number ...
+.Sh DESCRIPTION
+.Bf -symbolic
+The
+.Nm
+utility is obsoleted for normal file system repair work by
+.Xr fsck 8 .
+.Ef
+.Pp
+The
+.Nm
+utility zeroes out the inodes with the specified inode number(s)
+on the file system residing on the given
+.Ar special_device .
+The
+.Xr fsck 8
+utility is usually run after
+.Nm
+to reclaim the zeroed inode(s) and the
+blocks previously claimed by those inode(s).
+Both read and write permission are required on the specified
+.Ar special_device .
+.Pp
+The primary purpose of this routine
+is to remove a file which
+for some reason is not being properly handled by
+.Xr fsck 8 .
+Once removed,
+it is anticipated that
+.Xr fsck 8
+will be able to clean up the resulting mess.
+.Sh SEE ALSO
+.Xr fsck 8 ,
+.Xr fsdb 8
+.Sh BUGS
+If the file is open, the work of
+.Nm
+will be lost when the inode is written back to disk from the inode cache.
diff --git a/sbin/clri/clri.c b/sbin/clri/clri.c
new file mode 100644
index 0000000..dc77a41
--- /dev/null
+++ b/sbin/clri/clri.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rich $alz of BBN 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)clri.c 8.2 (Berkeley) 9/23/93";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/disklabel.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+
+/*
+ * Possible superblock locations ordered from most to least likely.
+ */
+static int sblock_try[] = SBLOCKSEARCH;
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: clri special_device inode_number ...\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct fs *sbp;
+ struct ufs1_dinode *dp1;
+ struct ufs2_dinode *dp2;
+ char *ibuf[MAXBSIZE];
+ long generation, bsize;
+ off_t offset;
+ int i, fd, inonum;
+ char *fs, sblock[SBLOCKSIZE];
+
+ if (argc < 3)
+ usage();
+
+ fs = *++argv;
+ sbp = NULL;
+
+ /* get the superblock. */
+ if ((fd = open(fs, O_RDWR, 0)) < 0)
+ err(1, "%s", fs);
+ for (i = 0; sblock_try[i] != -1; i++) {
+ if (lseek(fd, (off_t)(sblock_try[i]), SEEK_SET) < 0)
+ err(1, "%s", fs);
+ if (read(fd, sblock, sizeof(sblock)) != sizeof(sblock))
+ errx(1, "%s: can't read superblock", fs);
+ sbp = (struct fs *)sblock;
+ if ((sbp->fs_magic == FS_UFS1_MAGIC ||
+ (sbp->fs_magic == FS_UFS2_MAGIC &&
+ sbp->fs_sblockloc == sblock_try[i])) &&
+ sbp->fs_bsize <= MAXBSIZE &&
+ sbp->fs_bsize >= (int)sizeof(struct fs))
+ break;
+ }
+ if (sblock_try[i] == -1)
+ errx(2, "cannot find file system superblock");
+ bsize = sbp->fs_bsize;
+
+ /* remaining arguments are inode numbers. */
+ while (*++argv) {
+ /* get the inode number. */
+ if ((inonum = atoi(*argv)) <= 0)
+ errx(1, "%s is not a valid inode number", *argv);
+ (void)printf("clearing %d\n", inonum);
+
+ /* read in the appropriate block. */
+ offset = ino_to_fsba(sbp, inonum); /* inode to fs blk */
+ offset = fsbtodb(sbp, offset); /* fs blk disk blk */
+ offset *= DEV_BSIZE; /* disk blk to bytes */
+
+ /* seek and read the block */
+ if (lseek(fd, offset, SEEK_SET) < 0)
+ err(1, "%s", fs);
+ if (read(fd, ibuf, bsize) != bsize)
+ err(1, "%s", fs);
+
+ if (sbp->fs_magic == FS_UFS2_MAGIC) {
+ /* get the inode within the block. */
+ dp2 = &(((struct ufs2_dinode *)ibuf)
+ [ino_to_fsbo(sbp, inonum)]);
+
+ /* clear the inode, and bump the generation count. */
+ generation = dp2->di_gen + 1;
+ memset(dp2, 0, sizeof(*dp2));
+ dp2->di_gen = generation;
+ } else {
+ /* get the inode within the block. */
+ dp1 = &(((struct ufs1_dinode *)ibuf)
+ [ino_to_fsbo(sbp, inonum)]);
+
+ /* clear the inode, and bump the generation count. */
+ generation = dp1->di_gen + 1;
+ memset(dp1, 0, sizeof(*dp1));
+ dp1->di_gen = generation;
+ }
+
+ /* backup and write the block */
+ if (lseek(fd, (off_t)-bsize, SEEK_CUR) < 0)
+ err(1, "%s", fs);
+ if (write(fd, ibuf, bsize) != bsize)
+ err(1, "%s", fs);
+ (void)fsync(fd);
+ }
+ (void)close(fd);
+ exit(0);
+}
diff --git a/sbin/comcontrol/Makefile b/sbin/comcontrol/Makefile
new file mode 100644
index 0000000..a7720d8
--- /dev/null
+++ b/sbin/comcontrol/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 5.4 (Berkeley) 6/5/91
+# $FreeBSD$
+
+PROG= comcontrol
+MAN= comcontrol.8
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/sbin/comcontrol/comcontrol.8 b/sbin/comcontrol/comcontrol.8
new file mode 100644
index 0000000..87a7630
--- /dev/null
+++ b/sbin/comcontrol/comcontrol.8
@@ -0,0 +1,65 @@
+.\" $FreeBSD$
+.Dd May 15, 1994
+.Dt COMCONTROL 8
+.Os
+.Sh NAME
+.Nm comcontrol
+.Nd control a special tty device
+.Sh SYNOPSIS
+.Nm
+.Ar special_device
+.Op dtrwait Ar number
+.Op drainwait Ar number
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to examine and modify some of the special characteristics
+of the specified tty device.
+If no arguments other than the device (or "-" for stdin)
+are specified,
+it prints the settings of all controllable characteristics.
+This usage requires only read access on the device.
+Only the superuser can change the settings.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Cm dtrwait Ar number
+Set the time to wait after dropping DTR
+to the given number.
+The units are hundredths of a second.
+The default is 300 hundredths, i.e., 3 seconds.
+This option needed mainly to set proper recover time after
+modem reset.
+.It Cm drainwait Ar number
+Set the time to wait for output drain
+to the given number.
+The units are seconds.
+The default is 5 minutes, 0 means
+waiting forever.
+This option needed mainly to specify upper limit of minutes
+to prevent modem hanging.
+.El
+.Pp
+The standard way to use
+.Nm
+is to put invocations of it in the
+.Pa /etc/rc.d/serial
+startup script.
+.Sh FILES
+.Bl -tag -width /dev/ttyd? -compact
+.It Pa /dev/ttyd?
+dialin devices, hardwired terminals
+.It Pa /dev/cuad?
+dialout devices
+.El
+.Sh SEE ALSO
+.Xr stty 1 ,
+.Xr sio 4
+.Sh HISTORY
+Originally part of cgd's com package patches, version 0.2.1, to
+.Bx 386 0.1 .
+Once controlled bidirectional capabilities.
+Little is left to control now
+that these capabilities are standard.
+.Sh AUTHORS
+.An Christopher G. Demetriou
diff --git a/sbin/comcontrol/comcontrol.c b/sbin/comcontrol/comcontrol.c
new file mode 100644
index 0000000..753ded3
--- /dev/null
+++ b/sbin/comcontrol/comcontrol.c
@@ -0,0 +1,128 @@
+/*-
+ * Copyright (c) 1992 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 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 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+static void usage(void);
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: comcontrol <filename> [dtrwait <n>] [drainwait <n>]\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ int res = 0;
+ int print_dtrwait = 1, print_drainwait = 1;
+ int dtrwait = -1, drainwait = -1;
+
+ if (argc < 2)
+ usage();
+
+ if (strcmp(argv[1], "-") == 0)
+ fd = STDIN_FILENO;
+ else {
+ fd = open(argv[1], O_RDONLY|O_NONBLOCK, 0);
+ if (fd < 0) {
+ warn("couldn't open file %s", argv[1]);
+ return 1;
+ }
+ }
+ if (argc == 2) {
+ if (ioctl(fd, TIOCMGDTRWAIT, &dtrwait) < 0) {
+ print_dtrwait = 0;
+ if (errno != ENOTTY) {
+ res = 1;
+ warn("TIOCMGDTRWAIT");
+ }
+ }
+ if (ioctl(fd, TIOCGDRAINWAIT, &drainwait) < 0) {
+ print_drainwait = 0;
+ if (errno != ENOTTY) {
+ res = 1;
+ warn("TIOCGDRAINWAIT");
+ }
+ }
+ if (print_dtrwait)
+ printf("dtrwait %d ", dtrwait);
+ if (print_drainwait)
+ printf("drainwait %d ", drainwait);
+ printf("\n");
+ } else {
+ while (argv[2] != NULL) {
+ if (!strcmp(argv[2],"dtrwait")) {
+ if (dtrwait >= 0)
+ usage();
+ if (argv[3] == NULL || !isdigit(argv[3][0]))
+ usage();
+ dtrwait = atoi(argv[3]);
+ argv += 2;
+ } else if (!strcmp(argv[2],"drainwait")) {
+ if (drainwait >= 0)
+ usage();
+ if (argv[3] == NULL || !isdigit(argv[3][0]))
+ usage();
+ drainwait = atoi(argv[3]);
+ argv += 2;
+ } else
+ usage();
+ }
+ if (dtrwait >= 0) {
+ if (ioctl(fd, TIOCMSDTRWAIT, &dtrwait) < 0) {
+ res = 1;
+ warn("TIOCMSDTRWAIT");
+ }
+ }
+ if (drainwait >= 0) {
+ if (ioctl(fd, TIOCSDRAINWAIT, &drainwait) < 0) {
+ res = 1;
+ warn("TIOCSDRAINWAIT");
+ }
+ }
+ }
+
+ close(fd);
+ return res;
+}
diff --git a/sbin/conscontrol/Makefile b/sbin/conscontrol/Makefile
new file mode 100644
index 0000000..ddd2434
--- /dev/null
+++ b/sbin/conscontrol/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= conscontrol
+MAN= conscontrol.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/conscontrol/conscontrol.8 b/sbin/conscontrol/conscontrol.8
new file mode 100644
index 0000000..726041e
--- /dev/null
+++ b/sbin/conscontrol/conscontrol.8
@@ -0,0 +1,115 @@
+.\"
+.\" Copyright (c) 2001 Jonathan Lemon <jlemon@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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 23, 2001
+.Dt CONSCONTROL 8
+.Os
+.Sh NAME
+.Nm conscontrol
+.Nd control physical console devices
+.Sh SYNOPSIS
+.Nm
+.Op Cm list
+.Nm
+.Cm mute on | off
+.Nm
+.Cm add | delete
+.Ar console
+.Nm
+.Cm set Ar console | Cm unset
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to examine and modify the physical devices which back
+the virtual console devices.
+If no arguments
+(or only the
+.Cm list
+command)
+are specified,
+the current console settings are shown.
+.Pp
+There are two types of logical consoles; a high level console which
+is represented by
+.Pa /dev/console ,
+and a low level console.
+The low level console is used for kernel
+.Xr printf 9
+and
+.Xr ddb 4
+debugger support,
+while the high level console is used by user programs like
+.Xr syslogd 8 .
+Multiple device support is implemented only for the low level console;
+the high level console is set to the first device in the console list.
+.Pp
+Multiple console support may be invoked by passing the kernel the
+.Fl D
+flag from the boot loader, or by using
+.Nm
+to change the list of console devices after the system has booted.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Cm add | delete Ar console
+Add or delete a physical device from the logical console.
+The device must support low-level console operations.
+Adding a device will place it at the front of the list of console
+devices; the first device is used for the high level console.
+.Pp
+The
+.Ar console
+argument
+is the name of a console device in
+.Pa /dev ;
+the name of the directory may be omitted.
+.It Cm mute on | off
+Change the state of console muting.
+All console output is suppressed when console muting is
+.Cm on .
+.It Cm set Ar console | Cm unset
+Set or unset the virtual console.
+When unset, output from the system, such as the kernel
+.Xr printf 9 ,
+always goes out to the real main console.
+When set, it goes to another.
+This is an interface to the tty ioctl
+.Dv TIOCCONS .
+.El
+.Sh SEE ALSO
+.Xr sio 4 ,
+.Xr syscons 4 ,
+.Xr tty 4 ,
+.Xr boot 8 ,
+.Xr loader 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.0 .
+.Sh AUTHORS
+.An Jonathan Lemon
diff --git a/sbin/conscontrol/conscontrol.c b/sbin/conscontrol/conscontrol.c
new file mode 100644
index 0000000..d8e5bc5
--- /dev/null
+++ b/sbin/conscontrol/conscontrol.c
@@ -0,0 +1,208 @@
+/*-
+ * Copyright (c) 2001 Jonathan Lemon <jlemon@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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+#include <sys/ttycom.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define DEVDIR "/dev/"
+
+static void __dead2
+usage(void)
+{
+
+ (void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: conscontrol [list]",
+ " conscontrol mute on | off",
+ " conscontrol add | delete console",
+ " conscontrol set console | unset");
+ exit(1);
+}
+
+static void
+consstatus(void)
+{
+ int mute;
+ size_t len;
+ char *buf, *p, *avail;
+
+ len = sizeof(mute);
+ if (sysctlbyname("kern.consmute", &mute, &len, NULL, 0) == -1)
+ err(1, "kern.consmute retrieval failed");
+ if (sysctlbyname("kern.console", NULL, &len, NULL, 0) == -1)
+ err(1, "kern.console estimate failed");
+ if ((buf = malloc(len)) == NULL)
+ errx(1, "kern.console malloc failed");
+ if (sysctlbyname("kern.console", buf, &len, NULL, 0) == -1)
+ err(1, "kern.console retrieval failed");
+ if ((avail = strchr(buf, '/')) == NULL)
+ errx(1, "kern.console format not understood");
+ p = avail;
+ *avail++ = '\0';
+ if (p != buf)
+ *--p = '\0'; /* remove trailing ',' */
+ p = avail + strlen(avail);
+ if (p != avail)
+ *--p = '\0'; /* remove trailing ',' */
+ printf("Configured: %s\n", buf);
+ printf(" Available: %s\n", avail);
+ printf(" Muting: %s\n", mute ? "on" : "off");
+ free(buf);
+}
+
+static void
+consmute(const char *onoff)
+{
+ int mute;
+ size_t len;
+
+ if (strcmp(onoff, "on") == 0)
+ mute = 1;
+ else if (strcmp(onoff, "off") == 0)
+ mute = 0;
+ else
+ usage();
+ len = sizeof(mute);
+ if (sysctlbyname("kern.consmute", NULL, NULL, &mute, len) == -1)
+ err(1, "could not change console muting");
+}
+
+/*
+ * The name we supply to the sysctls should be an entry in /dev. If
+ * the user has specified the full pathname in /dev, DTRT. If he
+ * specifies a name in some other directory, it's an error.
+ */
+
+static char*
+stripdev(char *devnam)
+{
+ if (memcmp (devnam, DEVDIR, strlen(DEVDIR)) == 0)
+ return (&devnam[strlen(DEVDIR)]); /* remove /dev */
+ else if (strchr (devnam, '/')) {
+ fprintf(stderr, "Not a device in /dev: %s\n", devnam);
+ return (NULL); /* end of string */
+ } else
+ return (devnam); /* passed */
+}
+
+static void
+consadd(char *devnam)
+{
+ size_t len;
+
+ devnam = stripdev(devnam);
+ if (devnam == NULL)
+ return;
+ len = strlen(devnam);
+ if (sysctlbyname("kern.console", NULL, NULL, devnam, len) == -1)
+ err(1, "could not add %s as a console", devnam);
+}
+
+static void
+consdel(char *devnam)
+{
+ char *buf;
+ size_t len;
+
+ devnam = stripdev(devnam);
+ if (devnam == NULL)
+ return;
+ len = strlen(devnam) + sizeof("-");
+ if ((buf = malloc(len)) == NULL)
+ errx(1, "malloc failed");
+ buf[0] = '-';
+ strcpy(buf + 1, devnam);
+ if (sysctlbyname("kern.console", NULL, NULL, buf, len) == -1)
+ err(1, "could not remove %s as a console", devnam);
+ free(buf);
+}
+
+static void
+consset(char *devnam)
+{
+ int ttyfd, flag = 1;
+
+ ttyfd = open(devnam, O_RDONLY);
+ if (ttyfd == -1)
+ err(1, "opening %s", devnam);
+ if (ioctl(ttyfd, TIOCCONS, &flag) == -1)
+ err(1, "could not set %s as virtual console", devnam);
+ close(ttyfd);
+}
+
+static void
+consunset(void)
+{
+ int ttyfd, flag = 0;
+
+ ttyfd = open(DEVDIR "console", O_RDONLY);
+ if (ttyfd == -1)
+ err(1, "opening virtual console");
+ if (ioctl(ttyfd, TIOCCONS, &flag) == -1)
+ err(1, "could not unset virtual console");
+ close(ttyfd);
+}
+
+int
+main(int argc, char **argv)
+{
+
+ if (getopt(argc, argv, "") != -1)
+ usage();
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0 && strcmp(argv[0], "list") != 0) {
+ if (argc == 1 && strcmp(argv[0], "unset") == 0)
+ consunset();
+ else if (argc != 2)
+ usage();
+ else if (strcmp(argv[0], "mute") == 0)
+ consmute(argv[1]);
+ else if (strcmp(argv[0], "add") == 0)
+ consadd(argv[1]);
+ else if (strcmp(argv[0], "delete") == 0)
+ consdel(argv[1]);
+ else if (strcmp(argv[0], "set") == 0)
+ consset(argv[1]);
+ else
+ usage();
+ }
+ consstatus();
+ exit(0);
+}
diff --git a/sbin/devd/Makefile b/sbin/devd/Makefile
new file mode 100644
index 0000000..8f52d90
--- /dev/null
+++ b/sbin/devd/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+PROG_CXX=devd
+SRCS= devd.cc token.l parse.y y.tab.h
+MAN= devd.8 devd.conf.5
+WARNS?= 4
+
+NO_SHARED?=YES
+
+DPADD= ${LIBL} ${LIBUTIL}
+LDADD= -ll -lutil
+
+YFLAGS+=-v
+CFLAGS+=-I. -I${.CURDIR}
+
+CLEANFILES= y.output
+
+.include <bsd.prog.mk>
diff --git a/sbin/devd/devd.8 b/sbin/devd/devd.8
new file mode 100644
index 0000000..494995e
--- /dev/null
+++ b/sbin/devd/devd.8
@@ -0,0 +1,148 @@
+.\"
+.\" Copyright (c) 2002 M. Warner Losh.
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 24, 2005
+.Dt DEVD 8
+.Os
+.Sh NAME
+.Nm devd
+.Nd "device state change daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl Dd
+.Op Fl f Ar file
+.Op Fl n
+.Sh DESCRIPTION
+The
+.Nm
+daemon provides a way to have userland programs run when certain
+kernel events happen.
+.Pp
+The following options are accepted.
+.Bl -tag -width indent-two
+.It Fl D
+Enable debugging messages.
+.It Fl d
+Run in the foreground instead of becoming a daemon.
+.It Fl f Ar file
+Use configuration file
+.Ar file
+instead of the default
+.Pa /etc/devd.conf .
+If option
+.Fl f
+is specified more than once, the last file specified is used.
+.It Fl n
+Do not process all pending events before becoming a daemon.
+Instead, call daemon right away.
+.El
+.Sh IMPLEMENTATION NOTES
+The
+.Nm
+utility
+is a system daemon that runs in the background all the time.
+Whenever a device is added to or removed from the device tree,
+.Nm
+will execute actions specified in
+.Xr devd.conf 5 .
+For example,
+.Nm
+might execute
+.Xr dhclient 8
+when an Ethernet adapter is added to the system, and kill the
+.Xr dhclient 8
+instance when the same adapter is removed.
+Another example would be for
+.Nm
+to use a table to locate and load via
+.Xr kldload 8
+the proper driver for an unrecognized device that is added to the system.
+.Pp
+The
+.Nm
+utility
+hooks into the
+.Xr devctl 4
+device driver.
+This device driver has hooks into the device configuration system.
+When nodes are added or deleted from the tree, this device will
+deliver information about the event to
+.Nm .
+Once
+.Nm
+has parsed the message, it will search its action list for that kind
+of event and perform the action with the highest matching value.
+For most mundane uses, the default handlers are adequate.
+However, for more advanced users, the power is present to tweak every
+aspect of what happens.
+.Pp
+The
+.Nm
+utility
+reads
+.Pa /etc/devd.conf
+or the alternate configuration file specified with a
+.Fl f
+option and uses that file to drive the rest of the process.
+While the format of this file is described in
+.Xr devd.conf 5 ,
+some basics are covered here.
+In the
+.Ic options
+section, one can define multiple directories to search
+for config files.
+All files in these directories whose names match the pattern
+.Pa *.conf
+are parsed.
+These files are intended to be installed by third party vendors that
+wish to hook into the
+.Nm
+system without modifying the user's other
+config files.
+.Pp
+All messages that
+.Nm
+receives are forwarded to the
+.Ux
+domain socket at
+.Pa /var/run/devd.pipe .
+.Sh FILES
+.Bl -tag -width ".Pa /var/run/devd.pipe" -compact
+.It Pa /etc/devd.conf
+The default
+.Nm
+configuration file.
+.It Pa /var/run/devd.pipe
+The socket used by
+.Nm
+to communicate with its clients.
+.El
+.Sh SEE ALSO
+.Xr devctl 4 ,
+.Xr devd.conf 5
+.Sh AUTHORS
+.An M. Warner Losh
diff --git a/sbin/devd/devd.cc b/sbin/devd/devd.cc
new file mode 100644
index 0000000..3dadde4
--- /dev/null
+++ b/sbin/devd/devd.cc
@@ -0,0 +1,964 @@
+/*-
+ * Copyright (c) 2002-2003 M. Warner Losh.
+ * 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.
+ */
+
+/*
+ * DEVD control daemon.
+ */
+
+// TODO list:
+// o devd.conf and devd man pages need a lot of help:
+// - devd needs to document the unix domain socket
+// - devd.conf needs more details on the supported statements.
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <err.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <list>
+#include <vector>
+
+#include "devd.h" /* C compatible definitions */
+#include "devd.hh" /* C++ class definitions */
+
+#define PIPE "/var/run/devd.pipe"
+#define CF "/etc/devd.conf"
+#define SYSCTL "hw.bus.devctl_disable"
+
+using namespace std;
+
+extern FILE *yyin;
+extern int lineno;
+
+static const char notify = '!';
+static const char nomatch = '?';
+static const char attach = '+';
+static const char detach = '-';
+
+static struct pidfh *pfh;
+
+int Dflag;
+int dflag;
+int nflag;
+int romeo_must_die = 0;
+
+static const char *configfile = CF;
+
+static void event_loop(void);
+static void usage(void);
+
+template <class T> void
+delete_and_clear(vector<T *> &v)
+{
+ typename vector<T *>::const_iterator i;
+
+ for (i = v.begin(); i != v.end(); i++)
+ delete *i;
+ v.clear();
+}
+
+config cfg;
+
+event_proc::event_proc() : _prio(-1)
+{
+ // nothing
+}
+
+event_proc::~event_proc()
+{
+ delete_and_clear(_epsvec);
+}
+
+void
+event_proc::add(eps *eps)
+{
+ _epsvec.push_back(eps);
+}
+
+bool
+event_proc::matches(config &c)
+{
+ vector<eps *>::const_iterator i;
+
+ for (i = _epsvec.begin(); i != _epsvec.end(); i++)
+ if (!(*i)->do_match(c))
+ return (false);
+ return (true);
+}
+
+bool
+event_proc::run(config &c)
+{
+ vector<eps *>::const_iterator i;
+
+ for (i = _epsvec.begin(); i != _epsvec.end(); i++)
+ if (!(*i)->do_action(c))
+ return (false);
+ return (true);
+}
+
+action::action(const char *cmd)
+ : _cmd(cmd)
+{
+ // nothing
+}
+
+action::~action()
+{
+ // nothing
+}
+
+bool
+action::do_action(config &c)
+{
+ string s = c.expand_string(_cmd);
+ if (Dflag)
+ fprintf(stderr, "Executing '%s'\n", s.c_str());
+ ::system(s.c_str());
+ return (true);
+}
+
+match::match(config &c, const char *var, const char *re)
+ : _var(var)
+{
+ string pattern = re;
+ _re = "^";
+ _re.append(c.expand_string(string(re)));
+ _re.append("$");
+ regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE);
+}
+
+match::~match()
+{
+ regfree(&_regex);
+}
+
+bool
+match::do_match(config &c)
+{
+ string value = c.get_variable(_var);
+ bool retval;
+
+ if (Dflag)
+ fprintf(stderr, "Testing %s=%s against %s\n", _var.c_str(),
+ value.c_str(), _re.c_str());
+
+ retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0);
+ return retval;
+}
+
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <net/if_media.h>
+
+media::media(config &, const char *var, const char *type)
+ : _var(var), _type(-1)
+{
+ static struct ifmedia_description media_types[] = {
+ { IFM_ETHER, "Ethernet" },
+ { IFM_TOKEN, "Tokenring" },
+ { IFM_FDDI, "FDDI" },
+ { IFM_IEEE80211, "802.11" },
+ { IFM_ATM, "ATM" },
+ { IFM_CARP, "CARP" },
+ { -1, "unknown" },
+ { 0, NULL },
+ };
+ for (int i = 0; media_types[i].ifmt_string != NULL; i++)
+ if (strcasecmp(type, media_types[i].ifmt_string) == 0) {
+ _type = media_types[i].ifmt_word;
+ break;
+ }
+}
+
+media::~media()
+{
+}
+
+bool
+media::do_match(config &c)
+{
+ string value;
+ struct ifmediareq ifmr;
+ bool retval;
+ int s;
+
+ // Since we can be called from both a device attach/detach
+ // context where device-name is defined and what we want,
+ // as well as from a link status context, where subsystem is
+ // the name of interest, first try device-name and fall back
+ // to subsystem if none exists.
+ value = c.get_variable("device-name");
+ if (value.length() == 0)
+ value = c.get_variable("subsystem");
+ if (Dflag)
+ fprintf(stderr, "Testing media type of %s against 0x%x\n",
+ value.c_str(), _type);
+
+ retval = false;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s >= 0) {
+ memset(&ifmr, 0, sizeof(ifmr));
+ strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name));
+
+ if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 &&
+ ifmr.ifm_status & IFM_AVALID) {
+ if (Dflag)
+ fprintf(stderr, "%s has media type 0x%x\n",
+ value.c_str(), IFM_TYPE(ifmr.ifm_active));
+ retval = (IFM_TYPE(ifmr.ifm_active) == _type);
+ } else if (_type == -1) {
+ if (Dflag)
+ fprintf(stderr, "%s has unknown media type\n",
+ value.c_str());
+ retval = true;
+ }
+ close(s);
+ }
+
+ return retval;
+}
+
+const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_";
+const string var_list::nothing = "";
+
+const string &
+var_list::get_variable(const string &var) const
+{
+ map<string, string>::const_iterator i;
+
+ i = _vars.find(var);
+ if (i == _vars.end())
+ return (var_list::bogus);
+ return (i->second);
+}
+
+bool
+var_list::is_set(const string &var) const
+{
+ return (_vars.find(var) != _vars.end());
+}
+
+void
+var_list::set_variable(const string &var, const string &val)
+{
+ if (Dflag)
+ fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str());
+ _vars[var] = val;
+}
+
+void
+config::reset(void)
+{
+ _dir_list.clear();
+ delete_and_clear(_var_list_table);
+ delete_and_clear(_attach_list);
+ delete_and_clear(_detach_list);
+ delete_and_clear(_nomatch_list);
+ delete_and_clear(_notify_list);
+}
+
+void
+config::parse_one_file(const char *fn)
+{
+ if (Dflag)
+ printf("Parsing %s\n", fn);
+ yyin = fopen(fn, "r");
+ if (yyin == NULL)
+ err(1, "Cannot open config file %s", fn);
+ if (yyparse() != 0)
+ errx(1, "Cannot parse %s at line %d", fn, lineno);
+ fclose(yyin);
+}
+
+void
+config::parse_files_in_dir(const char *dirname)
+{
+ DIR *dirp;
+ struct dirent *dp;
+ char path[PATH_MAX];
+
+ if (Dflag)
+ printf("Parsing files in %s\n", dirname);
+ dirp = opendir(dirname);
+ if (dirp == NULL)
+ return;
+ readdir(dirp); /* Skip . */
+ readdir(dirp); /* Skip .. */
+ while ((dp = readdir(dirp)) != NULL) {
+ if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) {
+ snprintf(path, sizeof(path), "%s/%s",
+ dirname, dp->d_name);
+ parse_one_file(path);
+ }
+ }
+}
+
+class epv_greater {
+public:
+ int operator()(event_proc *const&l1, event_proc *const&l2)
+ {
+ return (l1->get_priority() > l2->get_priority());
+ }
+};
+
+void
+config::sort_vector(vector<event_proc *> &v)
+{
+ sort(v.begin(), v.end(), epv_greater());
+}
+
+void
+config::parse(void)
+{
+ vector<string>::const_iterator i;
+
+ parse_one_file(configfile);
+ for (i = _dir_list.begin(); i != _dir_list.end(); i++)
+ parse_files_in_dir((*i).c_str());
+ sort_vector(_attach_list);
+ sort_vector(_detach_list);
+ sort_vector(_nomatch_list);
+ sort_vector(_notify_list);
+}
+
+void
+config::open_pidfile()
+{
+ pid_t otherpid;
+
+ if (_pidfile == "")
+ return;
+ pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid);
+ if (pfh == NULL) {
+ if (errno == EEXIST)
+ errx(1, "devd already running, pid: %d", (int)otherpid);
+ warn("cannot open pid file");
+ }
+}
+
+void
+config::write_pidfile()
+{
+
+ pidfile_write(pfh);
+}
+
+void
+config::remove_pidfile()
+{
+
+ pidfile_remove(pfh);
+}
+
+void
+config::add_attach(int prio, event_proc *p)
+{
+ p->set_priority(prio);
+ _attach_list.push_back(p);
+}
+
+void
+config::add_detach(int prio, event_proc *p)
+{
+ p->set_priority(prio);
+ _detach_list.push_back(p);
+}
+
+void
+config::add_directory(const char *dir)
+{
+ _dir_list.push_back(string(dir));
+}
+
+void
+config::add_nomatch(int prio, event_proc *p)
+{
+ p->set_priority(prio);
+ _nomatch_list.push_back(p);
+}
+
+void
+config::add_notify(int prio, event_proc *p)
+{
+ p->set_priority(prio);
+ _notify_list.push_back(p);
+}
+
+void
+config::set_pidfile(const char *fn)
+{
+ _pidfile = string(fn);
+}
+
+void
+config::push_var_table()
+{
+ var_list *vl;
+
+ vl = new var_list();
+ _var_list_table.push_back(vl);
+ if (Dflag)
+ fprintf(stderr, "Pushing table\n");
+}
+
+void
+config::pop_var_table()
+{
+ delete _var_list_table.back();
+ _var_list_table.pop_back();
+ if (Dflag)
+ fprintf(stderr, "Popping table\n");
+}
+
+void
+config::set_variable(const char *var, const char *val)
+{
+ _var_list_table.back()->set_variable(var, val);
+}
+
+const string &
+config::get_variable(const string &var)
+{
+ vector<var_list *>::reverse_iterator i;
+
+ for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); i++) {
+ if ((*i)->is_set(var))
+ return ((*i)->get_variable(var));
+ }
+ return (var_list::nothing);
+}
+
+bool
+config::is_id_char(char ch)
+{
+ return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' ||
+ ch == '-'));
+}
+
+void
+config::expand_one(const char *&src, string &dst)
+{
+ int count;
+ string buffer, varstr;
+
+ src++;
+ // $$ -> $
+ if (*src == '$') {
+ dst.append(src++, 1);
+ return;
+ }
+
+ // $(foo) -> $(foo)
+ // Not sure if I want to support this or not, so for now we just pass
+ // it through.
+ if (*src == '(') {
+ dst.append("$");
+ count = 1;
+ /* If the string ends before ) is matched , return. */
+ while (count > 0 && *src) {
+ if (*src == ')')
+ count--;
+ else if (*src == '(')
+ count++;
+ dst.append(src++, 1);
+ }
+ return;
+ }
+
+ // ${^A-Za-z] -> $\1
+ if (!isalpha(*src)) {
+ dst.append("$");
+ dst.append(src++, 1);
+ return;
+ }
+
+ // $var -> replace with value
+ do {
+ buffer.append(src++, 1);
+ } while (is_id_char(*src));
+ buffer.append("", 1);
+ varstr = get_variable(buffer.c_str());
+ dst.append(varstr);
+}
+
+const string
+config::expand_string(const string &s)
+{
+ const char *src;
+ string dst;
+
+ src = s.c_str();
+ while (*src) {
+ if (*src == '$')
+ expand_one(src, dst);
+ else
+ dst.append(src++, 1);
+ }
+ dst.append("", 1);
+
+ return (dst);
+}
+
+bool
+config::chop_var(char *&buffer, char *&lhs, char *&rhs)
+{
+ char *walker;
+
+ if (*buffer == '\0')
+ return (false);
+ walker = lhs = buffer;
+ while (is_id_char(*walker))
+ walker++;
+ if (*walker != '=')
+ return (false);
+ walker++; // skip =
+ if (*walker == '"') {
+ walker++; // skip "
+ rhs = walker;
+ while (*walker && *walker != '"')
+ walker++;
+ if (*walker != '"')
+ return (false);
+ rhs[-2] = '\0';
+ *walker++ = '\0';
+ } else {
+ rhs = walker;
+ while (*walker && !isspace(*walker))
+ walker++;
+ if (*walker != '\0')
+ *walker++ = '\0';
+ rhs[-1] = '\0';
+ }
+ while (isspace(*walker))
+ walker++;
+ buffer = walker;
+ return (true);
+}
+
+
+char *
+config::set_vars(char *buffer)
+{
+ char *lhs;
+ char *rhs;
+
+ while (1) {
+ if (!chop_var(buffer, lhs, rhs))
+ break;
+ set_variable(lhs, rhs);
+ }
+ return (buffer);
+}
+
+void
+config::find_and_execute(char type)
+{
+ vector<event_proc *> *l;
+ vector<event_proc *>::const_iterator i;
+ const char *s;
+
+ switch (type) {
+ default:
+ return;
+ case notify:
+ l = &_notify_list;
+ s = "notify";
+ break;
+ case nomatch:
+ l = &_nomatch_list;
+ s = "nomatch";
+ break;
+ case attach:
+ l = &_attach_list;
+ s = "attach";
+ break;
+ case detach:
+ l = &_detach_list;
+ s = "detach";
+ break;
+ }
+ if (Dflag)
+ fprintf(stderr, "Processing %s event\n", s);
+ for (i = l->begin(); i != l->end(); i++) {
+ if ((*i)->matches(*this)) {
+ (*i)->run(*this);
+ break;
+ }
+ }
+
+}
+
+
+static void
+process_event(char *buffer)
+{
+ char type;
+ char *sp;
+
+ sp = buffer + 1;
+ if (Dflag)
+ fprintf(stderr, "Processing event '%s'\n", buffer);
+ type = *buffer++;
+ cfg.push_var_table();
+ // No match doesn't have a device, and the format is a little
+ // different, so handle it separately.
+ switch (type) {
+ case notify:
+ sp = cfg.set_vars(sp);
+ break;
+ case nomatch:
+ //? at location pnp-info on bus
+ sp = strchr(sp, ' ');
+ if (sp == NULL)
+ return; /* Can't happen? */
+ *sp++ = '\0';
+ if (strncmp(sp, "at ", 3) == 0)
+ sp += 3;
+ sp = cfg.set_vars(sp);
+ if (strncmp(sp, "on ", 3) == 0)
+ cfg.set_variable("bus", sp + 3);
+ break;
+ case attach: /*FALLTHROUGH*/
+ case detach:
+ sp = strchr(sp, ' ');
+ if (sp == NULL)
+ return; /* Can't happen? */
+ *sp++ = '\0';
+ cfg.set_variable("device-name", buffer);
+ if (strncmp(sp, "at ", 3) == 0)
+ sp += 3;
+ sp = cfg.set_vars(sp);
+ if (strncmp(sp, "on ", 3) == 0)
+ cfg.set_variable("bus", sp + 3);
+ break;
+ }
+
+ cfg.find_and_execute(type);
+ cfg.pop_var_table();
+}
+
+int
+create_socket(const char *name)
+{
+ int fd, slen;
+ struct sockaddr_un sun;
+
+ if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0)
+ err(1, "socket");
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, name, sizeof(sun.sun_path));
+ slen = SUN_LEN(&sun);
+ unlink(name);
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
+ err(1, "fcntl");
+ if (bind(fd, (struct sockaddr *) & sun, slen) < 0)
+ err(1, "bind");
+ listen(fd, 4);
+ chown(name, 0, 0); /* XXX - root.wheel */
+ chmod(name, 0666);
+ return (fd);
+}
+
+list<int> clients;
+
+void
+notify_clients(const char *data, int len)
+{
+ list<int> bad;
+ list<int>::const_iterator i;
+
+ for (i = clients.begin(); i != clients.end(); i++) {
+ if (write(*i, data, len) <= 0) {
+ bad.push_back(*i);
+ close(*i);
+ }
+ }
+
+ for (i = bad.begin(); i != bad.end(); i++)
+ clients.erase(find(clients.begin(), clients.end(), *i));
+}
+
+void
+new_client(int fd)
+{
+ int s;
+
+ s = accept(fd, NULL, NULL);
+ if (s != -1)
+ clients.push_back(s);
+}
+
+static void
+event_loop(void)
+{
+ int rv;
+ int fd;
+ char buffer[DEVCTL_MAXBUF];
+ int once = 0;
+ int server_fd, max_fd;
+ timeval tv;
+ fd_set fds;
+
+ fd = open(PATH_DEVCTL, O_RDONLY);
+ if (fd == -1)
+ err(1, "Can't open devctl device %s", PATH_DEVCTL);
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0)
+ err(1, "Can't set close-on-exec flag on devctl");
+ server_fd = create_socket(PIPE);
+ max_fd = max(fd, server_fd) + 1;
+ while (1) {
+ if (romeo_must_die)
+ break;
+ if (!once && !dflag && !nflag) {
+ // Check to see if we have any events pending.
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ rv = select(fd + 1, &fds, &fds, &fds, &tv);
+ // No events -> we've processed all pending events
+ if (rv == 0) {
+ if (Dflag)
+ fprintf(stderr, "Calling daemon\n");
+ cfg.remove_pidfile();
+ cfg.open_pidfile();
+ daemon(0, 0);
+ cfg.write_pidfile();
+ once++;
+ }
+ }
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ FD_SET(server_fd, &fds);
+ rv = select(max_fd, &fds, NULL, NULL, NULL);
+ if (rv == -1) {
+ if (errno == EINTR)
+ continue;
+ err(1, "select");
+ }
+ if (FD_ISSET(fd, &fds)) {
+ rv = read(fd, buffer, sizeof(buffer) - 1);
+ if (rv > 0) {
+ notify_clients(buffer, rv);
+ buffer[rv] = '\0';
+ while (buffer[--rv] == '\n')
+ buffer[rv] = '\0';
+ process_event(buffer);
+ } else if (rv < 0) {
+ if (errno != EINTR)
+ break;
+ } else {
+ /* EOF */
+ break;
+ }
+ }
+ if (FD_ISSET(server_fd, &fds))
+ new_client(server_fd);
+ }
+ close(fd);
+}
+
+/*
+ * functions that the parser uses.
+ */
+void
+add_attach(int prio, event_proc *p)
+{
+ cfg.add_attach(prio, p);
+}
+
+void
+add_detach(int prio, event_proc *p)
+{
+ cfg.add_detach(prio, p);
+}
+
+void
+add_directory(const char *dir)
+{
+ cfg.add_directory(dir);
+ free(const_cast<char *>(dir));
+}
+
+void
+add_nomatch(int prio, event_proc *p)
+{
+ cfg.add_nomatch(prio, p);
+}
+
+void
+add_notify(int prio, event_proc *p)
+{
+ cfg.add_notify(prio, p);
+}
+
+event_proc *
+add_to_event_proc(event_proc *ep, eps *eps)
+{
+ if (ep == NULL)
+ ep = new event_proc();
+ ep->add(eps);
+ return (ep);
+}
+
+eps *
+new_action(const char *cmd)
+{
+ eps *e = new action(cmd);
+ free(const_cast<char *>(cmd));
+ return (e);
+}
+
+eps *
+new_match(const char *var, const char *re)
+{
+ eps *e = new match(cfg, var, re);
+ free(const_cast<char *>(var));
+ free(const_cast<char *>(re));
+ return (e);
+}
+
+eps *
+new_media(const char *var, const char *re)
+{
+ eps *e = new media(cfg, var, re);
+ free(const_cast<char *>(var));
+ free(const_cast<char *>(re));
+ return (e);
+}
+
+void
+set_pidfile(const char *name)
+{
+ cfg.set_pidfile(name);
+ free(const_cast<char *>(name));
+}
+
+void
+set_variable(const char *var, const char *val)
+{
+ cfg.set_variable(var, val);
+ free(const_cast<char *>(var));
+ free(const_cast<char *>(val));
+}
+
+
+
+static void
+gensighand(int)
+{
+ romeo_must_die++;
+ _exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: %s [-Ddn]\n", getprogname());
+ exit(1);
+}
+
+static void
+check_devd_enabled()
+{
+ int val = 0;
+ size_t len;
+
+ len = sizeof(val);
+ if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0)
+ errx(1, "devctl sysctl missing from kernel!");
+ if (val) {
+ warnx("Setting " SYSCTL " to 0");
+ val = 0;
+ sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val));
+ }
+}
+
+/*
+ * main
+ */
+int
+main(int argc, char **argv)
+{
+ int ch;
+
+ check_devd_enabled();
+ while ((ch = getopt(argc, argv, "Ddf:n")) != -1) {
+ switch (ch) {
+ case 'D':
+ Dflag++;
+ break;
+ case 'd':
+ dflag++;
+ break;
+ case 'f':
+ configfile = optarg;
+ break;
+ case 'n':
+ nflag++;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ cfg.parse();
+ if (!dflag && nflag) {
+ cfg.open_pidfile();
+ daemon(0, 0);
+ cfg.write_pidfile();
+ }
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, gensighand);
+ signal(SIGINT, gensighand);
+ signal(SIGTERM, gensighand);
+ event_loop();
+ return (0);
+}
diff --git a/sbin/devd/devd.conf.5 b/sbin/devd/devd.conf.5
new file mode 100644
index 0000000..1452eca
--- /dev/null
+++ b/sbin/devd/devd.conf.5
@@ -0,0 +1,129 @@
+.\"
+.\" Copyright (c) 2002 M. Warner Losh
+.\" 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. 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 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.
+.\"
+.\" $FreeBSD$
+.\"
+.\" The section on comments was taken from named.conf.5, which has the
+.\" following copyright:
+.\" Copyright (c) 1999-2000 by Internet Software Consortium
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+.\" ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+.\" CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, 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.
+.\"
+.Dd October 17, 2002
+.Dt DEVD.CONF 5
+.Os
+.Sh NAME
+.Nm devd.conf
+.Nd configuration file for
+.Xr devd 8
+.Sh DESCRIPTION
+.Ss General Syntax
+A
+.Xr devd 8
+configuration consists of two general features, statements
+and comments.
+All statements end with a semicolon.
+Many statements can contain substatements, which are also
+terminated with a semicolon.
+.Pp
+The following statements are supported:
+.Bl -tag -width ".Ic options"
+.It Ic options
+specifies various options and parameters for the operation of
+.Xr devd 8 .
+.It Ic attach
+specifies various matching criteria and actions to perform when
+a newly attached device matches said criteria.
+.It Ic detach
+specifies various matching criteria and actions to perform when
+a newly detached device matches said criteria.
+.It Ic nomatch
+specifies various matching criteria and actions to perform when
+no device driver currently loaded in the kernel claims a (new)
+device.
+.El
+.Pp
+Statements may occur in any order in the configuration file, and may be
+repeated as often as required.
+Further details on the syntax and meaning of each statement and their
+substatements are explained below.
+.Pp
+Comments may appear anywhere that whitespace may appear in a
+configuration file.
+To appeal to programmers of all kinds, they can
+be written in C, C++, or shell/Perl constructs.
+.Pp
+C-style comments start with the two characters
+.Ql /*
+(slash, star) and end with
+.Ql */
+(star, slash).
+Because they are completely delimited with these characters,
+they can be used to comment only a portion of a line or to span
+multiple lines.
+.Pp
+C-style comments cannot be nested.
+For example, the following is
+not valid because the entire comment ends with the first
+.Ql */ :
+.Bd -literal -offset indent
+/* This is the start of a comment.
+ This is still part of the comment.
+/* This is an incorrect attempt at nesting a comment. */
+ This is no longer in any comment. */
+.Ed
+.Pp
+C++-style comments start with the two characters
+.Ql //
+(slash, slash) and continue to the end of the physical line.
+They cannot be continued across multiple physical lines; to have
+one logical comment span multiple lines, each line must use the
+.Ql //
+pair.
+For example:
+.Bd -literal -offset indent
+// This is the start of a comment. The next line
+// is a new comment, even though it is logically
+// part of the previous comment.
+.Ed
+.Sh FILES
+.Bl -tag -width ".Pa /etc/devd.conf" -compact
+.It Pa /etc/devd.conf
+The
+.Xr devd 8
+configuration file.
+.El
+.Sh SEE ALSO
+.Xr devd 8
diff --git a/sbin/devd/devd.h b/sbin/devd/devd.h
new file mode 100644
index 0000000..a8d113b
--- /dev/null
+++ b/sbin/devd/devd.h
@@ -0,0 +1,58 @@
+/*-
+ * DEVD (Device action daemon)
+ *
+ * Copyright (c) 2002 M. Warner Losh <imp@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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef DEVD_H
+#define DEVD_H
+
+/** @warning This file needs to be purely 'C' compatible.
+ */
+struct event_proc;
+struct eps;
+__BEGIN_DECLS
+void add_attach(int, struct event_proc *);
+void add_detach(int, struct event_proc *);
+void add_directory(const char *);
+void add_nomatch(int, struct event_proc *);
+void add_notify(int, struct event_proc *);
+struct event_proc *add_to_event_proc(struct event_proc *, struct eps *);
+struct eps *new_match(const char *, const char *);
+struct eps *new_media(const char *, const char *);
+struct eps *new_action(const char *);
+void set_pidfile(const char *);
+void set_variable(const char *, const char *);
+void yyerror(const char *s);
+int yylex(void);
+int yyparse(void);
+__END_DECLS
+
+#define PATH_DEVCTL "/dev/devctl"
+#define DEVCTL_MAXBUF 1025
+
+#endif /* DEVD_H */
diff --git a/sbin/devd/devd.hh b/sbin/devd/devd.hh
new file mode 100644
index 0000000..a1ee9cd
--- /dev/null
+++ b/sbin/devd/devd.hh
@@ -0,0 +1,183 @@
+/*-
+ * Copyright (c) 2002-2003 M. Warner Losh.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef DEVD_HH
+#define DEVD_HH
+
+class config;
+
+/**
+ * var_list is a collection of variables. These collections of variables
+ * are stacked up and popped down for each event that we have to process.
+ * We have multiple levels so that we can push variables that are unique
+ * to the event in question, in addition to having global variables. This
+ * allows for future flexibility.
+ */
+class var_list
+{
+public:
+ var_list() {}
+ virtual ~var_list() {}
+ /** Set a variable in this var list.
+ */
+ void set_variable(const std::string &var, const std::string &val);
+ /** Get the variable out of this, and no other, var_list. If
+ * no variable of %var is set, then %bogus will be returned.
+ */
+ const std::string &get_variable(const std::string &var) const;
+ /** Is there a variable of %var set in thi stable?
+ */
+ bool is_set(const std::string &var) const;
+ /** A completely bogus string.
+ */
+ static const std::string bogus;
+ static const std::string nothing;
+private:
+ std::map<std::string, std::string> _vars;
+};
+
+/**
+ * eps is short for event_proc_single. It is a single entry in an
+ * event_proc. Each keyword needs its own subclass from eps.
+ */
+class eps
+{
+public:
+ eps() {}
+ virtual ~eps() {}
+ /** Does this eps match the current config?
+ */
+ virtual bool do_match(config &) = 0;
+ /** Perform some action for this eps.
+ */
+ virtual bool do_action(config &) = 0;
+};
+
+/**
+ * match is the subclass used to match an individual variable. Its
+ * actions are nops.
+ */
+class match : public eps
+{
+public:
+ match(config &, const char *var, const char *re);
+ virtual ~match();
+ virtual bool do_match(config &);
+ virtual bool do_action(config &) { return true; }
+private:
+ std::string _var;
+ std::string _re;
+ regex_t _regex;
+};
+
+/**
+ * media is the subclass used to match an individual variable. Its
+ * actions are nops.
+ */
+class media : public eps
+{
+public:
+ media(config &, const char *var, const char *type);
+ virtual ~media();
+ virtual bool do_match(config &);
+ virtual bool do_action(config &) { return true; }
+private:
+ std::string _var;
+ int _type;
+};
+
+/**
+ * action is used to fork a process. It matches everything.
+ */
+class action : public eps
+{
+public:
+ action(const char *cmd);
+ virtual ~action();
+ virtual bool do_match(config &) { return true; }
+ virtual bool do_action(config &);
+private:
+ std::string _cmd;
+};
+
+class event_proc
+{
+public:
+ event_proc();
+ virtual ~event_proc();
+ int get_priority() const { return (_prio); }
+ void set_priority(int prio) { _prio = prio; }
+ void add(eps *);
+ bool matches(config &);
+ bool run(config &);
+private:
+ int _prio;
+ std::vector<eps *> _epsvec;
+};
+
+class config
+{
+public:
+ config() : _pidfile("") { push_var_table(); }
+ virtual ~config() { reset(); }
+ void add_attach(int, event_proc *);
+ void add_detach(int, event_proc *);
+ void add_directory(const char *);
+ void add_nomatch(int, event_proc *);
+ void add_notify(int, event_proc *);
+ void set_pidfile(const char *);
+ void reset();
+ void parse();
+ void open_pidfile();
+ void write_pidfile();
+ void remove_pidfile();
+ void push_var_table();
+ void pop_var_table();
+ void set_variable(const char *var, const char *val);
+ const std::string &get_variable(const std::string &var);
+ const std::string expand_string(const std::string &var);
+ char *set_vars(char *);
+ void find_and_execute(char);
+protected:
+ void sort_vector(std::vector<event_proc *> &);
+ void parse_one_file(const char *fn);
+ void parse_files_in_dir(const char *dirname);
+ void expand_one(const char *&src, std::string &dst);
+ bool is_id_char(char);
+ bool chop_var(char *&buffer, char *&lhs, char *&rhs);
+private:
+ std::vector<std::string> _dir_list;
+ std::string _pidfile;
+ std::vector<var_list *> _var_list_table;
+ std::vector<event_proc *> _attach_list;
+ std::vector<event_proc *> _detach_list;
+ std::vector<event_proc *> _nomatch_list;
+ std::vector<event_proc *> _notify_list;
+};
+
+#endif /* DEVD_HH */
diff --git a/sbin/devd/parse.y b/sbin/devd/parse.y
new file mode 100644
index 0000000..28b8a90
--- /dev/null
+++ b/sbin/devd/parse.y
@@ -0,0 +1,152 @@
+%{
+/*-
+ * DEVD (Device action daemon)
+ *
+ * Copyright (c) 2002 M. Warner Losh <imp@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.
+ *
+ * $FreeBSD$
+ */
+
+#include "devd.h"
+#include <stdio.h>
+#include <string.h>
+
+%}
+
+%union {
+ char *str;
+ int i;
+ struct eps *eps; /* EventProcStatement */
+ struct event_proc *eventproc;
+}
+
+%token SEMICOLON BEGINBLOCK ENDBLOCK COMMA
+%token <i> NUMBER
+%token <str> STRING
+%token <str> ID
+%token OPTIONS SET DIRECTORY PID_FILE DEVICE_NAME ACTION MATCH
+%token ATTACH DETACH NOMATCH NOTIFY MEDIA_TYPE CLASS SUBDEVICE
+
+%type <eventproc> match_or_action_list
+%type <eps> match_or_action match action
+
+%%
+
+config_file
+ : config_list
+ |
+ ;
+
+config_list
+ : config
+ | config_list config
+ ;
+
+config
+ : option_block
+ | attach_block
+ | detach_block
+ | nomatch_block
+ | notify_block
+ ;
+
+option_block
+ : OPTIONS BEGINBLOCK options ENDBLOCK SEMICOLON
+ ;
+
+options
+ : option
+ | options option
+
+option
+ : directory_option
+ | pid_file_option
+ | set_option
+ ;
+
+directory_option
+ : DIRECTORY STRING SEMICOLON { add_directory($2); }
+ ;
+
+pid_file_option
+ : PID_FILE STRING SEMICOLON { set_pidfile($2); }
+ ;
+
+set_option
+ : SET ID STRING SEMICOLON { set_variable($2, $3); }
+ ;
+
+attach_block
+ : ATTACH NUMBER BEGINBLOCK match_or_action_list ENDBLOCK SEMICOLON
+ { add_attach($2, $4); }
+ | ATTACH NUMBER BEGINBLOCK ENDBLOCK SEMICOLON
+ ;
+
+detach_block
+ : DETACH NUMBER BEGINBLOCK match_or_action_list ENDBLOCK SEMICOLON
+ { add_detach($2, $4); }
+ | DETACH NUMBER BEGINBLOCK ENDBLOCK SEMICOLON
+ ;
+
+nomatch_block
+ : NOMATCH NUMBER BEGINBLOCK match_or_action_list ENDBLOCK SEMICOLON
+ { add_nomatch($2, $4); }
+ | NOMATCH NUMBER BEGINBLOCK ENDBLOCK SEMICOLON
+ ;
+
+notify_block
+ : NOTIFY NUMBER BEGINBLOCK match_or_action_list ENDBLOCK SEMICOLON
+ { add_notify($2, $4); }
+ | NOTIFY NUMBER BEGINBLOCK ENDBLOCK SEMICOLON
+ ;
+
+match_or_action_list
+ : match_or_action { $$ = add_to_event_proc( NULL, $1); }
+ | match_or_action_list match_or_action
+ { $$ = add_to_event_proc($1, $2); }
+ ;
+
+match_or_action
+ : match
+ | action
+ ;
+
+match
+ : MATCH STRING STRING SEMICOLON { $$ = new_match($2, $3); }
+ | DEVICE_NAME STRING SEMICOLON
+ { $$ = new_match(strdup("device-name"), $2); }
+ | MEDIA_TYPE STRING SEMICOLON
+ { $$ = new_media(strdup("media-type"), $2); }
+ | CLASS STRING SEMICOLON
+ { $$ = new_match(strdup("class"), $2); }
+ | SUBDEVICE STRING SEMICOLON
+ { $$ = new_match(strdup("subdevice"), $2); }
+ ;
+
+action
+ : ACTION STRING SEMICOLON { $$ = new_action($2); }
+ ;
+
+%%
diff --git a/sbin/devd/token.l b/sbin/devd/token.l
new file mode 100644
index 0000000..0fa78da
--- /dev/null
+++ b/sbin/devd/token.l
@@ -0,0 +1,110 @@
+%{
+/*-
+ * DEVD (Device action daemon)
+ *
+ * Copyright (c) 2002 M. Warner Losh <imp@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.
+ *
+ * $FreeBSD$
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include "devd.h"
+#include "y.tab.h"
+
+int lineno = 1;
+#define YY_NO_UNPUT
+
+static void
+update_lineno(const char *cp)
+{
+ while (*cp)
+ if (*cp++ == '\n')
+ lineno++;
+}
+
+%}
+
+%%
+
+[ \t]+ ;
+\n lineno++;
+; { return SEMICOLON; }
+#.*$ ;
+\/\/.*$ ;
+\/\*([^*]|(\*+([^*\/])))*\*+\/ { update_lineno(yytext); }
+\{ { return BEGINBLOCK; }
+\} { return ENDBLOCK; }
+[0-9]+ { yylval.i = atoi(yytext); return NUMBER; }
+\"[^"]+\" {
+ update_lineno(yytext);
+ int len = strlen(yytext) - 2;
+ char *walker;
+ int i;
+ if ((yylval.str = (char *) malloc(len + 1)) == NULL)
+ goto out;
+ walker = yylval.str;
+ for (i = 1; i <= len; i++) {
+ if (yytext[i] == '\\' &&
+ yytext[i + 1] == '\n') {
+ i += 2;
+ while(isspace(yytext[i]))
+ i++;
+ }
+ *walker++ = yytext[i];
+ }
+ *walker++ = '\0';
+ out:;
+ return STRING;
+ }
+
+
+options { return OPTIONS; }
+set { return SET; }
+directory { return DIRECTORY; }
+pid-file { return PID_FILE; }
+attach { return ATTACH; }
+detach { return DETACH; }
+device-name { return DEVICE_NAME; }
+media-type { return MEDIA_TYPE; }
+class { return CLASS; }
+subdevice { return SUBDEVICE; }
+action { return ACTION; }
+match { return MATCH; }
+nomatch { return NOMATCH; }
+notify { return NOTIFY; }
+[A-Za-z][A-Za-z0-9_-]* {
+ yylval.str = strdup(yytext);
+ return ID;
+ }
+%%
+
+void
+yyerror(const char *s)
+{
+ syslog(LOG_ERR, "line %d: %s%s %s.\n", lineno, yytext, yytext?":":"", s);
+}
diff --git a/sbin/devfs/Makefile b/sbin/devfs/Makefile
new file mode 100644
index 0000000..95d2540
--- /dev/null
+++ b/sbin/devfs/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= devfs
+SRCS= devfs.c rule.c
+MAN= devfs.8
+WARNS?= 5
+
+.include <bsd.prog.mk>
diff --git a/sbin/devfs/devfs.8 b/sbin/devfs/devfs.8
new file mode 100644
index 0000000..ec517b0
--- /dev/null
+++ b/sbin/devfs/devfs.8
@@ -0,0 +1,355 @@
+.\"
+.\" Copyright (c) 2002 Dima Dorfman.
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 1, 2002
+.Dt DEVFS 8
+.Os
+.Sh NAME
+.Nm devfs
+.Nd "DEVFS control"
+.Sh SYNOPSIS
+.Nm
+.Op Fl m Ar mount-point
+.Ar keyword
+.Ar argument ...
+.Sh DESCRIPTION
+The
+.Nm
+utility provides an interface to manipulate properties of
+.Xr devfs 5
+mounts.
+.Pp
+The
+.Ar keyword
+argument determines the context for
+the rest of the arguments.
+For example,
+most of the commands related to the rule subsystem must be preceded by the
+.Cm rule
+keyword.
+The following flags are common to all keywords:
+.Bl -tag -offset indent
+.It Fl m Ar mount-point
+Operate on
+.Ar mount-point ,
+which is expected to be a
+.Xr devfs 5
+mount.
+If this option is not specified,
+.Nm
+operates on
+.Pa /dev .
+.El
+.Ss Rule Subsystem
+The
+.Xr devfs 5
+rule subsystem provides a way for the administrator of a system to control
+the attributes of DEVFS nodes.
+.\" XXX devfs node? entry? what?
+Each DEVFS mount-point has a
+.Dq ruleset ,
+or a list of rules,
+associated with it.
+When a device driver creates a new node,
+all the rules in the ruleset associated with each mount-point are applied
+(see below) before the node becomes visible to the userland.
+This permits the administrator to change the properties,
+including the visibility,
+of certain nodes.
+For example, one might want to hide all disk nodes in a
+.Xr jail 2 Ns 's
+.Pa /dev .
+.Ss Rule Manipulation
+Rule manipulation commands follow the
+.Cm rule
+keyword.
+The following flags are common to all of the rule manipulation commands:
+.Bl -tag -offset indent
+.It Fl s Ar ruleset
+Operate on the ruleset with the number
+.Ar ruleset .
+If this is not specified,
+the commands operate on the ruleset currently associated with the
+specified mount-point.
+.El
+.Pp
+The following commands are recognized:
+.Bl -tag -offset indent
+.It Cm rule add Oo Ar rulenum Oc Ar rulespec
+Add the rule described by
+.Ar rulespec
+(defined below)
+to the ruleset.
+The rule has the number
+.Ar rulenum
+if it is explicitly specified;
+otherwise, the rule number is automatically determined by the kernel.
+.It Cm rule apply Ar rulenum | rulespec
+Apply rule number
+.Ar rulenum
+or the rule described by
+.Ar rulespec
+to the mount-point.
+Rules that are
+.Dq applied
+have their conditions checked against all nodes
+in the mount-point and the actions taken if they match.
+.It Cm rule applyset
+Apply all the rules in the ruleset to the mount-point
+(see above for the definition of
+.Dq apply ) .
+.It Cm rule del Ar rulenum
+Delete rule number
+.Ar rulenum
+from the ruleset.
+.It Cm rule delset
+Delete all rules from the ruleset.
+.It Cm rule show Op Ar rulenum
+Display the rule number
+.Ar rulenum ,
+or all the rules in the ruleset.
+The output lines (one line per rule) are expected to be valid
+.Ar rulespec Ns s .
+.It Cm rule showsets
+Report the numbers of existing rulesets.
+.It Cm ruleset Ar ruleset
+Set ruleset number
+.Ar ruleset
+as the current ruleset for the mount-point.
+.El
+.Ss Rule Specification
+Rules have two parts: the conditions and the actions.
+The conditions determine which DEVFS nodes the rule matches
+and the actions determine what should be done when a rule matches a node.
+For example, a rule can be written that sets the GID to
+.Dq Li operator
+for all devices of type tape.
+If the first token of a rule specification is a single dash
+.Pq Sq Fl ,
+rules are read from the standard input and the rest of the specification
+is ignored.
+.Pp
+The following conditions are recognized.
+Conditions are ANDed together when matching a device;
+if OR is desired, multiple rules can be written.
+.Bl -tag -offset indent
+.It Cm path Ar pattern
+Matches any node with a path that matches
+.Ar pattern ,
+which is interpreted as a
+.Xr glob 3 Ns -style
+pattern.
+.It Cm type Ar devtype
+Matches any node that is of type
+.Ar devtype .
+Valid types are
+.Cm disk , mem , tape
+and
+.Cm tty .
+.El
+.Pp
+The following actions are recognized.
+Although there is no explicit delimiter between conditions and actions,
+they may not be intermixed.
+.Bl -tag -offset indent
+.It Cm group Ar gid
+Set the GID of the node to
+.Ar gid ,
+which may be a group name
+(looked up in
+.Pa /etc/group )
+or number.
+.It Cm hide
+Hide the node.
+Nodes may later be revived manually with
+.Xr mknod 8
+or with the
+.Cm unhide
+action.
+.It Cm include Ar ruleset
+Apply all the rules in ruleset number
+.Ar ruleset
+to the node.
+This does not necessarily result in any changes to the node
+(e.g., if none of the rules in the included ruleset match).
+.It Cm mode Ar filemode
+Set the file mode to
+.Ar filemode ,
+which is interpreted as in
+.Xr chmod 1 .
+.It Cm user Ar uid
+Set the UID to
+.Ar uid ,
+which may be a user name
+(looked up in
+.Pa /etc/passwd )
+or number.
+.It Cm unhide
+Unhide the node.
+.El
+.Sh IMPLEMENTATION NOTES
+Rulesets are created by the kernel at the first reference
+and destroyed when the last reference disappears.
+E.g., a ruleset is created when a rule is added to it or when it is set
+as the current ruleset for a mount-point, and
+a ruleset is destroyed when the last rule in it is deleted
+and no other references to it exist
+(i.e., it is not included by any rules and it is not the current ruleset
+for any mount-point).
+.Pp
+Ruleset number 0 is the default ruleset for all new mount-points.
+It is always empty, cannot be modified or deleted, and does not show up
+in the output of
+.Cm showsets .
+.Pp
+Rules and rulesets are unique to the entire system,
+not a particular mount-point.
+I.e., a
+.Cm showsets
+will return the same information regardless of the mount-point specified with
+.Fl m .
+The mount-point is only relevant when changing what its current ruleset is
+or when using one of the apply commands.
+.Sh FILES
+.Bl -tag -compact
+.It Pa /etc/defaults/devfs.rules
+.It Pa /etc/devfs.rules
+.El
+.Sh EXAMPLES
+When the system boots,
+the only ruleset that exists is ruleset number 0;
+since the latter may not be modified, we have to create another ruleset
+before adding rules.
+Note that since most of the following examples do not specify
+.Fl m ,
+the operations are performed on
+.Pa /dev
+(this only matters for things that might change the properties of nodes).
+.Pp
+.Dl "devfs ruleset 10"
+.Pp
+Specify that ruleset 10 should be the current ruleset for
+.Pa /dev
+(if it does not already exist, it is created).
+.Pp
+.Dl "devfs rule add path speaker mode 666"
+.Pp
+Add a rule that causes all nodes that have a path that matches
+.Dq Li speaker
+(this is only
+.Pa /dev/speaker )
+to have the file mode 666 (read and write for all).
+Note that if any such nodes already exist, their mode will not be changed
+unless this rule (or ruleset) is explicitly applied (see below).
+The mode
+.Em will
+be changed if the node is created
+.Em after
+the rule is added
+(e.g., the
+.Pa atspeaker
+module is loaded after the above rule is added).
+.Pp
+.Dl "devfs rule applyset"
+.Pp
+Apply all the rules in the current ruleset to all the existing nodes.
+E.g., if the above rule was added after
+.Pa /dev/speaker
+was created,
+this command will cause its file mode to be changed to 666
+as prescribed by the rule.
+.Pp
+.Dl devfs rule add path "snp*" mode 660 group snoopers
+.Pp
+(Quoting the argument to
+.Cm path
+is often necessary to disable the shell's globbing features.)
+For all devices with a path that matches
+.Dq Li snp* ,
+set the file mode to 660 and the GID to
+.Dq Li snoopers .
+This permits users in the
+.Dq Li snoopers
+group to use the
+.Xr snp 4
+devices.
+.Pp
+.Dl "devfs rule -s 20 add major 53 group games"
+.Pp
+Add a rule to ruleset number 20.
+Since this ruleset is not the current ruleset for any mount-points,
+this rule is never applied automatically (unless ruleset 20 becomes
+a current ruleset for some mount-point at a later time).
+However, it can be applied explicitly, as such:
+.Pp
+.Dl "devfs -m /my/jail/dev rule -s 20 applyset"
+.Pp
+This will apply all rules in ruleset number 20 to the DEVFS mount on
+.Pa /my/jail/dev .
+It does not matter that ruleset 20 is not the current ruleset for that
+mount-point; the rules are still applied.
+.Pp
+.Dl "devfs rule apply hide"
+.Pp
+Since this rule has no conditions, the action
+.Pq Cm hide
+will be applied to all nodes.
+Since hiding all nodes is not very useful, we can undo it:
+.Pp
+.Dl "devfs rule apply unhide"
+.Pp
+which applies
+.Cm unhide
+to all the nodes,
+causing them to reappear.
+.Pp
+.Dl "devfs rule -s 10 add - < my_rules"
+.Pp
+Add all the rules from the file
+.Pa my_rules
+to ruleset 10.
+.Pp
+.Dl "devfs rule -s 20 show | devfs rule -s 10 add -"
+.Pp
+Since
+.Cm show
+outputs valid rules,
+this feature can be used to copy rulesets.
+The above copies all the rules from ruleset 20 into ruleset 10.
+The rule numbers are preserved,
+but ruleset 10 may already have rules with non-conflicting numbers
+(these will be preserved).
+.Sh SEE ALSO
+.Xr chmod 1 ,
+.Xr jail 2 ,
+.Xr glob 3 ,
+.Xr devfs 5 ,
+.Xr chown 8 ,
+.Xr jail 8 ,
+.Xr mknod 8
+.Sh AUTHORS
+.An Dima Dorfman
diff --git a/sbin/devfs/devfs.c b/sbin/devfs/devfs.c
new file mode 100644
index 0000000..f531f75
--- /dev/null
+++ b/sbin/devfs/devfs.c
@@ -0,0 +1,230 @@
+/*-
+ * Copyright (c) 2001, 2002 Dima Dorfman.
+ * 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.
+ */
+
+/*
+ * DEVFS control.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+int mpfd;
+
+static ctbl_t ctbl_main = {
+ { "rule", rule_main },
+ { "ruleset", ruleset_main },
+ { NULL, NULL }
+};
+
+int
+main(int ac, char **av)
+{
+ const char *mountpt;
+ struct cmd *c;
+ int ch;
+
+ mountpt = NULL;
+ while ((ch = getopt(ac, av, "m:")) != -1)
+ switch (ch) {
+ case 'm':
+ mountpt = optarg;
+ break;
+ default:
+ usage();
+ }
+ ac -= optind;
+ av += optind;
+ if (ac < 1)
+ usage();
+
+ if (mountpt == NULL)
+ mountpt = _PATH_DEV;
+ mpfd = open(mountpt, O_RDONLY);
+ if (mpfd == -1)
+ err(1, "open: %s", mountpt);
+
+ for (c = ctbl_main; c->name != NULL; ++c)
+ if (strcmp(c->name, av[0]) == 0)
+ exit((*c->handler)(ac, av));
+ errx(1, "unknown command: %s", av[0]);
+}
+
+/*
+ * Convert an integer to a "number" (ruleset numbers and rule numbers
+ * are 16-bit). If the conversion is successful, num contains the
+ * integer representation of s and 1 is returned; otherwise, 0 is
+ * returned and num is unchanged.
+ */
+int
+atonum(const char *s, uint16_t *num)
+{
+ unsigned long ul;
+ char *cp;
+
+ ul = strtoul(s, &cp, 10);
+ if (ul > UINT16_MAX || *cp != '\0')
+ return (0);
+ *num = (uint16_t)ul;
+ return (1);
+}
+
+/*
+ * Convert user input in ASCII to an integer.
+ */
+int
+eatoi(const char *s)
+{
+ char *cp;
+ long l;
+
+ l = strtol(s, &cp, 10);
+ if (l > INT_MAX || *cp != '\0')
+ errx(1, "error converting to integer: %s", s);
+ return ((int)l);
+}
+
+/*
+ * As atonum(), but the result of failure is death.
+ */
+uint16_t
+eatonum(const char *s)
+{
+ uint16_t num;
+
+ if (!atonum(s, &num))
+ errx(1, "error converting to number: %s", s); /* XXX clarify */
+ return (num);
+}
+
+/*
+ * Read a line from a /FILE/. If the return value isn't 0, it is the
+ * length of the line, a pointer to which exists in /line/. It is the
+ * caller's responsibility to free(3) it. If the return value is 0,
+ * there was an error or we reached EOF, and /line/ is undefined (so,
+ * obviously, the caller shouldn't try to free(3) it).
+ */
+size_t
+efgetln(FILE *fp, char **line)
+{
+ size_t rv;
+ char *cp;
+
+ cp = fgetln(fp, &rv);
+ if (cp == NULL) {
+ *line = NULL;
+ return (rv);
+ }
+ if (cp[rv - 1] == '\n') {
+ cp[rv - 1] = '\0';
+ *line = strdup(cp);
+ if (*line == NULL)
+ errx(1, "cannot allocate memory");
+ --rv;
+ } else {
+ *line = malloc(rv + 1);
+ if (*line == NULL)
+ errx(1, "cannot allocate memory");
+ memcpy(*line, cp, rv);
+ *line[rv] = '\0';
+ }
+ assert(rv == strlen(*line));
+ return (rv);
+}
+
+struct ptrstq {
+ STAILQ_ENTRY(ptrstq) tq;
+ void *ptr;
+};
+
+/*
+ * Create an argument vector from /line/. The caller must free(3)
+ * /avp/, and /avp[0]/ when the argument vector is no longer
+ * needed unless /acp/ is 0, in which case /avp/ is undefined.
+ * /avp/ is NULL-terminated, so it is actually one longer than /acp/.
+ */
+void
+tokenize(const char *line, int *acp, char ***avp)
+{
+ static const char *delims = " \t\n";
+ struct ptrstq *pt;
+ STAILQ_HEAD(, ptrstq) plist;
+ char **ap, *cp, *wline, *xcp;
+
+ line += strspn(line, delims);
+ wline = strdup(line);
+ if (wline == NULL)
+ errx(1, "cannot allocate memory");
+
+ STAILQ_INIT(&plist);
+ for (xcp = wline, *acp = 0;
+ (cp = strsep(&xcp, delims)) != NULL;)
+ if (*cp != '\0') {
+ pt = calloc(1, sizeof(*pt));
+ if (pt == NULL)
+ errx(1, "cannot allocate memory");
+ pt->ptr = cp;
+ STAILQ_INSERT_TAIL(&plist, pt, tq);
+ ++*acp;
+ }
+ if (*acp == 0)
+ return;
+ assert(STAILQ_FIRST(&plist)->ptr == wline);
+ *avp = malloc(sizeof(**avp) * (*acp + 1));
+ if (*avp == NULL)
+ errx(1, "cannot allocate memory");
+ for (ap = *avp; !STAILQ_EMPTY(&plist);) {
+ pt = STAILQ_FIRST(&plist);
+ *ap = pt->ptr;
+ ++ap;
+ assert(ap <= *avp + (*acp));
+ STAILQ_REMOVE_HEAD(&plist, tq);
+ free(pt);
+ }
+ *ap = NULL;
+}
+
+void
+usage(void)
+{
+
+ fprintf(stderr, "usage: devfs rule|ruleset arguments\n");
+ exit(1);
+}
diff --git a/sbin/devfs/extern.h b/sbin/devfs/extern.h
new file mode 100644
index 0000000..a9f8d22
--- /dev/null
+++ b/sbin/devfs/extern.h
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 2002 Dima Dorfman.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __DEVFS_H__
+#define __DEVFS_H__
+
+#include <fs/devfs/devfs.h>
+
+struct intstr {
+ const char *s;
+ int i;
+};
+
+typedef int (command_t)(int, char **);
+struct cmd {
+ const char *name;
+ command_t *handler;
+};
+typedef struct cmd ctbl_t[];
+
+command_t rule_main, ruleset_main;
+
+int atonum(const char *, uint16_t *);
+int eatoi(const char *);
+uint16_t eatonum(const char *);
+size_t efgetln(FILE *, char **);
+void tokenize(const char *, int *, char ***);
+void usage(void) __dead2;
+
+extern int mpfd; /* Mount-point file descriptor. */
+
+#endif /* !__DEVFS_H__ */
diff --git a/sbin/devfs/rule.c b/sbin/devfs/rule.c
new file mode 100644
index 0000000..25a7d28
--- /dev/null
+++ b/sbin/devfs/rule.c
@@ -0,0 +1,462 @@
+/*-
+ * Copyright (c) 2002 Dima Dorfman.
+ * 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.
+ */
+
+/*
+ * Rule subsystem manipulation.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/ioctl.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static void rulespec_infp(FILE *fp, unsigned long reqest, devfs_rsnum rsnum);
+static void rulespec_instr(struct devfs_rule *dr, const char *str,
+ devfs_rsnum rsnum);
+static void rulespec_intok(struct devfs_rule *dr, int ac, char **av,
+ devfs_rsnum rsnum);
+static void rulespec_outfp(FILE *fp, struct devfs_rule *dr);
+
+static command_t rule_add, rule_apply, rule_applyset;
+static command_t rule_del, rule_delset, rule_show, rule_showsets;
+
+static ctbl_t ctbl_rule = {
+ { "add", rule_add },
+ { "apply", rule_apply },
+ { "applyset", rule_applyset },
+ { "del", rule_del },
+ { "delset", rule_delset },
+ { "show", rule_show },
+ { "showsets", rule_showsets },
+ { NULL, NULL }
+};
+
+static struct intstr ist_type[] = {
+ { "disk", D_DISK },
+ { "mem", D_MEM },
+ { "tape", D_TAPE },
+ { "tty", D_TTY },
+ { NULL, -1 }
+};
+
+devfs_rsnum in_rsnum;
+
+int
+rule_main(int ac, char **av)
+{
+ struct cmd *c;
+ int ch;
+
+ setprogname("devfs rule");
+ optreset = optind = 1;
+ while ((ch = getopt(ac, av, "s:")) != -1)
+ switch (ch) {
+ case 's':
+ in_rsnum = eatonum(optarg);
+ break;
+ default:
+ usage();
+ }
+ ac -= optind;
+ av += optind;
+ if (ac < 1)
+ usage();
+
+ for (c = ctbl_rule; c->name != NULL; ++c)
+ if (strcmp(c->name, av[0]) == 0)
+ exit((*c->handler)(ac, av));
+ errx(1, "unknown command: %s", av[0]);
+}
+
+static int
+rule_add(int ac, char **av)
+{
+ struct devfs_rule dr;
+ int rv;
+
+ if (ac < 2)
+ usage();
+ if (strcmp(av[1], "-") == 0)
+ rulespec_infp(stdin, DEVFSIO_RADD, in_rsnum);
+ else {
+ rulespec_intok(&dr, ac - 1, av + 1, in_rsnum);
+ rv = ioctl(mpfd, DEVFSIO_RADD, &dr);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RADD");
+ }
+ return (0);
+}
+
+static int
+rule_apply(int ac __unused, char **av __unused)
+{
+ struct devfs_rule dr;
+ devfs_rnum rnum;
+ devfs_rid rid;
+ int rv;
+
+ if (ac < 2)
+ usage();
+ if (!atonum(av[1], &rnum)) {
+ if (strcmp(av[1], "-") == 0)
+ rulespec_infp(stdin, DEVFSIO_RAPPLY, in_rsnum);
+ else {
+ rulespec_intok(&dr, ac - 1, av + 1, in_rsnum);
+ rv = ioctl(mpfd, DEVFSIO_RAPPLY, &dr);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RAPPLY");
+ }
+ } else {
+ rid = mkrid(in_rsnum, rnum);
+ rv = ioctl(mpfd, DEVFSIO_RAPPLYID, &rid);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RAPPLYID");
+ }
+ return (0);
+}
+
+static int
+rule_applyset(int ac, char **av __unused)
+{
+ int rv;
+
+ if (ac != 1)
+ usage();
+ rv = ioctl(mpfd, DEVFSIO_SAPPLY, &in_rsnum);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_SAPPLY");
+ return (0);
+}
+
+static int
+rule_del(int ac __unused, char **av)
+{
+ devfs_rid rid;
+ int rv;
+
+ if (av[1] == NULL)
+ usage();
+ rid = mkrid(in_rsnum, eatoi(av[1]));
+ rv = ioctl(mpfd, DEVFSIO_RDEL, &rid);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RDEL");
+ return (0);
+}
+
+static int
+rule_delset(int ac, char **av __unused)
+{
+ struct devfs_rule dr;
+ int rv;
+
+ if (ac != 1)
+ usage();
+ memset(&dr, '\0', sizeof(dr));
+ dr.dr_magic = DEVFS_MAGIC;
+ dr.dr_id = mkrid(in_rsnum, 0);
+ while (ioctl(mpfd, DEVFSIO_RGETNEXT, &dr) != -1) {
+ rv = ioctl(mpfd, DEVFSIO_RDEL, &dr.dr_id);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RDEL");
+ }
+ if (errno != ENOENT)
+ err(1, "ioctl DEVFSIO_RGETNEXT");
+ return (0);
+}
+
+static int
+rule_show(int ac __unused, char **av)
+{
+ struct devfs_rule dr;
+ devfs_rnum rnum;
+ int rv;
+
+ memset(&dr, '\0', sizeof(dr));
+ dr.dr_magic = DEVFS_MAGIC;
+ if (av[1] != NULL) {
+ rnum = eatoi(av[1]);
+ dr.dr_id = mkrid(in_rsnum, rnum - 1);
+ rv = ioctl(mpfd, DEVFSIO_RGETNEXT, &dr);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RGETNEXT");
+ if (rid2rn(dr.dr_id) == rnum)
+ rulespec_outfp(stdout, &dr);
+ } else {
+ dr.dr_id = mkrid(in_rsnum, 0);
+ while (ioctl(mpfd, DEVFSIO_RGETNEXT, &dr) != -1)
+ rulespec_outfp(stdout, &dr);
+ if (errno != ENOENT)
+ err(1, "ioctl DEVFSIO_RGETNEXT");
+ }
+ return (0);
+}
+
+static int
+rule_showsets(int ac, char **av __unused)
+{
+ devfs_rsnum rsnum;
+
+ if (ac != 1)
+ usage();
+ rsnum = 0;
+ while (ioctl(mpfd, DEVFSIO_SGETNEXT, &rsnum) != -1)
+ printf("%d\n", rsnum);
+ if (errno != ENOENT)
+ err(1, "ioctl DEVFSIO_SGETNEXT");
+ return (0);
+}
+
+int
+ruleset_main(int ac, char **av)
+{
+ devfs_rsnum rsnum;
+ int rv;
+
+ setprogname("devfs ruleset");
+ if (ac < 2)
+ usage();
+ rsnum = eatonum(av[1]);
+ rv = ioctl(mpfd, DEVFSIO_SUSE, &rsnum);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_SUSE");
+ return (0);
+}
+
+
+/*
+ * Input rules from a file (probably the standard input). This
+ * differs from the other rulespec_in*() routines in that it also
+ * calls ioctl() for the rules, since it is impractical (and not very
+ * useful) to return a list (or array) of rules, just so the caller
+ * can call call ioctl() for each of them.
+ */
+static void
+rulespec_infp(FILE *fp, unsigned long request, devfs_rsnum rsnum)
+{
+ struct devfs_rule dr;
+ char *line;
+ int rv;
+
+ assert(fp == stdin); /* XXX: De-hardcode "stdin" from error msg. */
+ while (efgetln(fp, &line)) {
+ rulespec_instr(&dr, line, rsnum);
+ rv = ioctl(mpfd, request, &dr);
+ if (rv == -1)
+ err(1, "ioctl");
+ free(line); /* efgetln() always malloc()s. */
+ }
+ if (ferror(stdin))
+ err(1, "stdin");
+}
+
+/*
+ * Construct a /struct devfs_rule/ from a string.
+ */
+static void
+rulespec_instr(struct devfs_rule *dr, const char *str, devfs_rsnum rsnum)
+{
+ char **av;
+ int ac;
+
+ tokenize(str, &ac, &av);
+ if (ac == 0)
+ errx(1, "unexpected end of rulespec");
+ rulespec_intok(dr, ac, av, rsnum);
+ free(av[0]);
+ free(av);
+}
+
+/*
+ * Construct a /struct devfs_rule/ from ac and av.
+ */
+static void
+rulespec_intok(struct devfs_rule *dr, int ac __unused, char **av,
+ devfs_rsnum rsnum)
+{
+ struct intstr *is;
+ struct passwd *pw;
+ struct group *gr;
+ devfs_rnum rnum;
+ void *set;
+
+ memset(dr, '\0', sizeof(*dr));
+
+ /*
+ * We don't maintain ac hereinafter.
+ */
+ if (av[0] == NULL)
+ errx(1, "unexpected end of rulespec");
+
+ /* If the first argument is an integer, treat it as a rule number. */
+ if (!atonum(av[0], &rnum))
+ rnum = 0; /* auto-number */
+ else
+ ++av;
+
+ /*
+ * These aren't table-driven since that would result in more
+ * tiny functions than I care to deal with.
+ */
+ for (;;) {
+ if (av[0] == NULL)
+ break;
+ else if (strcmp(av[0], "type") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for type");
+ for (is = ist_type; is->s != NULL; ++is)
+ if (strcmp(av[1], is->s) == 0) {
+ dr->dr_dswflags |= is->i;
+ break;
+ }
+ if (is->s == NULL)
+ errx(1, "unknown type: %s", av[1]);
+ dr->dr_icond |= DRC_DSWFLAGS;
+ av += 2;
+ } else if (strcmp(av[0], "path") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for path");
+ if (strlcpy(dr->dr_pathptrn, av[1], DEVFS_MAXPTRNLEN)
+ >= DEVFS_MAXPTRNLEN)
+ warnx("pattern specified too long; truncated");
+ dr->dr_icond |= DRC_PATHPTRN;
+ av += 2;
+ } else
+ break;
+ }
+ while (av[0] != NULL) {
+ if (strcmp(av[0], "hide") == 0) {
+ dr->dr_iacts |= DRA_BACTS;
+ dr->dr_bacts |= DRB_HIDE;
+ ++av;
+ } else if (strcmp(av[0], "unhide") == 0) {
+ dr->dr_iacts |= DRA_BACTS;
+ dr->dr_bacts |= DRB_UNHIDE;
+ ++av;
+ } else if (strcmp(av[0], "user") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for user");
+ dr->dr_iacts |= DRA_UID;
+ pw = getpwnam(av[1]);
+ if (pw != NULL)
+ dr->dr_uid = pw->pw_uid;
+ else
+ dr->dr_uid = eatoi(av[1]); /* XXX overflow */
+ av += 2;
+ } else if (strcmp(av[0], "group") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for group");
+ dr->dr_iacts |= DRA_GID;
+ gr = getgrnam(av[1]);
+ if (gr != NULL)
+ dr->dr_gid = gr->gr_gid;
+ else
+ dr->dr_gid = eatoi(av[1]); /* XXX overflow */
+ av += 2;
+ } else if (strcmp(av[0], "mode") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for mode");
+ dr->dr_iacts |= DRA_MODE;
+ set = setmode(av[1]);
+ if (set == NULL)
+ errx(1, "invalid mode: %s", av[1]);
+ dr->dr_mode = getmode(set, 0);
+ av += 2;
+ } else if (strcmp(av[0], "include") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for include");
+ dr->dr_iacts |= DRA_INCSET;
+ dr->dr_incset = eatonum(av[1]);
+ av += 2;
+ } else
+ errx(1, "unknown argument: %s", av[0]);
+ }
+
+ dr->dr_id = mkrid(rsnum, rnum);
+ dr->dr_magic = DEVFS_MAGIC;
+}
+
+/*
+ * Write a human-readable (and machine-parsable, by rulespec_in*())
+ * representation of dr to bufp. *bufp should be free(3)'d when the
+ * caller is finished with it.
+ */
+static void
+rulespec_outfp(FILE *fp, struct devfs_rule *dr)
+{
+ struct intstr *is;
+ struct passwd *pw;
+ struct group *gr;
+
+ fprintf(fp, "%d", rid2rn(dr->dr_id));
+
+ if (dr->dr_icond & DRC_DSWFLAGS)
+ for (is = ist_type; is->s != NULL; ++is)
+ if (dr->dr_dswflags & is->i)
+ fprintf(fp, " type %s", is->s);
+ if (dr->dr_icond & DRC_PATHPTRN)
+ fprintf(fp, " path %s", dr->dr_pathptrn);
+
+ if (dr->dr_iacts & DRA_BACTS) {
+ if (dr->dr_bacts & DRB_HIDE)
+ fprintf(fp, " hide");
+ if (dr->dr_bacts & DRB_UNHIDE)
+ fprintf(fp, " unhide");
+ }
+ if (dr->dr_iacts & DRA_UID) {
+ pw = getpwuid(dr->dr_uid);
+ if (pw == NULL)
+ fprintf(fp, " user %d", dr->dr_uid);
+ else
+ fprintf(fp, " user %s", pw->pw_name);
+ }
+ if (dr->dr_iacts & DRA_GID) {
+ gr = getgrgid(dr->dr_gid);
+ if (gr == NULL)
+ fprintf(fp, " group %d", dr->dr_gid);
+ else
+ fprintf(fp, " group %s", gr->gr_name);
+ }
+ if (dr->dr_iacts & DRA_MODE)
+ fprintf(fp, " mode %o", dr->dr_mode);
+ if (dr->dr_iacts & DRA_INCSET)
+ fprintf(fp, " include %d", dr->dr_incset);
+
+ fprintf(fp, "\n");
+}
diff --git a/sbin/dhclient/Makefile b/sbin/dhclient/Makefile
new file mode 100644
index 0000000..7e32326
--- /dev/null
+++ b/sbin/dhclient/Makefile
@@ -0,0 +1,43 @@
+# $OpenBSD: Makefile,v 1.9 2004/05/04 12:52:05 henning Exp $
+# $FreeBSD$
+#
+# Copyright (c) 1996, 1997 The Internet Software Consortium.
+# 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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.
+#
+
+SRCS= dhclient.c clparse.c alloc.c dispatch.c hash.c bpf.c options.c \
+ tree.c conflex.c errwarn.c inet.c packet.c convert.c tables.c \
+ parse.c privsep.c
+
+PROG= dhclient
+SCRIPTS=dhclient-script
+MAN= dhclient.8 dhclient.conf.5 dhclient.leases.5 dhcp-options.5 \
+ dhclient-script.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/dhclient/alloc.c b/sbin/dhclient/alloc.c
new file mode 100644
index 0000000..cd60cec
--- /dev/null
+++ b/sbin/dhclient/alloc.c
@@ -0,0 +1,79 @@
+/* $OpenBSD: alloc.c,v 1.9 2004/05/04 20:28:40 deraadt Exp $ */
+
+/* Memory allocation... */
+
+/*
+ * Copyright (c) 1995, 1996, 1998 The Internet Software Consortium.
+ * 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "dhcpd.h"
+
+struct string_list *
+new_string_list(size_t size)
+{
+ struct string_list *rval;
+
+ rval = calloc(1, sizeof(struct string_list) + size);
+ if (rval != NULL)
+ rval->string = ((char *)rval) + sizeof(struct string_list);
+ return (rval);
+}
+
+struct hash_table *
+new_hash_table(int count)
+{
+ struct hash_table *rval;
+
+ rval = calloc(1, sizeof(struct hash_table) -
+ (DEFAULT_HASH_SIZE * sizeof(struct hash_bucket *)) +
+ (count * sizeof(struct hash_bucket *)));
+ if (rval == NULL)
+ return (NULL);
+ rval->hash_count = count;
+ return (rval);
+}
+
+struct hash_bucket *
+new_hash_bucket(void)
+{
+ struct hash_bucket *rval = calloc(1, sizeof(struct hash_bucket));
+
+ return (rval);
+}
diff --git a/sbin/dhclient/bpf.c b/sbin/dhclient/bpf.c
new file mode 100644
index 0000000..785db21
--- /dev/null
+++ b/sbin/dhclient/bpf.c
@@ -0,0 +1,387 @@
+/* $OpenBSD: bpf.c,v 1.13 2004/05/05 14:28:58 deraadt Exp $ */
+
+/* BPF socket interface code, originally contributed by Archie Cobbs. */
+
+/*
+ * Copyright (c) 1995, 1996, 1998, 1999
+ * The Internet Software Consortium. 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "dhcpd.h"
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+
+#include <net/bpf.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/if_ether.h>
+
+#define BPF_FORMAT "/dev/bpf%d"
+
+/*
+ * Called by get_interface_list for each interface that's discovered.
+ * Opens a packet filter for each interface and adds it to the select
+ * mask.
+ */
+int
+if_register_bpf(struct interface_info *info)
+{
+ char filename[50];
+ int sock, b;
+
+ /* Open a BPF device */
+ for (b = 0; 1; b++) {
+ snprintf(filename, sizeof(filename), BPF_FORMAT, b);
+ sock = open(filename, O_RDWR, 0);
+ if (sock < 0) {
+ if (errno == EBUSY)
+ continue;
+ else
+ error("Can't find free bpf: %m");
+ } else
+ break;
+ }
+
+ /* Set the BPF device to point at this interface. */
+ if (ioctl(sock, BIOCSETIF, info->ifp) < 0)
+ error("Can't attach interface %s to bpf device %s: %m",
+ info->name, filename);
+
+ return (sock);
+}
+
+void
+if_register_send(struct interface_info *info)
+{
+ /*
+ * If we're using the bpf API for sending and receiving, we
+ * don't need to register this interface twice.
+ */
+ info->wfdesc = info->rfdesc;
+}
+
+/*
+ * Packet filter program...
+ *
+ * XXX: Changes to the filter program may require changes to the
+ * constant offsets used in if_register_send to patch the BPF program!
+ */
+struct bpf_insn dhcp_bpf_filter[] = {
+ /* Make sure this is an IP packet... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
+
+ /* Make sure it's a UDP packet... */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
+
+ /* Make sure this isn't a fragment... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
+ BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
+
+ /* Get the IP header length... */
+ BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
+
+ /* Make sure it's to the right port... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */
+
+ /* If we passed all the tests, ask for the whole packet. */
+ BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
+
+ /* Otherwise, drop it. */
+ BPF_STMT(BPF_RET+BPF_K, 0),
+};
+
+int dhcp_bpf_filter_len = sizeof(dhcp_bpf_filter) / sizeof(struct bpf_insn);
+
+/*
+ * Packet write filter program:
+ * 'ip and udp and src port bootps and dst port (bootps or bootpc)'
+ */
+struct bpf_insn dhcp_bpf_wfilter[] = {
+ BPF_STMT(BPF_LD + BPF_B + BPF_IND, 14),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (IPVERSION << 4) + 5, 0, 12),
+
+ /* Make sure this is an IP packet... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 10),
+
+ /* Make sure it's a UDP packet... */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 8),
+
+ /* Make sure this isn't a fragment... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
+ BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 6, 0), /* patched */
+
+ /* Get the IP header length... */
+ BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
+
+ /* Make sure it's from the right port... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 14),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 68, 0, 3),
+
+ /* Make sure it is to the right ports ... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1),
+
+ /* If we passed all the tests, ask for the whole packet. */
+ BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
+
+ /* Otherwise, drop it. */
+ BPF_STMT(BPF_RET+BPF_K, 0),
+};
+
+int dhcp_bpf_wfilter_len = sizeof(dhcp_bpf_wfilter) / sizeof(struct bpf_insn);
+
+void
+if_register_receive(struct interface_info *info)
+{
+ struct bpf_version v;
+ struct bpf_program p;
+ int flag = 1, sz;
+
+ /* Open a BPF device and hang it on this interface... */
+ info->rfdesc = if_register_bpf(info);
+
+ /* Make sure the BPF version is in range... */
+ if (ioctl(info->rfdesc, BIOCVERSION, &v) < 0)
+ error("Can't get BPF version: %m");
+
+ if (v.bv_major != BPF_MAJOR_VERSION ||
+ v.bv_minor < BPF_MINOR_VERSION)
+ error("Kernel BPF version out of range - recompile dhcpd!");
+
+ /*
+ * Set immediate mode so that reads return as soon as a packet
+ * comes in, rather than waiting for the input buffer to fill
+ * with packets.
+ */
+ if (ioctl(info->rfdesc, BIOCIMMEDIATE, &flag) < 0)
+ error("Can't set immediate mode on bpf device: %m");
+
+ /* Get the required BPF buffer length from the kernel. */
+ if (ioctl(info->rfdesc, BIOCGBLEN, &sz) < 0)
+ error("Can't get bpf buffer length: %m");
+ info->rbuf_max = sz;
+ info->rbuf = malloc(info->rbuf_max);
+ if (!info->rbuf)
+ error("Can't allocate %lu bytes for bpf input buffer.",
+ (unsigned long)info->rbuf_max);
+ info->rbuf_offset = 0;
+ info->rbuf_len = 0;
+
+ /* Set up the bpf filter program structure. */
+ p.bf_len = dhcp_bpf_filter_len;
+ p.bf_insns = dhcp_bpf_filter;
+
+ /* Patch the server port into the BPF program...
+ *
+ * XXX: changes to filter program may require changes to the
+ * insn number(s) used below!
+ */
+ dhcp_bpf_filter[8].k = LOCAL_PORT;
+
+ if (ioctl(info->rfdesc, BIOCSETF, &p) < 0)
+ error("Can't install packet filter program: %m");
+
+ /* Set up the bpf write filter program structure. */
+ p.bf_len = dhcp_bpf_wfilter_len;
+ p.bf_insns = dhcp_bpf_wfilter;
+
+ if (dhcp_bpf_wfilter[7].k == 0x1fff)
+ dhcp_bpf_wfilter[7].k = htons(IP_MF|IP_OFFMASK);
+
+ if (ioctl(info->rfdesc, BIOCSETWF, &p) < 0)
+ error("Can't install write filter program: %m");
+
+ if (ioctl(info->rfdesc, BIOCLOCK, NULL) < 0)
+ error("Cannot lock bpf");
+}
+
+ssize_t
+send_packet(struct interface_info *interface, struct dhcp_packet *raw,
+ size_t len, struct in_addr from, struct sockaddr_in *to,
+ struct hardware *hto)
+{
+ unsigned char buf[256];
+ struct iovec iov[2];
+ int result, bufp = 0;
+
+ /* Assemble the headers... */
+ assemble_hw_header(interface, buf, &bufp, hto);
+ assemble_udp_ip_header(buf, &bufp, from.s_addr,
+ to->sin_addr.s_addr, to->sin_port, (unsigned char *)raw, len);
+
+ /* Fire it off */
+ iov[0].iov_base = (char *)buf;
+ iov[0].iov_len = bufp;
+ iov[1].iov_base = (char *)raw;
+ iov[1].iov_len = len;
+
+ result = writev(interface->wfdesc, iov, 2);
+ if (result < 0)
+ warning("send_packet: %m");
+ return (result);
+}
+
+ssize_t
+receive_packet(struct interface_info *interface, unsigned char *buf,
+ size_t len, struct sockaddr_in *from, struct hardware *hfrom)
+{
+ int length = 0, offset = 0;
+ struct bpf_hdr hdr;
+
+ /*
+ * All this complexity is because BPF doesn't guarantee that
+ * only one packet will be returned at a time. We're getting
+ * what we deserve, though - this is a terrible abuse of the BPF
+ * interface. Sigh.
+ */
+
+ /* Process packets until we get one we can return or until we've
+ * done a read and gotten nothing we can return...
+ */
+ do {
+ /* If the buffer is empty, fill it. */
+ if (interface->rbuf_offset == interface->rbuf_len) {
+ length = read(interface->rfdesc, interface->rbuf,
+ interface->rbuf_max);
+ if (length <= 0)
+ return (length);
+ interface->rbuf_offset = 0;
+ interface->rbuf_len = length;
+ }
+
+ /*
+ * If there isn't room for a whole bpf header, something
+ * went wrong, but we'll ignore it and hope it goes
+ * away... XXX
+ */
+ if (interface->rbuf_len - interface->rbuf_offset <
+ sizeof(hdr)) {
+ interface->rbuf_offset = interface->rbuf_len;
+ continue;
+ }
+
+ /* Copy out a bpf header... */
+ memcpy(&hdr, &interface->rbuf[interface->rbuf_offset],
+ sizeof(hdr));
+
+ /*
+ * If the bpf header plus data doesn't fit in what's
+ * left of the buffer, stick head in sand yet again...
+ */
+ if (interface->rbuf_offset + hdr.bh_hdrlen + hdr.bh_caplen >
+ interface->rbuf_len) {
+ interface->rbuf_offset = interface->rbuf_len;
+ continue;
+ }
+
+ /* Skip over the BPF header... */
+ interface->rbuf_offset += hdr.bh_hdrlen;
+
+ /*
+ * If the captured data wasn't the whole packet, or if
+ * the packet won't fit in the input buffer, all we can
+ * do is drop it.
+ */
+ if (hdr.bh_caplen != hdr.bh_datalen) {
+ interface->rbuf_offset =
+ BPF_WORDALIGN(interface->rbuf_offset +
+ hdr.bh_caplen);
+ continue;
+ }
+
+ /* Decode the physical header... */
+ offset = decode_hw_header(interface->rbuf,
+ interface->rbuf_offset, hfrom);
+
+ /*
+ * If a physical layer checksum failed (dunno of any
+ * physical layer that supports this, but WTH), skip
+ * this packet.
+ */
+ if (offset < 0) {
+ interface->rbuf_offset =
+ BPF_WORDALIGN(interface->rbuf_offset +
+ hdr.bh_caplen);
+ continue;
+ }
+ interface->rbuf_offset += offset;
+ hdr.bh_caplen -= offset;
+
+ /* Decode the IP and UDP headers... */
+ offset = decode_udp_ip_header(interface->rbuf,
+ interface->rbuf_offset, from, NULL, hdr.bh_caplen);
+
+ /* If the IP or UDP checksum was bad, skip the packet... */
+ if (offset < 0) {
+ interface->rbuf_offset =
+ BPF_WORDALIGN(interface->rbuf_offset +
+ hdr.bh_caplen);
+ continue;
+ }
+ interface->rbuf_offset += offset;
+ hdr.bh_caplen -= offset;
+
+ /*
+ * If there's not enough room to stash the packet data,
+ * we have to skip it (this shouldn't happen in real
+ * life, though).
+ */
+ if (hdr.bh_caplen > len) {
+ interface->rbuf_offset =
+ BPF_WORDALIGN(interface->rbuf_offset +
+ hdr.bh_caplen);
+ continue;
+ }
+
+ /* Copy out the data in the packet... */
+ memcpy(buf, interface->rbuf + interface->rbuf_offset,
+ hdr.bh_caplen);
+ interface->rbuf_offset =
+ BPF_WORDALIGN(interface->rbuf_offset +
+ hdr.bh_caplen);
+ return (hdr.bh_caplen);
+ } while (!length);
+ return (0);
+}
diff --git a/sbin/dhclient/clparse.c b/sbin/dhclient/clparse.c
new file mode 100644
index 0000000..e43a876
--- /dev/null
+++ b/sbin/dhclient/clparse.c
@@ -0,0 +1,941 @@
+/* $OpenBSD: clparse.c,v 1.18 2004/09/15 18:15:18 henning Exp $ */
+
+/* Parser for dhclient config and lease files... */
+
+/*
+ * Copyright (c) 1997 The Internet Software Consortium.
+ * 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "dhcpd.h"
+#include "dhctoken.h"
+
+struct client_config top_level_config;
+struct interface_info *dummy_interfaces;
+extern struct interface_info *ifi;
+
+char client_script_name[] = "/sbin/dhclient-script";
+
+/*
+ * client-conf-file :== client-declarations EOF
+ * client-declarations :== <nil>
+ * | client-declaration
+ * | client-declarations client-declaration
+ */
+int
+read_client_conf(void)
+{
+ FILE *cfile;
+ char *val;
+ int token;
+ struct client_config *config;
+
+ new_parse(path_dhclient_conf);
+
+ /* Set up the initial dhcp option universe. */
+ initialize_universes();
+
+ /* Initialize the top level client configuration. */
+ memset(&top_level_config, 0, sizeof(top_level_config));
+
+ /* Set some defaults... */
+ top_level_config.timeout = 60;
+ top_level_config.select_interval = 0;
+ top_level_config.reboot_timeout = 10;
+ top_level_config.retry_interval = 300;
+ top_level_config.backoff_cutoff = 15;
+ top_level_config.initial_interval = 3;
+ top_level_config.bootp_policy = ACCEPT;
+ top_level_config.script_name = client_script_name;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_SUBNET_MASK;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_BROADCAST_ADDRESS;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_TIME_OFFSET;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_ROUTERS;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_DOMAIN_NAME;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] =
+ DHO_DOMAIN_NAME_SERVERS;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_HOST_NAME;
+
+ if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
+ do {
+ token = peek_token(&val, cfile);
+ if (token == EOF)
+ break;
+ parse_client_statement(cfile, NULL, &top_level_config);
+ } while (1);
+ token = next_token(&val, cfile); /* Clear the peek buffer */
+ fclose(cfile);
+ }
+
+ /*
+ * Set up state and config structures for clients that don't
+ * have per-interface configuration declarations.
+ */
+ config = NULL;
+ if (!ifi->client) {
+ ifi->client = malloc(sizeof(struct client_state));
+ if (!ifi->client)
+ error("no memory for client state.");
+ memset(ifi->client, 0, sizeof(*(ifi->client)));
+ }
+ if (!ifi->client->config) {
+ if (!config) {
+ config = malloc(sizeof(struct client_config));
+ if (!config)
+ error("no memory for client config.");
+ memcpy(config, &top_level_config,
+ sizeof(top_level_config));
+ }
+ ifi->client->config = config;
+ }
+
+ return (!warnings_occurred);
+}
+
+/*
+ * lease-file :== client-lease-statements EOF
+ * client-lease-statements :== <nil>
+ * | client-lease-statements LEASE client-lease-statement
+ */
+void
+read_client_leases(void)
+{
+ FILE *cfile;
+ char *val;
+ int token;
+
+ new_parse(path_dhclient_db);
+
+ /* Open the lease file. If we can't open it, just return -
+ we can safely trust the server to remember our state. */
+ if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
+ return;
+ do {
+ token = next_token(&val, cfile);
+ if (token == EOF)
+ break;
+ if (token != LEASE) {
+ warning("Corrupt lease file - possible data loss!");
+ skip_to_semi(cfile);
+ break;
+ } else
+ parse_client_lease_statement(cfile, 0);
+
+ } while (1);
+ fclose(cfile);
+}
+
+/*
+ * client-declaration :==
+ * SEND option-decl |
+ * DEFAULT option-decl |
+ * SUPERSEDE option-decl |
+ * PREPEND option-decl |
+ * APPEND option-decl |
+ * hardware-declaration |
+ * REQUEST option-list |
+ * REQUIRE option-list |
+ * TIMEOUT number |
+ * RETRY number |
+ * REBOOT number |
+ * SELECT_TIMEOUT number |
+ * SCRIPT string |
+ * interface-declaration |
+ * LEASE client-lease-statement |
+ * ALIAS client-lease-statement
+ */
+void
+parse_client_statement(FILE *cfile, struct interface_info *ip,
+ struct client_config *config)
+{
+ int token;
+ char *val;
+ struct option *option;
+
+ switch (next_token(&val, cfile)) {
+ case SEND:
+ parse_option_decl(cfile, &config->send_options[0]);
+ return;
+ case DEFAULT:
+ option = parse_option_decl(cfile, &config->defaults[0]);
+ if (option)
+ config->default_actions[option->code] = ACTION_DEFAULT;
+ return;
+ case SUPERSEDE:
+ option = parse_option_decl(cfile, &config->defaults[0]);
+ if (option)
+ config->default_actions[option->code] =
+ ACTION_SUPERSEDE;
+ return;
+ case APPEND:
+ option = parse_option_decl(cfile, &config->defaults[0]);
+ if (option)
+ config->default_actions[option->code] = ACTION_APPEND;
+ return;
+ case PREPEND:
+ option = parse_option_decl(cfile, &config->defaults[0]);
+ if (option)
+ config->default_actions[option->code] = ACTION_PREPEND;
+ return;
+ case MEDIA:
+ parse_string_list(cfile, &config->media, 1);
+ return;
+ case HARDWARE:
+ if (ip)
+ parse_hardware_param(cfile, &ip->hw_address);
+ else {
+ parse_warn("hardware address parameter %s",
+ "not allowed here.");
+ skip_to_semi(cfile);
+ }
+ return;
+ case REQUEST:
+ config->requested_option_count =
+ parse_option_list(cfile, config->requested_options);
+ return;
+ case REQUIRE:
+ memset(config->required_options, 0,
+ sizeof(config->required_options));
+ parse_option_list(cfile, config->required_options);
+ return;
+ case TIMEOUT:
+ parse_lease_time(cfile, &config->timeout);
+ return;
+ case RETRY:
+ parse_lease_time(cfile, &config->retry_interval);
+ return;
+ case SELECT_TIMEOUT:
+ parse_lease_time(cfile, &config->select_interval);
+ return;
+ case REBOOT:
+ parse_lease_time(cfile, &config->reboot_timeout);
+ return;
+ case BACKOFF_CUTOFF:
+ parse_lease_time(cfile, &config->backoff_cutoff);
+ return;
+ case INITIAL_INTERVAL:
+ parse_lease_time(cfile, &config->initial_interval);
+ return;
+ case SCRIPT:
+ config->script_name = parse_string(cfile);
+ return;
+ case INTERFACE:
+ if (ip)
+ parse_warn("nested interface declaration.");
+ parse_interface_declaration(cfile, config);
+ return;
+ case LEASE:
+ parse_client_lease_statement(cfile, 1);
+ return;
+ case ALIAS:
+ parse_client_lease_statement(cfile, 2);
+ return;
+ case REJECT:
+ parse_reject_statement(cfile, config);
+ return;
+ default:
+ parse_warn("expecting a statement.");
+ skip_to_semi(cfile);
+ break;
+ }
+ token = next_token(&val, cfile);
+ if (token != SEMI) {
+ parse_warn("semicolon expected.");
+ skip_to_semi(cfile);
+ }
+}
+
+int
+parse_X(FILE *cfile, u_int8_t *buf, int max)
+{
+ int token;
+ char *val;
+ int len;
+
+ token = peek_token(&val, cfile);
+ if (token == NUMBER_OR_NAME || token == NUMBER) {
+ len = 0;
+ do {
+ token = next_token(&val, cfile);
+ if (token != NUMBER && token != NUMBER_OR_NAME) {
+ parse_warn("expecting hexadecimal constant.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ convert_num(&buf[len], val, 16, 8);
+ if (len++ > max) {
+ parse_warn("hexadecimal constant too long.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ token = peek_token(&val, cfile);
+ if (token == COLON)
+ token = next_token(&val, cfile);
+ } while (token == COLON);
+ val = (char *)buf;
+ } else if (token == STRING) {
+ token = next_token(&val, cfile);
+ len = strlen(val);
+ if (len + 1 > max) {
+ parse_warn("string constant too long.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ memcpy(buf, val, len + 1);
+ } else {
+ parse_warn("expecting string or hexadecimal data");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ return (len);
+}
+
+/*
+ * option-list :== option_name |
+ * option_list COMMA option_name
+ */
+int
+parse_option_list(FILE *cfile, u_int8_t *list)
+{
+ int ix, i;
+ int token;
+ char *val;
+
+ ix = 0;
+ do {
+ token = next_token(&val, cfile);
+ if (!is_identifier(token)) {
+ parse_warn("expected option name.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ for (i = 0; i < 256; i++)
+ if (!strcasecmp(dhcp_options[i].name, val))
+ break;
+
+ if (i == 256) {
+ parse_warn("%s: unexpected option name.", val);
+ skip_to_semi(cfile);
+ return (0);
+ }
+ list[ix++] = i;
+ if (ix == 256) {
+ parse_warn("%s: too many options.", val);
+ skip_to_semi(cfile);
+ return (0);
+ }
+ token = next_token(&val, cfile);
+ } while (token == COMMA);
+ if (token != SEMI) {
+ parse_warn("expecting semicolon.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ return (ix);
+}
+
+/*
+ * interface-declaration :==
+ * INTERFACE string LBRACE client-declarations RBRACE
+ */
+void
+parse_interface_declaration(FILE *cfile, struct client_config *outer_config)
+{
+ int token;
+ char *val;
+ struct interface_info *ip;
+
+ token = next_token(&val, cfile);
+ if (token != STRING) {
+ parse_warn("expecting interface name (in quotes).");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ ip = interface_or_dummy(val);
+
+ if (!ip->client)
+ make_client_state(ip);
+
+ if (!ip->client->config)
+ make_client_config(ip, outer_config);
+
+ token = next_token(&val, cfile);
+ if (token != LBRACE) {
+ parse_warn("expecting left brace.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ do {
+ token = peek_token(&val, cfile);
+ if (token == EOF) {
+ parse_warn("unterminated interface declaration.");
+ return;
+ }
+ if (token == RBRACE)
+ break;
+ parse_client_statement(cfile, ip, ip->client->config);
+ } while (1);
+ token = next_token(&val, cfile);
+}
+
+struct interface_info *
+interface_or_dummy(char *name)
+{
+ struct interface_info *ip;
+
+ /* Find the interface (if any) that matches the name. */
+ if (!strcmp(ifi->name, name))
+ return (ifi);
+
+ /* If it's not a real interface, see if it's on the dummy list. */
+ for (ip = dummy_interfaces; ip; ip = ip->next)
+ if (!strcmp(ip->name, name))
+ return (ip);
+
+ /*
+ * If we didn't find an interface, make a dummy interface as a
+ * placeholder.
+ */
+ ip = malloc(sizeof(*ip));
+ if (!ip)
+ error("Insufficient memory to record interface %s", name);
+ memset(ip, 0, sizeof(*ip));
+ strlcpy(ip->name, name, IFNAMSIZ);
+ ip->next = dummy_interfaces;
+ dummy_interfaces = ip;
+ return (ip);
+}
+
+void
+make_client_state(struct interface_info *ip)
+{
+ ip->client = malloc(sizeof(*(ip->client)));
+ if (!ip->client)
+ error("no memory for state on %s", ip->name);
+ memset(ip->client, 0, sizeof(*(ip->client)));
+}
+
+void
+make_client_config(struct interface_info *ip, struct client_config *config)
+{
+ ip->client->config = malloc(sizeof(struct client_config));
+ if (!ip->client->config)
+ error("no memory for config for %s", ip->name);
+ memset(ip->client->config, 0, sizeof(*(ip->client->config)));
+ memcpy(ip->client->config, config, sizeof(*config));
+}
+
+/*
+ * client-lease-statement :==
+ * RBRACE client-lease-declarations LBRACE
+ *
+ * client-lease-declarations :==
+ * <nil> |
+ * client-lease-declaration |
+ * client-lease-declarations client-lease-declaration
+ */
+void
+parse_client_lease_statement(FILE *cfile, int is_static)
+{
+ struct client_lease *lease, *lp, *pl;
+ struct interface_info *ip;
+ int token;
+ char *val;
+
+ token = next_token(&val, cfile);
+ if (token != LBRACE) {
+ parse_warn("expecting left brace.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ lease = malloc(sizeof(struct client_lease));
+ if (!lease)
+ error("no memory for lease.");
+ memset(lease, 0, sizeof(*lease));
+ lease->is_static = is_static;
+
+ ip = NULL;
+
+ do {
+ token = peek_token(&val, cfile);
+ if (token == EOF) {
+ parse_warn("unterminated lease declaration.");
+ return;
+ }
+ if (token == RBRACE)
+ break;
+ parse_client_lease_declaration(cfile, lease, &ip);
+ } while (1);
+ token = next_token(&val, cfile);
+
+ /* If the lease declaration didn't include an interface
+ * declaration that we recognized, it's of no use to us.
+ */
+ if (!ip) {
+ free_client_lease(lease);
+ return;
+ }
+
+ /* Make sure there's a client state structure... */
+ if (!ip->client)
+ make_client_state(ip);
+
+ /* If this is an alias lease, it doesn't need to be sorted in. */
+ if (is_static == 2) {
+ ip->client->alias = lease;
+ return;
+ }
+
+ /*
+ * The new lease may supersede a lease that's not the active
+ * lease but is still on the lease list, so scan the lease list
+ * looking for a lease with the same address, and if we find it,
+ * toss it.
+ */
+ pl = NULL;
+ for (lp = ip->client->leases; lp; lp = lp->next) {
+ if (lp->address.len == lease->address.len &&
+ !memcmp(lp->address.iabuf, lease->address.iabuf,
+ lease->address.len)) {
+ if (pl)
+ pl->next = lp->next;
+ else
+ ip->client->leases = lp->next;
+ free_client_lease(lp);
+ break;
+ }
+ }
+
+ /*
+ * If this is a preloaded lease, just put it on the list of
+ * recorded leases - don't make it the active lease.
+ */
+ if (is_static) {
+ lease->next = ip->client->leases;
+ ip->client->leases = lease;
+ return;
+ }
+
+ /*
+ * The last lease in the lease file on a particular interface is
+ * the active lease for that interface. Of course, we don't
+ * know what the last lease in the file is until we've parsed
+ * the whole file, so at this point, we assume that the lease we
+ * just parsed is the active lease for its interface. If
+ * there's already an active lease for the interface, and this
+ * lease is for the same ip address, then we just toss the old
+ * active lease and replace it with this one. If this lease is
+ * for a different address, then if the old active lease has
+ * expired, we dump it; if not, we put it on the list of leases
+ * for this interface which are still valid but no longer
+ * active.
+ */
+ if (ip->client->active) {
+ if (ip->client->active->expiry < cur_time)
+ free_client_lease(ip->client->active);
+ else if (ip->client->active->address.len ==
+ lease->address.len &&
+ !memcmp(ip->client->active->address.iabuf,
+ lease->address.iabuf, lease->address.len))
+ free_client_lease(ip->client->active);
+ else {
+ ip->client->active->next = ip->client->leases;
+ ip->client->leases = ip->client->active;
+ }
+ }
+ ip->client->active = lease;
+
+ /* Phew. */
+}
+
+/*
+ * client-lease-declaration :==
+ * BOOTP |
+ * INTERFACE string |
+ * FIXED_ADDR ip_address |
+ * FILENAME string |
+ * SERVER_NAME string |
+ * OPTION option-decl |
+ * RENEW time-decl |
+ * REBIND time-decl |
+ * EXPIRE time-decl
+ */
+void
+parse_client_lease_declaration(FILE *cfile, struct client_lease *lease,
+ struct interface_info **ipp)
+{
+ int token;
+ char *val;
+ struct interface_info *ip;
+
+ switch (next_token(&val, cfile)) {
+ case BOOTP:
+ lease->is_bootp = 1;
+ break;
+ case INTERFACE:
+ token = next_token(&val, cfile);
+ if (token != STRING) {
+ parse_warn("expecting interface name (in quotes).");
+ skip_to_semi(cfile);
+ break;
+ }
+ ip = interface_or_dummy(val);
+ *ipp = ip;
+ break;
+ case FIXED_ADDR:
+ if (!parse_ip_addr(cfile, &lease->address))
+ return;
+ break;
+ case MEDIUM:
+ parse_string_list(cfile, &lease->medium, 0);
+ return;
+ case FILENAME:
+ lease->filename = parse_string(cfile);
+ return;
+ case SERVER_NAME:
+ lease->server_name = parse_string(cfile);
+ return;
+ case RENEW:
+ lease->renewal = parse_date(cfile);
+ return;
+ case REBIND:
+ lease->rebind = parse_date(cfile);
+ return;
+ case EXPIRE:
+ lease->expiry = parse_date(cfile);
+ return;
+ case OPTION:
+ parse_option_decl(cfile, lease->options);
+ return;
+ default:
+ parse_warn("expecting lease declaration.");
+ skip_to_semi(cfile);
+ break;
+ }
+ token = next_token(&val, cfile);
+ if (token != SEMI) {
+ parse_warn("expecting semicolon.");
+ skip_to_semi(cfile);
+ }
+}
+
+struct option *
+parse_option_decl(FILE *cfile, struct option_data *options)
+{
+ char *val;
+ int token;
+ u_int8_t buf[4];
+ u_int8_t hunkbuf[1024];
+ int hunkix = 0;
+ char *vendor;
+ char *fmt;
+ struct universe *universe;
+ struct option *option;
+ struct iaddr ip_addr;
+ u_int8_t *dp;
+ int len;
+ int nul_term = 0;
+
+ token = next_token(&val, cfile);
+ if (!is_identifier(token)) {
+ parse_warn("expecting identifier after option keyword.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ if ((vendor = strdup(val)) == NULL)
+ error("no memory for vendor information.");
+
+ token = peek_token(&val, cfile);
+ if (token == DOT) {
+ /* Go ahead and take the DOT token... */
+ token = next_token(&val, cfile);
+
+ /* The next token should be an identifier... */
+ token = next_token(&val, cfile);
+ if (!is_identifier(token)) {
+ parse_warn("expecting identifier after '.'");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+
+ /* Look up the option name hash table for the specified
+ vendor. */
+ universe = ((struct universe *)hash_lookup(&universe_hash,
+ (unsigned char *)vendor, 0));
+ /* If it's not there, we can't parse the rest of the
+ declaration. */
+ if (!universe) {
+ parse_warn("no vendor named %s.", vendor);
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ } else {
+ /* Use the default hash table, which contains all the
+ standard dhcp option names. */
+ val = vendor;
+ universe = &dhcp_universe;
+ }
+
+ /* Look up the actual option info... */
+ option = (struct option *)hash_lookup(universe->hash,
+ (unsigned char *)val, 0);
+
+ /* If we didn't get an option structure, it's an undefined option. */
+ if (!option) {
+ if (val == vendor)
+ parse_warn("no option named %s", val);
+ else
+ parse_warn("no option named %s for vendor %s",
+ val, vendor);
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+
+ /* Free the initial identifier token. */
+ free(vendor);
+
+ /* Parse the option data... */
+ do {
+ for (fmt = option->format; *fmt; fmt++) {
+ if (*fmt == 'A')
+ break;
+ switch (*fmt) {
+ case 'X':
+ len = parse_X(cfile, &hunkbuf[hunkix],
+ sizeof(hunkbuf) - hunkix);
+ hunkix += len;
+ break;
+ case 't': /* Text string... */
+ token = next_token(&val, cfile);
+ if (token != STRING) {
+ parse_warn("expecting string.");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ len = strlen(val);
+ if (hunkix + len + 1 > sizeof(hunkbuf)) {
+ parse_warn("option data buffer %s",
+ "overflow");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ memcpy(&hunkbuf[hunkix], val, len + 1);
+ nul_term = 1;
+ hunkix += len;
+ break;
+ case 'I': /* IP address. */
+ if (!parse_ip_addr(cfile, &ip_addr))
+ return (NULL);
+ len = ip_addr.len;
+ dp = ip_addr.iabuf;
+alloc:
+ if (hunkix + len > sizeof(hunkbuf)) {
+ parse_warn("option data buffer "
+ "overflow");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ memcpy(&hunkbuf[hunkix], dp, len);
+ hunkix += len;
+ break;
+ case 'L': /* Unsigned 32-bit integer... */
+ case 'l': /* Signed 32-bit integer... */
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+need_number:
+ parse_warn("expecting number.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ convert_num(buf, val, 0, 32);
+ len = 4;
+ dp = buf;
+ goto alloc;
+ case 's': /* Signed 16-bit integer. */
+ case 'S': /* Unsigned 16-bit integer. */
+ token = next_token(&val, cfile);
+ if (token != NUMBER)
+ goto need_number;
+ convert_num(buf, val, 0, 16);
+ len = 2;
+ dp = buf;
+ goto alloc;
+ case 'b': /* Signed 8-bit integer. */
+ case 'B': /* Unsigned 8-bit integer. */
+ token = next_token(&val, cfile);
+ if (token != NUMBER)
+ goto need_number;
+ convert_num(buf, val, 0, 8);
+ len = 1;
+ dp = buf;
+ goto alloc;
+ case 'f': /* Boolean flag. */
+ token = next_token(&val, cfile);
+ if (!is_identifier(token)) {
+ parse_warn("expecting identifier.");
+bad_flag:
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ if (!strcasecmp(val, "true") ||
+ !strcasecmp(val, "on"))
+ buf[0] = 1;
+ else if (!strcasecmp(val, "false") ||
+ !strcasecmp(val, "off"))
+ buf[0] = 0;
+ else {
+ parse_warn("expecting boolean.");
+ goto bad_flag;
+ }
+ len = 1;
+ dp = buf;
+ goto alloc;
+ default:
+ warning("Bad format %c in parse_option_param.",
+ *fmt);
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ }
+ token = next_token(&val, cfile);
+ } while (*fmt == 'A' && token == COMMA);
+
+ if (token != SEMI) {
+ parse_warn("semicolon expected.");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+
+ options[option->code].data = malloc(hunkix + nul_term);
+ if (!options[option->code].data)
+ error("out of memory allocating option data.");
+ memcpy(options[option->code].data, hunkbuf, hunkix + nul_term);
+ options[option->code].len = hunkix;
+ return (option);
+}
+
+void
+parse_string_list(FILE *cfile, struct string_list **lp, int multiple)
+{
+ int token;
+ char *val;
+ struct string_list *cur, *tmp;
+
+ /* Find the last medium in the media list. */
+ if (*lp)
+ for (cur = *lp; cur->next; cur = cur->next)
+ ; /* nothing */
+ else
+ cur = NULL;
+
+ do {
+ token = next_token(&val, cfile);
+ if (token != STRING) {
+ parse_warn("Expecting media options.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ tmp = new_string_list(strlen(val) + 1);
+ if (tmp == NULL)
+ error("no memory for string list entry.");
+ strlcpy(tmp->string, val, strlen(val) + 1);
+ tmp->next = NULL;
+
+ /* Store this medium at the end of the media list. */
+ if (cur)
+ cur->next = tmp;
+ else
+ *lp = tmp;
+ cur = tmp;
+
+ token = next_token(&val, cfile);
+ } while (multiple && token == COMMA);
+
+ if (token != SEMI) {
+ parse_warn("expecting semicolon.");
+ skip_to_semi(cfile);
+ }
+}
+
+void
+parse_reject_statement(FILE *cfile, struct client_config *config)
+{
+ int token;
+ char *val;
+ struct iaddr addr;
+ struct iaddrlist *list;
+
+ do {
+ if (!parse_ip_addr(cfile, &addr)) {
+ parse_warn("expecting IP address.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ list = malloc(sizeof(struct iaddrlist));
+ if (!list)
+ error("no memory for reject list!");
+
+ list->addr = addr;
+ list->next = config->reject_list;
+ config->reject_list = list;
+
+ token = next_token(&val, cfile);
+ } while (token == COMMA);
+
+ if (token != SEMI) {
+ parse_warn("expecting semicolon.");
+ skip_to_semi(cfile);
+ }
+}
diff --git a/sbin/dhclient/conflex.c b/sbin/dhclient/conflex.c
new file mode 100644
index 0000000..3c8932d
--- /dev/null
+++ b/sbin/dhclient/conflex.c
@@ -0,0 +1,529 @@
+/* $OpenBSD: conflex.c,v 1.7 2004/09/15 19:02:38 deraadt Exp $ */
+
+/* Lexical scanner for dhcpd config file... */
+
+/*
+ * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium.
+ * 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <ctype.h>
+
+#include "dhcpd.h"
+#include "dhctoken.h"
+
+int lexline;
+int lexchar;
+char *token_line;
+char *prev_line;
+char *cur_line;
+char *tlname;
+int eol_token;
+
+static char line1[81];
+static char line2[81];
+static int lpos;
+static int line;
+static int tlpos;
+static int tline;
+static int token;
+static int ugflag;
+static char *tval;
+static char tokbuf[1500];
+
+static int get_char(FILE *);
+static int get_token(FILE *);
+static void skip_to_eol(FILE *);
+static int read_string(FILE *);
+static int read_number(int, FILE *);
+static int read_num_or_name(int, FILE *);
+static int intern(char *, int);
+
+void
+new_parse(char *name)
+{
+ tlname = name;
+ lpos = line = 1;
+ cur_line = line1;
+ prev_line = line2;
+ token_line = cur_line;
+ cur_line[0] = prev_line[0] = 0;
+ warnings_occurred = 0;
+}
+
+static int
+get_char(FILE *cfile)
+{
+ int c = getc(cfile);
+ if (!ugflag) {
+ if (c == '\n') {
+ if (cur_line == line1) {
+ cur_line = line2;
+ prev_line = line1;
+ } else {
+ cur_line = line2;
+ prev_line = line1;
+ }
+ line++;
+ lpos = 1;
+ cur_line[0] = 0;
+ } else if (c != EOF) {
+ if (lpos < sizeof(line1)) {
+ cur_line[lpos - 1] = c;
+ cur_line[lpos] = 0;
+ }
+ lpos++;
+ }
+ } else
+ ugflag = 0;
+ return (c);
+}
+
+static int
+get_token(FILE *cfile)
+{
+ int c, ttok;
+ static char tb[2];
+ int l, p;
+
+ do {
+ l = line;
+ p = lpos;
+
+ c = get_char(cfile);
+
+ if (!(c == '\n' && eol_token) && isascii(c) && isspace(c))
+ continue;
+ if (c == '#') {
+ skip_to_eol(cfile);
+ continue;
+ }
+ if (c == '"') {
+ lexline = l;
+ lexchar = p;
+ ttok = read_string(cfile);
+ break;
+ }
+ if ((isascii(c) && isdigit(c)) || c == '-') {
+ lexline = l;
+ lexchar = p;
+ ttok = read_number(c, cfile);
+ break;
+ } else if (isascii(c) && isalpha(c)) {
+ lexline = l;
+ lexchar = p;
+ ttok = read_num_or_name(c, cfile);
+ break;
+ } else {
+ lexline = l;
+ lexchar = p;
+ tb[0] = c;
+ tb[1] = 0;
+ tval = tb;
+ ttok = c;
+ break;
+ }
+ } while (1);
+ return (ttok);
+}
+
+int
+next_token(char **rval, FILE *cfile)
+{
+ int rv;
+
+ if (token) {
+ if (lexline != tline)
+ token_line = cur_line;
+ lexchar = tlpos;
+ lexline = tline;
+ rv = token;
+ token = 0;
+ } else {
+ rv = get_token(cfile);
+ token_line = cur_line;
+ }
+ if (rval)
+ *rval = tval;
+
+ return (rv);
+}
+
+int
+peek_token(char **rval, FILE *cfile)
+{
+ int x;
+
+ if (!token) {
+ tlpos = lexchar;
+ tline = lexline;
+ token = get_token(cfile);
+ if (lexline != tline)
+ token_line = prev_line;
+ x = lexchar;
+ lexchar = tlpos;
+ tlpos = x;
+ x = lexline;
+ lexline = tline;
+ tline = x;
+ }
+ if (rval)
+ *rval = tval;
+
+ return (token);
+}
+
+static void
+skip_to_eol(FILE *cfile)
+{
+ int c;
+
+ do {
+ c = get_char(cfile);
+ if (c == EOF)
+ return;
+ if (c == '\n')
+ return;
+ } while (1);
+}
+
+static int
+read_string(FILE *cfile)
+{
+ int i, c, bs = 0;
+
+ for (i = 0; i < sizeof(tokbuf); i++) {
+ c = get_char(cfile);
+ if (c == EOF) {
+ parse_warn("eof in string constant");
+ break;
+ }
+ if (bs) {
+ bs = 0;
+ i--;
+ tokbuf[i] = c;
+ } else if (c == '\\')
+ bs = 1;
+ else if (c == '"')
+ break;
+ else
+ tokbuf[i] = c;
+ }
+ /*
+ * Normally, I'd feel guilty about this, but we're talking about
+ * strings that'll fit in a DHCP packet here...
+ */
+ if (i == sizeof(tokbuf)) {
+ parse_warn("string constant larger than internal buffer");
+ i--;
+ }
+ tokbuf[i] = 0;
+ tval = tokbuf;
+ return (STRING);
+}
+
+static int
+read_number(int c, FILE *cfile)
+{
+ int seenx = 0, i = 0, token = NUMBER;
+
+ tokbuf[i++] = c;
+ for (; i < sizeof(tokbuf); i++) {
+ c = get_char(cfile);
+ if (!seenx && c == 'x')
+ seenx = 1;
+ else if (!isascii(c) || !isxdigit(c)) {
+ ungetc(c, cfile);
+ ugflag = 1;
+ break;
+ }
+ tokbuf[i] = c;
+ }
+ if (i == sizeof(tokbuf)) {
+ parse_warn("numeric token larger than internal buffer");
+ i--;
+ }
+ tokbuf[i] = 0;
+ tval = tokbuf;
+
+ return (token);
+}
+
+static int
+read_num_or_name(int c, FILE *cfile)
+{
+ int i = 0;
+ int rv = NUMBER_OR_NAME;
+
+ tokbuf[i++] = c;
+ for (; i < sizeof(tokbuf); i++) {
+ c = get_char(cfile);
+ if (!isascii(c) || (c != '-' && c != '_' && !isalnum(c))) {
+ ungetc(c, cfile);
+ ugflag = 1;
+ break;
+ }
+ if (!isxdigit(c))
+ rv = NAME;
+ tokbuf[i] = c;
+ }
+ if (i == sizeof(tokbuf)) {
+ parse_warn("token larger than internal buffer");
+ i--;
+ }
+ tokbuf[i] = 0;
+ tval = tokbuf;
+
+ return (intern(tval, rv));
+}
+
+static int
+intern(char *atom, int dfv)
+{
+ if (!isascii(atom[0]))
+ return (dfv);
+
+ switch (tolower(atom[0])) {
+ case 'a':
+ if (!strcasecmp(atom + 1, "lways-reply-rfc1048"))
+ return (ALWAYS_REPLY_RFC1048);
+ if (!strcasecmp(atom + 1, "ppend"))
+ return (APPEND);
+ if (!strcasecmp(atom + 1, "llow"))
+ return (ALLOW);
+ if (!strcasecmp(atom + 1, "lias"))
+ return (ALIAS);
+ if (!strcasecmp(atom + 1, "bandoned"))
+ return (ABANDONED);
+ if (!strcasecmp(atom + 1, "uthoritative"))
+ return (AUTHORITATIVE);
+ break;
+ case 'b':
+ if (!strcasecmp(atom + 1, "ackoff-cutoff"))
+ return (BACKOFF_CUTOFF);
+ if (!strcasecmp(atom + 1, "ootp"))
+ return (BOOTP);
+ if (!strcasecmp(atom + 1, "ooting"))
+ return (BOOTING);
+ if (!strcasecmp(atom + 1, "oot-unknown-clients"))
+ return (BOOT_UNKNOWN_CLIENTS);
+ case 'c':
+ if (!strcasecmp(atom + 1, "lass"))
+ return (CLASS);
+ if (!strcasecmp(atom + 1, "iaddr"))
+ return (CIADDR);
+ if (!strcasecmp(atom + 1, "lient-identifier"))
+ return (CLIENT_IDENTIFIER);
+ if (!strcasecmp(atom + 1, "lient-hostname"))
+ return (CLIENT_HOSTNAME);
+ break;
+ case 'd':
+ if (!strcasecmp(atom + 1, "omain"))
+ return (DOMAIN);
+ if (!strcasecmp(atom + 1, "eny"))
+ return (DENY);
+ if (!strncasecmp(atom + 1, "efault", 6)) {
+ if (!atom[7])
+ return (DEFAULT);
+ if (!strcasecmp(atom + 7, "-lease-time"))
+ return (DEFAULT_LEASE_TIME);
+ break;
+ }
+ if (!strncasecmp(atom + 1, "ynamic-bootp", 12)) {
+ if (!atom[13])
+ return (DYNAMIC_BOOTP);
+ if (!strcasecmp(atom + 13, "-lease-cutoff"))
+ return (DYNAMIC_BOOTP_LEASE_CUTOFF);
+ if (!strcasecmp(atom + 13, "-lease-length"))
+ return (DYNAMIC_BOOTP_LEASE_LENGTH);
+ break;
+ }
+ break;
+ case 'e':
+ if (!strcasecmp(atom + 1, "thernet"))
+ return (ETHERNET);
+ if (!strcasecmp(atom + 1, "nds"))
+ return (ENDS);
+ if (!strcasecmp(atom + 1, "xpire"))
+ return (EXPIRE);
+ break;
+ case 'f':
+ if (!strcasecmp(atom + 1, "ilename"))
+ return (FILENAME);
+ if (!strcasecmp(atom + 1, "ixed-address"))
+ return (FIXED_ADDR);
+ if (!strcasecmp(atom + 1, "ddi"))
+ return (FDDI);
+ break;
+ case 'g':
+ if (!strcasecmp(atom + 1, "iaddr"))
+ return (GIADDR);
+ if (!strcasecmp(atom + 1, "roup"))
+ return (GROUP);
+ if (!strcasecmp(atom + 1, "et-lease-hostnames"))
+ return (GET_LEASE_HOSTNAMES);
+ break;
+ case 'h':
+ if (!strcasecmp(atom + 1, "ost"))
+ return (HOST);
+ if (!strcasecmp(atom + 1, "ardware"))
+ return (HARDWARE);
+ if (!strcasecmp(atom + 1, "ostname"))
+ return (HOSTNAME);
+ break;
+ case 'i':
+ if (!strcasecmp(atom + 1, "nitial-interval"))
+ return (INITIAL_INTERVAL);
+ if (!strcasecmp(atom + 1, "nterface"))
+ return (INTERFACE);
+ break;
+ case 'l':
+ if (!strcasecmp(atom + 1, "ease"))
+ return (LEASE);
+ break;
+ case 'm':
+ if (!strcasecmp(atom + 1, "ax-lease-time"))
+ return (MAX_LEASE_TIME);
+ if (!strncasecmp(atom + 1, "edi", 3)) {
+ if (!strcasecmp(atom + 4, "a"))
+ return (MEDIA);
+ if (!strcasecmp(atom + 4, "um"))
+ return (MEDIUM);
+ break;
+ }
+ break;
+ case 'n':
+ if (!strcasecmp(atom + 1, "ameserver"))
+ return (NAMESERVER);
+ if (!strcasecmp(atom + 1, "etmask"))
+ return (NETMASK);
+ if (!strcasecmp(atom + 1, "ext-server"))
+ return (NEXT_SERVER);
+ if (!strcasecmp(atom + 1, "ot"))
+ return (TOKEN_NOT);
+ break;
+ case 'o':
+ if (!strcasecmp(atom + 1, "ption"))
+ return (OPTION);
+ if (!strcasecmp(atom + 1, "ne-lease-per-client"))
+ return (ONE_LEASE_PER_CLIENT);
+ break;
+ case 'p':
+ if (!strcasecmp(atom + 1, "repend"))
+ return (PREPEND);
+ if (!strcasecmp(atom + 1, "acket"))
+ return (PACKET);
+ break;
+ case 'r':
+ if (!strcasecmp(atom + 1, "ange"))
+ return (RANGE);
+ if (!strcasecmp(atom + 1, "equest"))
+ return (REQUEST);
+ if (!strcasecmp(atom + 1, "equire"))
+ return (REQUIRE);
+ if (!strcasecmp(atom + 1, "etry"))
+ return (RETRY);
+ if (!strcasecmp(atom + 1, "enew"))
+ return (RENEW);
+ if (!strcasecmp(atom + 1, "ebind"))
+ return (REBIND);
+ if (!strcasecmp(atom + 1, "eboot"))
+ return (REBOOT);
+ if (!strcasecmp(atom + 1, "eject"))
+ return (REJECT);
+ break;
+ case 's':
+ if (!strcasecmp(atom + 1, "earch"))
+ return (SEARCH);
+ if (!strcasecmp(atom + 1, "tarts"))
+ return (STARTS);
+ if (!strcasecmp(atom + 1, "iaddr"))
+ return (SIADDR);
+ if (!strcasecmp(atom + 1, "ubnet"))
+ return (SUBNET);
+ if (!strcasecmp(atom + 1, "hared-network"))
+ return (SHARED_NETWORK);
+ if (!strcasecmp(atom + 1, "erver-name"))
+ return (SERVER_NAME);
+ if (!strcasecmp(atom + 1, "erver-identifier"))
+ return (SERVER_IDENTIFIER);
+ if (!strcasecmp(atom + 1, "elect-timeout"))
+ return (SELECT_TIMEOUT);
+ if (!strcasecmp(atom + 1, "end"))
+ return (SEND);
+ if (!strcasecmp(atom + 1, "cript"))
+ return (SCRIPT);
+ if (!strcasecmp(atom + 1, "upersede"))
+ return (SUPERSEDE);
+ break;
+ case 't':
+ if (!strcasecmp(atom + 1, "imestamp"))
+ return (TIMESTAMP);
+ if (!strcasecmp(atom + 1, "imeout"))
+ return (TIMEOUT);
+ if (!strcasecmp(atom + 1, "oken-ring"))
+ return (TOKEN_RING);
+ break;
+ case 'u':
+ if (!strncasecmp(atom + 1, "se", 2)) {
+ if (!strcasecmp(atom + 3, "r-class"))
+ return (USER_CLASS);
+ if (!strcasecmp(atom + 3, "-host-decl-names"))
+ return (USE_HOST_DECL_NAMES);
+ if (!strcasecmp(atom + 3,
+ "-lease-addr-for-default-route"))
+ return (USE_LEASE_ADDR_FOR_DEFAULT_ROUTE);
+ break;
+ }
+ if (!strcasecmp(atom + 1, "id"))
+ return (UID);
+ if (!strcasecmp(atom + 1, "nknown-clients"))
+ return (UNKNOWN_CLIENTS);
+ break;
+ case 'v':
+ if (!strcasecmp(atom + 1, "endor-class"))
+ return (VENDOR_CLASS);
+ break;
+ case 'y':
+ if (!strcasecmp(atom + 1, "iaddr"))
+ return (YIADDR);
+ break;
+ }
+ return (dfv);
+}
diff --git a/sbin/dhclient/convert.c b/sbin/dhclient/convert.c
new file mode 100644
index 0000000..adca092
--- /dev/null
+++ b/sbin/dhclient/convert.c
@@ -0,0 +1,117 @@
+/* $OpenBSD: convert.c,v 1.5 2004/02/07 11:35:59 henning Exp $ */
+
+/*
+ * Safe copying of option values into and out of the option buffer,
+ * which can't be assumed to be aligned.
+ */
+
+/*
+ * Copyright (c) 1995, 1996 The Internet Software Consortium.
+ * 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "dhcpd.h"
+
+u_int32_t
+getULong(unsigned char *buf)
+{
+ u_int32_t ibuf;
+
+ memcpy(&ibuf, buf, sizeof(ibuf));
+ return (ntohl(ibuf));
+}
+
+int32_t
+getLong(unsigned char *(buf))
+{
+ int32_t ibuf;
+
+ memcpy(&ibuf, buf, sizeof(ibuf));
+ return (ntohl(ibuf));
+}
+
+u_int16_t
+getUShort(unsigned char *buf)
+{
+ u_int16_t ibuf;
+
+ memcpy(&ibuf, buf, sizeof(ibuf));
+ return (ntohs(ibuf));
+}
+
+int16_t
+getShort(unsigned char *buf)
+{
+ int16_t ibuf;
+
+ memcpy(&ibuf, buf, sizeof(ibuf));
+ return (ntohs(ibuf));
+}
+
+void
+putULong(unsigned char *obuf, u_int32_t val)
+{
+ u_int32_t tmp = htonl(val);
+
+ memcpy(obuf, &tmp, sizeof(tmp));
+}
+
+void
+putLong(unsigned char *obuf, int32_t val)
+{
+ int32_t tmp = htonl(val);
+
+ memcpy(obuf, &tmp, sizeof(tmp));
+}
+
+void
+putUShort(unsigned char *obuf, unsigned int val)
+{
+ u_int16_t tmp = htons(val);
+
+ memcpy(obuf, &tmp, sizeof(tmp));
+}
+
+void
+putShort(unsigned char *obuf, int val)
+{
+ int16_t tmp = htons(val);
+
+ memcpy(obuf, &tmp, sizeof(tmp));
+}
diff --git a/sbin/dhclient/dhclient-script b/sbin/dhclient/dhclient-script
new file mode 100644
index 0000000..ced527d
--- /dev/null
+++ b/sbin/dhclient/dhclient-script
@@ -0,0 +1,295 @@
+#!/bin/sh
+#
+# $OpenBSD: dhclient-script,v 1.6 2004/05/06 18:22:41 claudio Exp $
+# $FreeBSD$
+#
+# Copyright (c) 2003 Kenneth R Westerback <krw@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, 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.
+#
+#
+
+ARP=/usr/sbin/arp
+AWK=/usr/bin/awk
+HOSTNAME=/bin/hostname
+NETSTAT=/usr/bin/netstat
+
+LOCALHOST=127.0.0.1
+
+if [ -x /usr/bin/logger ]; then
+ LOGGER="/usr/bin/logger -s -p user.notice -t dhclient"
+else
+ LOGGER=echo
+fi
+
+#
+# Helper functions that implement common actions.
+#
+
+check_hostname() {
+ current_hostname=`$HOSTNAME`
+ if [ -z "$current_hostname" ]; then
+ $LOGGER "New Hostname ($interface): $new_host_name"
+ $HOSTNAME $new_host_name
+ elif [ "$current_hostname" = "$old_host_name" -a \
+ "$new_host_name" != "$old_host_name" ]; then
+ $LOGGER "New Hostname ($interface): $new_host_name"
+ $HOSTNAME $new_host_name
+ fi
+}
+
+arp_flush() {
+ arp -an -i $interface | \
+ sed -n -e 's/^.*(\(.*\)) at .*$/arp -d \1/p' | \
+ sh >/dev/null 2>&1
+}
+
+delete_old_address() {
+ eval "ifconfig $interface inet -alias $old_ip_address $medium"
+}
+
+add_new_address() {
+ eval "ifconfig $interface \
+ inet $new_ip_address \
+ netmask $new_subnet_mask \
+ broadcast $new_broadcast_address \
+ $medium"
+
+ $LOGGER "New IP Address ($interface): $new_ip_address"
+ $LOGGER "New Subnet Mask ($interface): $new_subnet_mask"
+ $LOGGER "New Broadcast Address ($interface): $new_broadcast_address"
+ $LOGGER "New Routers ($interface): $new_routers"
+}
+
+delete_old_alias() {
+ if [ -n "$alias_ip_address" ]; then
+ ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1
+ #route delete $alias_ip_address $LOCALHOST > /dev/null 2>&1
+ fi
+}
+
+add_new_alias() {
+ if [ -n "$alias_ip_address" ]; then
+ ifconfig $interface inet alias $alias_ip_address netmask \
+ $alias_subnet_mask
+ #route add $alias_ip_address $LOCALHOST
+ fi
+}
+
+delete_old_routes() {
+ #route delete "$old_ip_address" $LOCALHOST >/dev/null 2>&1
+ for router in $old_routers; do
+ if [ $if_defaultroute = x -o $if_defaultroute = $interface ]; then
+ route delete default $route >/dev/null 2>&1
+ fi
+ done
+
+ if [ -n "$old_static_routes" ]; then
+ set $old_static_routes
+ while [ $# -gt 1 ]; do
+ route delete "$1" "$2"
+ shift; shift
+ done
+ fi
+
+ arp_flush
+}
+
+add_new_routes() {
+ #route add $new_ip_address $LOCALHOST >/dev/null 2>&1
+ for router in $new_routers; do
+ if [ "$new_ip_address" = "$router" ]; then
+ route add default -iface $router >/dev/null 2>&1
+ else
+ route add default $router >/dev/null 2>&1
+ fi
+ # 2nd and subsequent default routers error out, so explicitly
+ # stop processing the list after the first one.
+ break
+ done
+
+ if [ -n "$new_static_routes" ]; then
+ $LOGGER "New Static Routes ($interface): $new_static_routes"
+ set $new_static_routes
+ while [ $# -gt 1 ]; do
+ route add $1 $2
+ shift; shift
+ done
+ fi
+}
+
+add_new_resolv_conf() {
+ # XXX Old code did not create/update resolv.conf unless both
+ # $new_domain_name and $new_domain_name_servers were provided. PR
+ # #3135 reported some ISP's only provide $new_domain_name_servers and
+ # thus broke the script. This code creates the resolv.conf if either
+ # are provided.
+
+ local tmpres=/var/run/resolv.conf.${interface}
+ rm -f $tmpres
+
+ if [ -n "$new_domain_name" ]; then
+ echo "search $new_domain_name" >>$tmpres
+ fi
+
+ if [ -n "$new_domain_name_servers" ]; then
+ for nameserver in $new_domain_name_servers; do
+ echo "nameserver $nameserver" >>$tmpres
+ done
+ fi
+
+ if [ -f $tmpres ]; then
+ if [ -f /etc/resolv.conf.tail ]; then
+ cat /etc/resolv.conf.tail >>$tmpres
+ fi
+
+ # When resolv.conf is not changed actually, we don't
+ # need to update it.
+ # If /usr is not mounted yet, we cannot use cmp, then
+ # the following test fails. In such case, we simply
+ # ignore an error and do update resolv.conf.
+ if cmp -s $tmpres /etc/resolv.conf; then
+ rm -f $tmpres
+ return 0
+ fi 2>/dev/null
+
+ # In case (e.g. during OpenBSD installs) /etc/resolv.conf
+ # is a symbolic link, take care to preserve the link and write
+ # the new data in the correct location.
+
+ if [ -f /etc/resolv.conf ]; then
+ cat /etc/resolv.conf > /etc/resolv.conf.save
+ fi
+ cat $tmpres > /etc/resolv.conf
+ rm -f $tmpres
+
+ # Try to ensure correct ownership and permissions.
+ chown -RL root:wheel /etc/resolv.conf
+ chmod -RL 644 /etc/resolv.conf
+
+ return 0
+ fi
+
+ return 1
+}
+
+# Must be used on exit. Invokes the local dhcp client exit hooks, if any.
+exit_with_hooks() {
+ exit_status=$1
+ if [ -f /etc/dhclient-exit-hooks ]; then
+ . /etc/dhclient-exit-hooks
+ fi
+ # probably should do something with exit status of the local script
+ exit $exit_status
+}
+
+#
+# Start of active code.
+#
+
+# Invoke the local dhcp client enter hooks, if they exist.
+if [ -f /etc/dhclient-enter-hooks ]; then
+ exit_status=0
+ . /etc/dhclient-enter-hooks
+ # allow the local script to abort processing of this state
+ # local script must set exit_status variable to nonzero.
+ if [ $exit_status -ne 0 ]; then
+ exit $exit_status
+ fi
+fi
+
+if [ -x $NETSTAT ]; then
+ if_defaultroute=`$NETSTAT -rnf inet | $AWK '{if ($1=="default") printf $6}'`
+else
+ if_defaultroute="x"
+fi
+
+case $reason in
+MEDIUM)
+ eval "ifconfig $interface $medium"
+ eval "ifconfig $interface inet -alias 0.0.0.0 $medium" >/dev/null 2>&1
+ sleep 1
+ ;;
+
+PREINIT)
+ delete_old_alias
+ ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 broadcast 255.255.255.255 up
+ ;;
+
+ARPCHECK|ARPSEND)
+ ;;
+
+BOUND|RENEW|REBIND|REBOOT)
+ check_hostname
+ if [ -n "$old_ip_address" ]; then
+ if [ "$old_ip_address" != "$alias_ip_address" ]; then
+ delete_old_alias
+ fi
+ if [ "$old_ip_address" != "$new_ip_address" ]; then
+ delete_old_address
+ delete_old_routes
+ fi
+ fi
+ if [ "$reason" = BOUND ] || \
+ [ "$reason" = REBOOT ] || \
+ [ -z "$old_ip_address" ] || \
+ [ "$old_ip_address" != "$new_ip_address" ]; then
+ add_new_address
+ add_new_routes
+ fi
+ if [ "$new_ip_address" != "$alias_ip_address" ]; then
+ add_new_alias
+ fi
+ add_new_resolv_conf
+ ;;
+
+EXPIRE|FAIL)
+ delete_old_alias
+ if [ -n "$old_ip_address" ]; then
+ delete_old_address
+ delete_old_routes
+ fi
+ if [ -x $ARP ]; then
+ $ARP -d -a -i $interface
+ fi
+ # XXX Why add alias we just deleted above?
+ add_new_alias
+ if [ -f /etc/resolv.conf.save ]; then
+ cat /etc/resolv.conf.save > /etc/resolv.conf
+ fi
+ ;;
+
+TIMEOUT)
+ delete_old_alias
+ add_new_address
+ sleep 1
+ if [ -n "$new_routers" ]; then
+ $LOGGER "New Routers ($interface): $new_routers"
+ set "$new_routers"
+ if ping -q -c 1 -t 1 "$1"; then
+ if [ "$new_ip_address" != "$alias_ip_address" ]; then
+ add_new_alias
+ fi
+ add_new_routes
+ if add_new_resolv_conf; then
+ exit_with_hooks 0
+ fi
+ fi
+ fi
+ eval "ifconfig $interface inet -alias $new_ip_address $medium"
+ delete_old_routes
+ exit_with_hooks 1
+ ;;
+esac
+
+exit_with_hooks 0
diff --git a/sbin/dhclient/dhclient-script.8 b/sbin/dhclient/dhclient-script.8
new file mode 100644
index 0000000..b04974b
--- /dev/null
+++ b/sbin/dhclient/dhclient-script.8
@@ -0,0 +1,273 @@
+.\" $OpenBSD: dhclient-script.8,v 1.2 2004/04/09 18:30:15 jmc Exp $
+.\"
+.\" Copyright (c) 1997 The Internet Software Consortium.
+.\" 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+.\" by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+.\" Enterprises. To learn more about the Internet Software Consortium,
+.\" see ``http://www.isc.org/isc''. To learn more about Vixie
+.\" Enterprises, see ``http://www.vix.com''.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 1, 1997
+.Dt DHCLIENT-SCRIPT 8
+.Os
+.Sh NAME
+.Nm dhclient-script
+.Nd DHCP client network configuration script
+.Sh DESCRIPTION
+The DHCP client network configuration script is invoked from time to
+time by
+.Xr dhclient 8 .
+This script is used by the DHCP client to set each interface's initial
+configuration prior to requesting an address, to test the address once it
+has been offered, and to set the interface's final configuration once a
+lease has been acquired.
+If no lease is acquired, the script is used to test predefined leases, if
+any, and also called once if no valid lease can be identified.
+.Pp
+.\" No standard client script exists for some operating systems, even though
+.\" the actual client may work, so a pioneering user may well need to create
+.\" a new script or modify an existing one.
+In general, customizations specific to a particular computer should be done
+in the
+.Pa /etc/dhclient.conf
+file.
+.Sh OPERATION
+When
+.Xr dhclient 8
+needs to invoke the client configuration script, it sets up a number of
+environment variables and runs
+.Nm .
+In all cases,
+.Va $reason
+is set to the name of the reason why the script has been invoked.
+The following reasons are currently defined:
+.Li MEDIUM , PREINIT , ARPCHECK , ARPSEND , BOUND , RENEW , REBIND , REBOOT ,
+.Li EXPIRE , FAIL
+and
+.Li TIMEOUT .
+.Bl -tag -width ".Li ARPCHECK"
+.It Li MEDIUM
+The DHCP client is requesting that an interface's media type be set.
+The interface name is passed in
+.Va $interface ,
+and the media type is passed in
+.Va $medium .
+.It Li PREINIT
+The DHCP client is requesting that an interface be configured as
+required in order to send packets prior to receiving an actual address.
+.\" For clients which use the BSD socket library,
+This means configuring the interface with an IP address of 0.0.0.0
+and a broadcast address of 255.255.255.255.
+.\" For other clients, it may be possible to simply configure the interface up
+.\" without actually giving it an IP address at all.
+The interface name is passed in
+.Va $interface ,
+and the media type in
+.Va $medium .
+.Pp
+If an IP alias has been declared in
+.Xr dhclient.conf 5 ,
+its address will be passed in
+.Va $alias_ip_address ,
+and that IP alias should be deleted from the interface,
+along with any routes to it.
+.It Li ARPSEND
+The DHCP client is requesting that an address that has been offered to
+it be checked to see if somebody else is using it, by sending an ARP
+request for that address.
+It is not clear how to implement this, so no examples exist yet.
+The IP address to check is passed in
+.Va $new_ip_address ,
+and the interface name is passed in
+.Va $interface .
+.It Li ARPCHECK
+The DHCP client wants to know if a response to the ARP request sent
+using
+.Li ARPSEND
+has been received.
+If one has, the script should exit with a nonzero status, indicating that
+the offered address has already been requested and should be declined.
+The
+.Va $new_ip_address
+and
+.Va $interface
+variables are set as with
+.Li ARPSEND .
+.It Li BOUND
+The DHCP client has done an initial binding to a new address.
+The new IP address is passed in
+.Va $new_ip_address ,
+and the interface name is passed in
+.Va $interface .
+The media type is passed in
+.Va $medium .
+Any options acquired from the server are passed using the option name
+described in
+.Xr dhcp-options 5 ,
+except that dashes
+.Pq Ql -
+are replaced by underscores
+.Pq Ql _
+in order to make valid shell variables, and the variable names start with
+.Dq Li new_ .
+So for example, the new subnet mask would be passed in
+.Va $new_subnet_mask .
+.Pp
+When a binding has been completed, a lot of network parameters are
+likely to need to be set up.
+A new
+.Pa /etc/resolv.conf
+needs to be created, using the values of
+.Va $new_domain_name
+and
+.Va $new_domain_name_servers
+(which may list more than one server, separated by spaces).
+A default route should be set using
+.Va $new_routers ,
+and static routes may need to be set up using
+.Va $new_static_routes .
+.Pp
+If an IP alias has been declared, it must be set up here.
+The alias IP address will be written as
+.Va $alias_ip_address ,
+and other DHCP options that are set for the alias (e.g., subnet mask)
+will be passed in variables named as described previously except starting with
+.Dq Li $alias_
+instead of
+.Dq Li $new_ .
+Care should be taken that the alias IP address not be used if it is identical
+to the bound IP address
+.Pq Va $new_ip_address ,
+since the other alias parameters may be incorrect in this case.
+.It Li RENEW
+When a binding has been renewed, the script is called as in
+.Li BOUND ,
+except that in addition to all the variables starting with
+.Dq Li $new_ ,
+there is another set of variables starting with
+.Dq Li $old_ .
+Persistent settings that may have changed need to be deleted - for example,
+if a local route to the bound address is being configured, the old local
+route should be deleted.
+If the default route has changed, the old default route should be deleted.
+If the static routes have changed, the old ones should be deleted.
+Otherwise, processing can be done as with
+.Li BOUND .
+.It Li REBIND
+The DHCP client has rebound to a new DHCP server.
+This can be handled as with
+.Li RENEW ,
+except that if the IP address has changed,
+the ARP table should be cleared.
+.It Li REBOOT
+The DHCP client has successfully reacquired its old address after a reboot.
+This can be processed as with
+.Li BOUND .
+.It Li EXPIRE
+The DHCP client has failed to renew its lease or acquire a new one,
+and the lease has expired.
+The IP address must be relinquished, and all related parameters should be
+deleted, as in
+.Li RENEW
+and
+.Li REBIND .
+.It Li FAIL
+The DHCP client has been unable to contact any DHCP servers, and any
+leases that have been tested have not proved to be valid.
+The parameters from the last lease tested should be deconfigured.
+This can be handled in the same way as
+.Li EXPIRE .
+.It Li TIMEOUT
+The DHCP client has been unable to contact any DHCP servers.
+However, an old lease has been identified, and its parameters have
+been passed in as with
+.Li BOUND .
+The client configuration script should test these parameters and,
+if it has reason to believe they are valid, should exit with a value of zero.
+If not, it should exit with a nonzero value.
+.El
+.Pp
+The usual way to test a lease is to set up the network as with
+.Li REBIND
+(since this may be called to test more than one lease) and then ping
+the first router defined in
+.Va $routers .
+If a response is received, the lease must be valid for the network to
+which the interface is currently connected.
+It would be more complete to try to ping all of the routers listed in
+.Va $new_routers ,
+as well as those listed in
+.Va $new_static_routes ,
+but current scripts do not do this.
+.\" .Sh FILES
+.\" Each operating system should generally have its own script file,
+.\" although the script files for similar operating systems may be similar
+.\" or even identical.
+.\" The script files included in the Internet Software Consortium DHCP
+.\" distribution appear in the distribution tree under client/scripts,
+.\" and bear the names of the operating systems on which they are intended
+.\" to work.
+.Sh SEE ALSO
+.Xr dhclient.conf 5 ,
+.Xr dhclient.leases 5 ,
+.Xr dhclient 8 ,
+.Xr dhcpd 8 ,
+.Xr dhcrelay 8
+.Sh AUTHORS
+.An -nosplit
+The original version of
+.Nm
+was written for the Internet Software Consortium by
+.An Ted Lemon Aq mellon@fugue.com
+in cooperation with Vixie Enterprises.
+.Pp
+The
+.Ox
+implementation of
+.Nm
+was written by
+.An Kenneth R. Westerback Aq krw@openbsd.org .
+.Sh BUGS
+If more than one interface is being used, there is no obvious way to
+avoid clashes between server-supplied configuration parameters - for
+example, the stock
+.Nm
+rewrites
+.Pa /etc/resolv.conf .
+If more than one interface is being configured,
+.Pa /etc/resolv.conf
+will be repeatedly initialized to the values provided by one server, and then
+the other.
+Assuming the information provided by both servers is valid, this should not
+cause any real problems, but it could be confusing.
diff --git a/sbin/dhclient/dhclient.8 b/sbin/dhclient/dhclient.8
new file mode 100644
index 0000000..281159f
--- /dev/null
+++ b/sbin/dhclient/dhclient.8
@@ -0,0 +1,194 @@
+.\" $OpenBSD: dhclient.8,v 1.3 2004/04/09 18:30:15 jmc Exp $
+.\"
+.\" Copyright (c) 1997 The Internet Software Consortium.
+.\" 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+.\" by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+.\" Enterprises. To learn more about the Internet Software Consortium,
+.\" see ``http://www.isc.org/isc''. To learn more about Vixie
+.\" Enterprises, see ``http://www.vix.com''.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 22, 2005
+.Dt DHCLIENT 8
+.Os
+.Sh NAME
+.Nm dhclient
+.Nd "Dynamic Host Configuration Protocol (DHCP) client"
+.Sh SYNOPSIS
+.Nm
+.Op Fl bdqu
+.Op Fl c Ar file
+.Op Fl l Ar file
+.Ar interface
+.Sh DESCRIPTION
+The
+.Nm
+utility provides a means for configuring network interfaces using DHCP, BOOTP,
+or if these protocols fail, by statically assigning an address.
+.Pp
+The name of the network interface that
+.Nm
+should attempt to
+configure must be specified on the command line.
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl c Ar file"
+.It Fl b
+Forces
+.Nm
+to immediately move to the background.
+.It Fl c Ar file
+Specify an alternate location,
+.Ar file ,
+for the configuration file.
+.It Fl d
+Forces
+.Nm
+to always run as a foreground process.
+By default,
+.Nm
+runs in the foreground until it has configured the interface, and then
+will revert to running in the background.
+.It Fl l Ar file
+Specify an alternate location,
+.Ar file ,
+for the leases file.
+.It Fl q
+Forces
+.Nm
+to be less verbose on startup.
+.It Fl u
+Forces
+.Nm
+to reject leases with unknown options in them.
+The default behaviour is to accept such lease offers.
+.El
+.Pp
+The DHCP protocol allows a host to contact a central server which
+maintains a list of IP addresses which may be assigned on one or more
+subnets.
+A DHCP client may request an address from this pool, and
+then use it on a temporary basis for communication on the network.
+The DHCP protocol also provides a mechanism whereby a client can learn
+important details about the network to which it is attached, such as
+the location of a default router, the location of a name server, and
+so on.
+.Pp
+On startup,
+.Nm
+reads
+.Pa /etc/dhclient.conf
+for configuration instructions.
+It then gets a list of all the
+network interfaces that are configured in the current system.
+It then attempts to configure each interface with DHCP.
+.Pp
+In order to keep track of leases across system reboots and server
+restarts,
+.Nm
+keeps a list of leases it has been assigned in the
+.Pa /var/db/dhclient.leases. Ns Ar IFNAME
+file.
+.Ar IFNAME
+represents the network interface of the DHCP client
+(e.g.,
+.Li em0 ) ,
+one for each interface.
+On startup, after reading the
+.Xr dhclient.conf 5
+file,
+.Nm
+reads the leases file to refresh its memory about what leases it has been
+assigned.
+.Pp
+Old leases are kept around in case the DHCP server is unavailable when
+.Nm
+is first invoked (generally during the initial system boot
+process).
+In that event, old leases from the
+.Pa dhclient.leases. Ns Ar IFNAME
+file which have not yet expired are tested, and if they are determined to
+be valid, they are used until either they expire or the DHCP server
+becomes available.
+.Pp
+A mobile host which may sometimes need to access a network on which no
+DHCP server exists may be preloaded with a lease for a fixed
+address on that network.
+When all attempts to contact a DHCP server have failed,
+.Nm
+will try to validate the static lease, and if it
+succeeds, it will use that lease until it is restarted.
+.Pp
+A mobile host may also travel to some networks on which DHCP is not
+available but BOOTP is.
+In that case, it may be advantageous to
+arrange with the network administrator for an entry on the BOOTP
+database, so that the host can boot quickly on that network rather
+than cycling through the list of old leases.
+.Sh NOTES
+You must have the Berkeley Packet Filter (BPF) configured in your kernel.
+The
+.Nm
+utility
+requires at least one
+.Pa /dev/bpf*
+device for each broadcast network interface that is attached to your system.
+See
+.Xr bpf 4
+for more information.
+.Sh FILES
+.Bl -tag -width ".Pa /var/db/dhclient.leases. Ns Ar IFNAME" -compact
+.It Pa /etc/dhclient.conf
+DHCP client configuration file
+.It Pa /var/db/dhclient.leases. Ns Ar IFNAME
+database of acquired leases
+.El
+.Sh SEE ALSO
+.Xr dhclient.conf 5 ,
+.Xr dhclient.leases 5 ,
+.Xr dhclient-script 8 ,
+.Xr dhcp 8 ,
+.Xr dhcpd 8 ,
+.Xr dhcrelay 8
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility
+was written by
+.An Ted Lemon Aq mellon@fugue.com
+and
+.An Elliot Poger Aq elliot@poger.com .
+.Pp
+The current implementation was reworked by
+.An Henning Brauer Aq henning@openbsd.org .
diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c
new file mode 100644
index 0000000..40e68af
--- /dev/null
+++ b/sbin/dhclient/dhclient.c
@@ -0,0 +1,2486 @@
+/* $OpenBSD: dhclient.c,v 1.63 2005/02/06 17:10:13 krw Exp $ */
+
+/*
+ * Copyright 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999
+ * The Internet Software Consortium. 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ *
+ * This client was substantially modified and enhanced by Elliot Poger
+ * for use on Linux while he was working on the MosquitoNet project at
+ * Stanford.
+ *
+ * The current version owes much to Elliot's Linux enhancements, but
+ * was substantially reorganized and partially rewritten by Ted Lemon
+ * so as to use the same networking framework that the Internet Software
+ * Consortium DHCP server uses. Much system-specific configuration code
+ * was moved into a shell script so that as support for more operating
+ * systems is added, it will not be necessary to port and maintain
+ * system-specific configuration code to these operating systems - instead,
+ * the shell script can invoke the native tools to accomplish the same
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "dhcpd.h"
+#include "privsep.h"
+
+#include <net80211/ieee80211_freebsd.h>
+
+#ifndef _PATH_VAREMPTY
+#define _PATH_VAREMPTY "/var/empty"
+#endif
+
+#define PERIOD 0x2e
+#define hyphenchar(c) ((c) == 0x2d)
+#define bslashchar(c) ((c) == 0x5c)
+#define periodchar(c) ((c) == PERIOD)
+#define asterchar(c) ((c) == 0x2a)
+#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) || \
+ ((c) >= 0x61 && (c) <= 0x7a))
+#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39)
+#define whitechar(c) ((c) == ' ' || (c) == '\t')
+
+#define borderchar(c) (alphachar(c) || digitchar(c))
+#define middlechar(c) (borderchar(c) || hyphenchar(c))
+#define domainchar(c) ((c) > 0x20 && (c) < 0x7f)
+
+#define CLIENT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
+
+time_t cur_time;
+time_t default_lease_time = 43200; /* 12 hours... */
+
+char *path_dhclient_conf = _PATH_DHCLIENT_CONF;
+char *path_dhclient_db = NULL;
+
+int log_perror = 1;
+int privfd;
+int nullfd = -1;
+
+struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } };
+struct in_addr inaddr_any;
+struct sockaddr_in sockaddr_broadcast;
+
+/*
+ * ASSERT_STATE() does nothing now; it used to be
+ * assert (state_is == state_shouldbe).
+ */
+#define ASSERT_STATE(state_is, state_shouldbe) {}
+
+#define TIME_MAX 2147483647
+
+int log_priority;
+int no_daemon;
+int unknown_ok = 1;
+int routefd;
+
+struct interface_info *ifi;
+
+int findproto(char *, int);
+struct sockaddr *get_ifa(char *, int);
+void routehandler(struct protocol *);
+void usage(void);
+int check_option(struct client_lease *l, int option);
+int ipv4addrs(char * buf);
+int res_hnok(const char *dn);
+int check_search(const char *srch);
+char *option_as_string(unsigned int code, unsigned char *data, int len);
+int fork_privchld(int, int);
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+time_t scripttime;
+
+int
+findproto(char *cp, int n)
+{
+ struct sockaddr *sa;
+ int i;
+
+ if (n == 0)
+ return -1;
+ for (i = 1; i; i <<= 1) {
+ if (i & n) {
+ sa = (struct sockaddr *)cp;
+ switch (i) {
+ case RTA_IFA:
+ case RTA_DST:
+ case RTA_GATEWAY:
+ case RTA_NETMASK:
+ if (sa->sa_family == AF_INET)
+ return AF_INET;
+ if (sa->sa_family == AF_INET6)
+ return AF_INET6;
+ break;
+ case RTA_IFP:
+ break;
+ }
+ ADVANCE(cp, sa);
+ }
+ }
+ return (-1);
+}
+
+struct sockaddr *
+get_ifa(char *cp, int n)
+{
+ struct sockaddr *sa;
+ int i;
+
+ if (n == 0)
+ return (NULL);
+ for (i = 1; i; i <<= 1)
+ if (i & n) {
+ sa = (struct sockaddr *)cp;
+ if (i == RTA_IFA)
+ return (sa);
+ ADVANCE(cp, sa);
+ }
+
+ return (NULL);
+}
+struct iaddr defaddr = { 4 };
+
+/* ARGSUSED */
+void
+routehandler(struct protocol *p)
+{
+ char msg[2048];
+ struct rt_msghdr *rtm;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ struct if_announcemsghdr *ifan;
+ struct client_lease *l;
+ time_t t = time(NULL);
+ struct sockaddr *sa;
+ struct iaddr a;
+ ssize_t n;
+
+ n = read(routefd, &msg, sizeof(msg));
+ rtm = (struct rt_msghdr *)msg;
+ if (n < sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen ||
+ rtm->rtm_version != RTM_VERSION)
+ return;
+
+ switch (rtm->rtm_type) {
+ case RTM_NEWADDR:
+ /*
+ * XXX: If someone other than us adds our address,
+ * we should assume they are taking over from us,
+ * delete the lease record, and exit without modifying
+ * the interface.
+ */
+ break;
+ case RTM_DELADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+
+ if (ifam->ifam_index != ifi->index)
+ break;
+ if (findproto((char *)(ifam + 1), ifam->ifam_addrs) != AF_INET)
+ break;
+ if (scripttime == 0 || t < scripttime + 10)
+ break;
+
+ sa = get_ifa((char *)(ifam + 1), ifam->ifam_addrs);
+ if (sa == NULL)
+ goto die;
+
+ if ((a.len = sizeof(struct in_addr)) > sizeof(a.iabuf))
+ error("king bula sez: len mismatch");
+ memcpy(a.iabuf, &((struct sockaddr_in *)sa)->sin_addr, a.len);
+ if (addr_eq(a, defaddr))
+ break;
+
+ for (l = ifi->client->active; l != NULL; l = l->next)
+ if (addr_eq(a, l->address))
+ break;
+
+ if (l == NULL) /* deleted addr is not the one we set */
+ break;
+ goto die;
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *)rtm;
+ if (ifm->ifm_index != ifi->index)
+ break;
+ if ((rtm->rtm_flags & RTF_UP) == 0)
+ goto die;
+ break;
+ case RTM_IFANNOUNCE:
+ ifan = (struct if_announcemsghdr *)rtm;
+ if (ifan->ifan_what == IFAN_DEPARTURE &&
+ ifan->ifan_index == ifi->index)
+ goto die;
+ break;
+ case RTM_IEEE80211:
+ ifan = (struct if_announcemsghdr *)rtm;
+ if (ifan->ifan_index != ifi->index)
+ break;
+ switch (ifan->ifan_what) {
+ case RTM_IEEE80211_ASSOC:
+ case RTM_IEEE80211_REASSOC:
+ state_reboot(ifi);
+ break;
+ case RTM_IEEE80211_DISASSOC:
+ /*
+ * Clear existing state; transition to the init
+ * state and then wait for either a link down
+ * notification or an associate event.
+ */
+ if (ifi->client->active != NULL) {
+ script_init("EXPIRE", NULL);
+ script_write_params("old_",
+ ifi->client->active);
+ if (ifi->client->alias)
+ script_write_params("alias_",
+ ifi->client->alias);
+ script_go();
+ }
+ ifi->client->state = S_INIT;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return;
+
+die:
+ script_init("FAIL", NULL);
+ if (ifi->client->alias)
+ script_write_params("alias_", ifi->client->alias);
+ script_go();
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ extern char *__progname;
+ int ch, fd, quiet = 0, i = 0;
+ int pipe_fd[2];
+ int immediate_daemon = 0;
+ struct passwd *pw;
+
+ /* Initially, log errors to stderr as well as to syslogd. */
+ openlog(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY);
+ setlogmask(LOG_UPTO(LOG_INFO));
+
+ while ((ch = getopt(argc, argv, "bc:dl:nqu")) != -1)
+ switch (ch) {
+ case 'b':
+ immediate_daemon = 1;
+ break;
+ case 'c':
+ path_dhclient_conf = optarg;
+ break;
+ case 'd':
+ no_daemon = 1;
+ break;
+ case 'l':
+ path_dhclient_db = optarg;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'u':
+ unknown_ok = 0;
+ break;
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ if ((ifi = calloc(1, sizeof(struct interface_info))) == NULL)
+ error("calloc");
+ if (strlcpy(ifi->name, argv[0], IFNAMSIZ) >= IFNAMSIZ)
+ error("Interface name too long");
+ if (path_dhclient_db == NULL && asprintf(&path_dhclient_db, "%s.%s",
+ _PATH_DHCLIENT_DB, ifi->name) == -1)
+ error("asprintf");
+
+ if (quiet)
+ log_perror = 0;
+
+ tzset();
+ time(&cur_time);
+
+ memset(&sockaddr_broadcast, 0, sizeof(sockaddr_broadcast));
+ sockaddr_broadcast.sin_family = AF_INET;
+ sockaddr_broadcast.sin_port = htons(REMOTE_PORT);
+ sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST;
+ sockaddr_broadcast.sin_len = sizeof(sockaddr_broadcast);
+ inaddr_any.s_addr = INADDR_ANY;
+
+ read_client_conf();
+
+ if (!interface_link_status(ifi->name)) {
+ fprintf(stderr, "%s: no link ...", ifi->name);
+ fflush(stderr);
+ sleep(1);
+ while (!interface_link_status(ifi->name)) {
+ fprintf(stderr, ".");
+ fflush(stderr);
+ if (++i > 10) {
+ fprintf(stderr, " giving up\n");
+ exit(1);
+ }
+ sleep(1);
+ }
+ fprintf(stderr, " got link\n");
+ }
+
+ if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1)
+ error("cannot open %s: %m", _PATH_DEVNULL);
+
+ if ((pw = getpwnam("_dhcp")) == NULL) {
+ warning("no such user: _dhcp, falling back to \"nobody\"");
+ if ((pw = getpwnam("nobody")) == NULL)
+ error("no such user: nobody");
+ }
+
+ if (pipe(pipe_fd) == -1)
+ error("pipe");
+
+ fork_privchld(pipe_fd[0], pipe_fd[1]);
+
+ close(pipe_fd[0]);
+ privfd = pipe_fd[1];
+
+ if ((fd = open(path_dhclient_db, O_RDONLY|O_EXLOCK|O_CREAT, 0)) == -1)
+ error("can't open and lock %s: %m", path_dhclient_db);
+ read_client_leases();
+ rewrite_client_leases();
+ close(fd);
+
+ priv_script_init("PREINIT", NULL);
+ if (ifi->client->alias)
+ priv_script_write_params("alias_", ifi->client->alias);
+ priv_script_go();
+
+ if ((routefd = socket(PF_ROUTE, SOCK_RAW, 0)) != -1)
+ add_protocol("AF_ROUTE", routefd, routehandler, ifi);
+
+ /* set up the interface */
+ discover_interfaces(ifi);
+
+ if (chroot(_PATH_VAREMPTY) == -1)
+ error("chroot");
+ if (chdir("/") == -1)
+ error("chdir(\"/\")");
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setegid(pw->pw_gid) || setgid(pw->pw_gid) ||
+ seteuid(pw->pw_uid) || setuid(pw->pw_uid))
+ error("can't drop privileges: %m");
+
+ endpwent();
+
+ setproctitle("%s", ifi->name);
+
+ if (immediate_daemon)
+ go_daemon();
+
+ ifi->client->state = S_INIT;
+ state_reboot(ifi);
+
+ bootp_packet_handler = do_packet;
+
+ dispatch();
+
+ /* not reached */
+ return (0);
+}
+
+void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-dqu] ", __progname);
+ fprintf(stderr, "[-c conffile] [-l leasefile] interface\n");
+ exit(1);
+}
+
+/*
+ * Individual States:
+ *
+ * Each routine is called from the dhclient_state_machine() in one of
+ * these conditions:
+ * -> entering INIT state
+ * -> recvpacket_flag == 0: timeout in this state
+ * -> otherwise: received a packet in this state
+ *
+ * Return conditions as handled by dhclient_state_machine():
+ * Returns 1, sendpacket_flag = 1: send packet, reset timer.
+ * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone).
+ * Returns 0: finish the nap which was interrupted for no good reason.
+ *
+ * Several per-interface variables are used to keep track of the process:
+ * active_lease: the lease that is being used on the interface
+ * (null pointer if not configured yet).
+ * offered_leases: leases corresponding to DHCPOFFER messages that have
+ * been sent to us by DHCP servers.
+ * acked_leases: leases corresponding to DHCPACK messages that have been
+ * sent to us by DHCP servers.
+ * sendpacket: DHCP packet we're trying to send.
+ * destination: IP address to send sendpacket to
+ * In addition, there are several relevant per-lease variables.
+ * T1_expiry, T2_expiry, lease_expiry: lease milestones
+ * In the active lease, these control the process of renewing the lease;
+ * In leases on the acked_leases list, this simply determines when we
+ * can no longer legitimately use the lease.
+ */
+
+void
+state_reboot(void *ipp)
+{
+ struct interface_info *ip = ipp;
+
+ /* If we don't remember an active lease, go straight to INIT. */
+ if (!ip->client->active || ip->client->active->is_bootp) {
+ state_init(ip);
+ return;
+ }
+
+ /* We are in the rebooting state. */
+ ip->client->state = S_REBOOTING;
+
+ /* make_request doesn't initialize xid because it normally comes
+ from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER,
+ so pick an xid now. */
+ ip->client->xid = arc4random();
+
+ /* Make a DHCPREQUEST packet, and set appropriate per-interface
+ flags. */
+ make_request(ip, ip->client->active);
+ ip->client->destination = iaddr_broadcast;
+ ip->client->first_sending = cur_time;
+ ip->client->interval = ip->client->config->initial_interval;
+
+ /* Zap the medium list... */
+ ip->client->medium = NULL;
+
+ /* Send out the first DHCPREQUEST packet. */
+ send_request(ip);
+}
+
+/*
+ * Called when a lease has completely expired and we've
+ * been unable to renew it.
+ */
+void
+state_init(void *ipp)
+{
+ struct interface_info *ip = ipp;
+
+ ASSERT_STATE(state, S_INIT);
+
+ /* Make a DHCPDISCOVER packet, and set appropriate per-interface
+ flags. */
+ make_discover(ip, ip->client->active);
+ ip->client->xid = ip->client->packet.xid;
+ ip->client->destination = iaddr_broadcast;
+ ip->client->state = S_SELECTING;
+ ip->client->first_sending = cur_time;
+ ip->client->interval = ip->client->config->initial_interval;
+
+ /* Add an immediate timeout to cause the first DHCPDISCOVER packet
+ to go out. */
+ send_discover(ip);
+}
+
+/*
+ * state_selecting is called when one or more DHCPOFFER packets
+ * have been received and a configurable period of time has passed.
+ */
+void
+state_selecting(void *ipp)
+{
+ struct interface_info *ip = ipp;
+ struct client_lease *lp, *next, *picked;
+
+ ASSERT_STATE(state, S_SELECTING);
+
+ /* Cancel state_selecting and send_discover timeouts, since either
+ one could have got us here. */
+ cancel_timeout(state_selecting, ip);
+ cancel_timeout(send_discover, ip);
+
+ /* We have received one or more DHCPOFFER packets. Currently,
+ the only criterion by which we judge leases is whether or
+ not we get a response when we arp for them. */
+ picked = NULL;
+ for (lp = ip->client->offered_leases; lp; lp = next) {
+ next = lp->next;
+
+ /* Check to see if we got an ARPREPLY for the address
+ in this particular lease. */
+ if (!picked) {
+ script_init("ARPCHECK", lp->medium);
+ script_write_params("check_", lp);
+
+ /* If the ARPCHECK code detects another
+ machine using the offered address, it exits
+ nonzero. We need to send a DHCPDECLINE and
+ toss the lease. */
+ if (script_go()) {
+ make_decline(ip, lp);
+ send_decline(ip);
+ goto freeit;
+ }
+ picked = lp;
+ picked->next = NULL;
+ } else {
+freeit:
+ free_client_lease(lp);
+ }
+ }
+ ip->client->offered_leases = NULL;
+
+ /* If we just tossed all the leases we were offered, go back
+ to square one. */
+ if (!picked) {
+ ip->client->state = S_INIT;
+ state_init(ip);
+ return;
+ }
+
+ /* If it was a BOOTREPLY, we can just take the address right now. */
+ if (!picked->options[DHO_DHCP_MESSAGE_TYPE].len) {
+ ip->client->new = picked;
+
+ /* Make up some lease expiry times
+ XXX these should be configurable. */
+ ip->client->new->expiry = cur_time + 12000;
+ ip->client->new->renewal += cur_time + 8000;
+ ip->client->new->rebind += cur_time + 10000;
+
+ ip->client->state = S_REQUESTING;
+
+ /* Bind to the address we received. */
+ bind_lease(ip);
+ return;
+ }
+
+ /* Go to the REQUESTING state. */
+ ip->client->destination = iaddr_broadcast;
+ ip->client->state = S_REQUESTING;
+ ip->client->first_sending = cur_time;
+ ip->client->interval = ip->client->config->initial_interval;
+
+ /* Make a DHCPREQUEST packet from the lease we picked. */
+ make_request(ip, picked);
+ ip->client->xid = ip->client->packet.xid;
+
+ /* Toss the lease we picked - we'll get it back in a DHCPACK. */
+ free_client_lease(picked);
+
+ /* Add an immediate timeout to send the first DHCPREQUEST packet. */
+ send_request(ip);
+}
+
+/* state_requesting is called when we receive a DHCPACK message after
+ having sent out one or more DHCPREQUEST packets. */
+
+void
+dhcpack(struct packet *packet)
+{
+ struct interface_info *ip = packet->interface;
+ struct client_lease *lease;
+
+ /* If we're not receptive to an offer right now, or if the offer
+ has an unrecognizable transaction id, then just drop it. */
+ if (packet->interface->client->xid != packet->raw->xid ||
+ (packet->interface->hw_address.hlen != packet->raw->hlen) ||
+ (memcmp(packet->interface->hw_address.haddr,
+ packet->raw->chaddr, packet->raw->hlen)))
+ return;
+
+ if (ip->client->state != S_REBOOTING &&
+ ip->client->state != S_REQUESTING &&
+ ip->client->state != S_RENEWING &&
+ ip->client->state != S_REBINDING)
+ return;
+
+ note("DHCPACK from %s", piaddr(packet->client_addr));
+
+ lease = packet_to_lease(packet);
+ if (!lease) {
+ note("packet_to_lease failed.");
+ return;
+ }
+
+ ip->client->new = lease;
+
+ /* Stop resending DHCPREQUEST. */
+ cancel_timeout(send_request, ip);
+
+ /* Figure out the lease time. */
+ if (ip->client->new->options[DHO_DHCP_LEASE_TIME].data)
+ ip->client->new->expiry = getULong(
+ ip->client->new->options[DHO_DHCP_LEASE_TIME].data);
+ else
+ ip->client->new->expiry = default_lease_time;
+ /* A number that looks negative here is really just very large,
+ because the lease expiry offset is unsigned. */
+ if (ip->client->new->expiry < 0)
+ ip->client->new->expiry = TIME_MAX;
+ /* XXX should be fixed by resetting the client state */
+ if (ip->client->new->expiry < 60)
+ ip->client->new->expiry = 60;
+
+ /* Take the server-provided renewal time if there is one;
+ otherwise figure it out according to the spec. */
+ if (ip->client->new->options[DHO_DHCP_RENEWAL_TIME].len)
+ ip->client->new->renewal = getULong(
+ ip->client->new->options[DHO_DHCP_RENEWAL_TIME].data);
+ else
+ ip->client->new->renewal = ip->client->new->expiry / 2;
+
+ /* Same deal with the rebind time. */
+ if (ip->client->new->options[DHO_DHCP_REBINDING_TIME].len)
+ ip->client->new->rebind = getULong(
+ ip->client->new->options[DHO_DHCP_REBINDING_TIME].data);
+ else
+ ip->client->new->rebind = ip->client->new->renewal +
+ ip->client->new->renewal / 2 + ip->client->new->renewal / 4;
+
+ ip->client->new->expiry += cur_time;
+ /* Lease lengths can never be negative. */
+ if (ip->client->new->expiry < cur_time)
+ ip->client->new->expiry = TIME_MAX;
+ ip->client->new->renewal += cur_time;
+ if (ip->client->new->renewal < cur_time)
+ ip->client->new->renewal = TIME_MAX;
+ ip->client->new->rebind += cur_time;
+ if (ip->client->new->rebind < cur_time)
+ ip->client->new->rebind = TIME_MAX;
+
+ bind_lease(ip);
+}
+
+void
+bind_lease(struct interface_info *ip)
+{
+ /* Remember the medium. */
+ ip->client->new->medium = ip->client->medium;
+
+ /* Write out the new lease. */
+ write_client_lease(ip, ip->client->new, 0);
+
+ /* Run the client script with the new parameters. */
+ script_init((ip->client->state == S_REQUESTING ? "BOUND" :
+ (ip->client->state == S_RENEWING ? "RENEW" :
+ (ip->client->state == S_REBOOTING ? "REBOOT" : "REBIND"))),
+ ip->client->new->medium);
+ if (ip->client->active && ip->client->state != S_REBOOTING)
+ script_write_params("old_", ip->client->active);
+ script_write_params("new_", ip->client->new);
+ if (ip->client->alias)
+ script_write_params("alias_", ip->client->alias);
+ script_go();
+
+ /* Replace the old active lease with the new one. */
+ if (ip->client->active)
+ free_client_lease(ip->client->active);
+ ip->client->active = ip->client->new;
+ ip->client->new = NULL;
+
+ /* Set up a timeout to start the renewal process. */
+ add_timeout(ip->client->active->renewal, state_bound, ip);
+
+ note("bound to %s -- renewal in %d seconds.",
+ piaddr(ip->client->active->address),
+ (int)(ip->client->active->renewal - cur_time));
+ ip->client->state = S_BOUND;
+ reinitialize_interfaces();
+ go_daemon();
+}
+
+/*
+ * state_bound is called when we've successfully bound to a particular
+ * lease, but the renewal time on that lease has expired. We are
+ * expected to unicast a DHCPREQUEST to the server that gave us our
+ * original lease.
+ */
+void
+state_bound(void *ipp)
+{
+ struct interface_info *ip = ipp;
+
+ ASSERT_STATE(state, S_BOUND);
+
+ /* T1 has expired. */
+ make_request(ip, ip->client->active);
+ ip->client->xid = ip->client->packet.xid;
+
+ if (ip->client->active->options[DHO_DHCP_SERVER_IDENTIFIER].len == 4) {
+ memcpy(ip->client->destination.iabuf, ip->client->active->
+ options[DHO_DHCP_SERVER_IDENTIFIER].data, 4);
+ ip->client->destination.len = 4;
+ } else
+ ip->client->destination = iaddr_broadcast;
+
+ ip->client->first_sending = cur_time;
+ ip->client->interval = ip->client->config->initial_interval;
+ ip->client->state = S_RENEWING;
+
+ /* Send the first packet immediately. */
+ send_request(ip);
+}
+
+void
+bootp(struct packet *packet)
+{
+ struct iaddrlist *ap;
+
+ if (packet->raw->op != BOOTREPLY)
+ return;
+
+ /* If there's a reject list, make sure this packet's sender isn't
+ on it. */
+ for (ap = packet->interface->client->config->reject_list;
+ ap; ap = ap->next) {
+ if (addr_eq(packet->client_addr, ap->addr)) {
+ note("BOOTREPLY from %s rejected.", piaddr(ap->addr));
+ return;
+ }
+ }
+ dhcpoffer(packet);
+}
+
+void
+dhcp(struct packet *packet)
+{
+ struct iaddrlist *ap;
+ void (*handler)(struct packet *);
+ char *type;
+
+ switch (packet->packet_type) {
+ case DHCPOFFER:
+ handler = dhcpoffer;
+ type = "DHCPOFFER";
+ break;
+ case DHCPNAK:
+ handler = dhcpnak;
+ type = "DHCPNACK";
+ break;
+ case DHCPACK:
+ handler = dhcpack;
+ type = "DHCPACK";
+ break;
+ default:
+ return;
+ }
+
+ /* If there's a reject list, make sure this packet's sender isn't
+ on it. */
+ for (ap = packet->interface->client->config->reject_list;
+ ap; ap = ap->next) {
+ if (addr_eq(packet->client_addr, ap->addr)) {
+ note("%s from %s rejected.", type, piaddr(ap->addr));
+ return;
+ }
+ }
+ (*handler)(packet);
+}
+
+void
+dhcpoffer(struct packet *packet)
+{
+ struct interface_info *ip = packet->interface;
+ struct client_lease *lease, *lp;
+ int i;
+ int arp_timeout_needed, stop_selecting;
+ char *name = packet->options[DHO_DHCP_MESSAGE_TYPE].len ?
+ "DHCPOFFER" : "BOOTREPLY";
+
+ /* If we're not receptive to an offer right now, or if the offer
+ has an unrecognizable transaction id, then just drop it. */
+ if (ip->client->state != S_SELECTING ||
+ packet->interface->client->xid != packet->raw->xid ||
+ (packet->interface->hw_address.hlen != packet->raw->hlen) ||
+ (memcmp(packet->interface->hw_address.haddr,
+ packet->raw->chaddr, packet->raw->hlen)))
+ return;
+
+ note("%s from %s", name, piaddr(packet->client_addr));
+
+
+ /* If this lease doesn't supply the minimum required parameters,
+ blow it off. */
+ for (i = 0; ip->client->config->required_options[i]; i++) {
+ if (!packet->options[ip->client->config->
+ required_options[i]].len) {
+ note("%s isn't satisfactory.", name);
+ return;
+ }
+ }
+
+ /* If we've already seen this lease, don't record it again. */
+ for (lease = ip->client->offered_leases;
+ lease; lease = lease->next) {
+ if (lease->address.len == sizeof(packet->raw->yiaddr) &&
+ !memcmp(lease->address.iabuf,
+ &packet->raw->yiaddr, lease->address.len)) {
+ debug("%s already seen.", name);
+ return;
+ }
+ }
+
+ lease = packet_to_lease(packet);
+ if (!lease) {
+ note("packet_to_lease failed.");
+ return;
+ }
+
+ /* If this lease was acquired through a BOOTREPLY, record that
+ fact. */
+ if (!packet->options[DHO_DHCP_MESSAGE_TYPE].len)
+ lease->is_bootp = 1;
+
+ /* Record the medium under which this lease was offered. */
+ lease->medium = ip->client->medium;
+
+ /* Send out an ARP Request for the offered IP address. */
+ script_init("ARPSEND", lease->medium);
+ script_write_params("check_", lease);
+ /* If the script can't send an ARP request without waiting,
+ we'll be waiting when we do the ARPCHECK, so don't wait now. */
+ if (script_go())
+ arp_timeout_needed = 0;
+ else
+ arp_timeout_needed = 2;
+
+ /* Figure out when we're supposed to stop selecting. */
+ stop_selecting =
+ ip->client->first_sending + ip->client->config->select_interval;
+
+ /* If this is the lease we asked for, put it at the head of the
+ list, and don't mess with the arp request timeout. */
+ if (lease->address.len == ip->client->requested_address.len &&
+ !memcmp(lease->address.iabuf,
+ ip->client->requested_address.iabuf,
+ ip->client->requested_address.len)) {
+ lease->next = ip->client->offered_leases;
+ ip->client->offered_leases = lease;
+ } else {
+ /* If we already have an offer, and arping for this
+ offer would take us past the selection timeout,
+ then don't extend the timeout - just hope for the
+ best. */
+ if (ip->client->offered_leases &&
+ (cur_time + arp_timeout_needed) > stop_selecting)
+ arp_timeout_needed = 0;
+
+ /* Put the lease at the end of the list. */
+ lease->next = NULL;
+ if (!ip->client->offered_leases)
+ ip->client->offered_leases = lease;
+ else {
+ for (lp = ip->client->offered_leases; lp->next;
+ lp = lp->next)
+ ; /* nothing */
+ lp->next = lease;
+ }
+ }
+
+ /* If we're supposed to stop selecting before we've had time
+ to wait for the ARPREPLY, add some delay to wait for
+ the ARPREPLY. */
+ if (stop_selecting - cur_time < arp_timeout_needed)
+ stop_selecting = cur_time + arp_timeout_needed;
+
+ /* If the selecting interval has expired, go immediately to
+ state_selecting(). Otherwise, time out into
+ state_selecting at the select interval. */
+ if (stop_selecting <= 0)
+ state_selecting(ip);
+ else {
+ add_timeout(stop_selecting, state_selecting, ip);
+ cancel_timeout(send_discover, ip);
+ }
+}
+
+/* Allocate a client_lease structure and initialize it from the parameters
+ in the specified packet. */
+
+struct client_lease *
+packet_to_lease(struct packet *packet)
+{
+ struct client_lease *lease;
+ int i;
+
+ lease = malloc(sizeof(struct client_lease));
+
+ if (!lease) {
+ warning("dhcpoffer: no memory to record lease.");
+ return (NULL);
+ }
+
+ memset(lease, 0, sizeof(*lease));
+
+ /* Copy the lease options. */
+ for (i = 0; i < 256; i++) {
+ if (packet->options[i].len) {
+ lease->options[i].data =
+ malloc(packet->options[i].len + 1);
+ if (!lease->options[i].data) {
+ warning("dhcpoffer: no memory for option %d", i);
+ free_client_lease(lease);
+ return (NULL);
+ } else {
+ memcpy(lease->options[i].data,
+ packet->options[i].data,
+ packet->options[i].len);
+ lease->options[i].len =
+ packet->options[i].len;
+ lease->options[i].data[lease->options[i].len] =
+ 0;
+ }
+ if (!check_option(lease,i)) {
+ /* ignore a bogus lease offer */
+ warning("Invalid lease option - ignoring offer");
+ free_client_lease(lease);
+ return (NULL);
+ }
+ }
+ }
+
+ lease->address.len = sizeof(packet->raw->yiaddr);
+ memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len);
+
+ /* If the server name was filled out, copy it.
+ Do not attempt to validate the server name as a host name.
+ RFC 2131 merely states that sname is NUL-terminated (which do
+ do not assume) and that it is the server's host name. Since
+ the ISC client and server allow arbitrary characters, we do
+ as well. */
+ if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
+ !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)) &&
+ packet->raw->sname[0]) {
+ lease->server_name = malloc(DHCP_SNAME_LEN + 1);
+ if (!lease->server_name) {
+ warning("dhcpoffer: no memory for server name.");
+ free_client_lease(lease);
+ return (NULL);
+ }
+ memcpy(lease->server_name, packet->raw->sname, DHCP_SNAME_LEN);
+ lease->server_name[DHCP_SNAME_LEN]='\0';
+ }
+
+ /* Ditto for the filename. */
+ if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
+ !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)) &&
+ packet->raw->file[0]) {
+ /* Don't count on the NUL terminator. */
+ lease->filename = malloc(DHCP_FILE_LEN + 1);
+ if (!lease->filename) {
+ warning("dhcpoffer: no memory for filename.");
+ free_client_lease(lease);
+ return (NULL);
+ }
+ memcpy(lease->filename, packet->raw->file, DHCP_FILE_LEN);
+ lease->filename[DHCP_FILE_LEN]='\0';
+ }
+ return lease;
+}
+
+void
+dhcpnak(struct packet *packet)
+{
+ struct interface_info *ip = packet->interface;
+
+ /* If we're not receptive to an offer right now, or if the offer
+ has an unrecognizable transaction id, then just drop it. */
+ if (packet->interface->client->xid != packet->raw->xid ||
+ (packet->interface->hw_address.hlen != packet->raw->hlen) ||
+ (memcmp(packet->interface->hw_address.haddr,
+ packet->raw->chaddr, packet->raw->hlen)))
+ return;
+
+ if (ip->client->state != S_REBOOTING &&
+ ip->client->state != S_REQUESTING &&
+ ip->client->state != S_RENEWING &&
+ ip->client->state != S_REBINDING)
+ return;
+
+ note("DHCPNAK from %s", piaddr(packet->client_addr));
+
+ if (!ip->client->active) {
+ note("DHCPNAK with no active lease.\n");
+ return;
+ }
+
+ free_client_lease(ip->client->active);
+ ip->client->active = NULL;
+
+ /* Stop sending DHCPREQUEST packets... */
+ cancel_timeout(send_request, ip);
+
+ ip->client->state = S_INIT;
+ state_init(ip);
+}
+
+/* Send out a DHCPDISCOVER packet, and set a timeout to send out another
+ one after the right interval has expired. If we don't get an offer by
+ the time we reach the panic interval, call the panic function. */
+
+void
+send_discover(void *ipp)
+{
+ struct interface_info *ip = ipp;
+ int interval, increase = 1;
+
+ /* Figure out how long it's been since we started transmitting. */
+ interval = cur_time - ip->client->first_sending;
+
+ /* If we're past the panic timeout, call the script and tell it
+ we haven't found anything for this interface yet. */
+ if (interval > ip->client->config->timeout) {
+ state_panic(ip);
+ return;
+ }
+
+ /* If we're selecting media, try the whole list before doing
+ the exponential backoff, but if we've already received an
+ offer, stop looping, because we obviously have it right. */
+ if (!ip->client->offered_leases &&
+ ip->client->config->media) {
+ int fail = 0;
+again:
+ if (ip->client->medium) {
+ ip->client->medium = ip->client->medium->next;
+ increase = 0;
+ }
+ if (!ip->client->medium) {
+ if (fail)
+ error("No valid media types for %s!", ip->name);
+ ip->client->medium = ip->client->config->media;
+ increase = 1;
+ }
+
+ note("Trying medium \"%s\" %d", ip->client->medium->string,
+ increase);
+ script_init("MEDIUM", ip->client->medium);
+ if (script_go())
+ goto again;
+ }
+
+ /*
+ * If we're supposed to increase the interval, do so. If it's
+ * currently zero (i.e., we haven't sent any packets yet), set
+ * it to one; otherwise, add to it a random number between zero
+ * and two times itself. On average, this means that it will
+ * double with every transmission.
+ */
+ if (increase) {
+ if (!ip->client->interval)
+ ip->client->interval =
+ ip->client->config->initial_interval;
+ else {
+ ip->client->interval += (arc4random() >> 2) %
+ (2 * ip->client->interval);
+ }
+
+ /* Don't backoff past cutoff. */
+ if (ip->client->interval >
+ ip->client->config->backoff_cutoff)
+ ip->client->interval =
+ ((ip->client->config->backoff_cutoff / 2)
+ + ((arc4random() >> 2) %
+ ip->client->config->backoff_cutoff));
+ } else if (!ip->client->interval)
+ ip->client->interval =
+ ip->client->config->initial_interval;
+
+ /* If the backoff would take us to the panic timeout, just use that
+ as the interval. */
+ if (cur_time + ip->client->interval >
+ ip->client->first_sending + ip->client->config->timeout)
+ ip->client->interval =
+ (ip->client->first_sending +
+ ip->client->config->timeout) - cur_time + 1;
+
+ /* Record the number of seconds since we started sending. */
+ if (interval < 65536)
+ ip->client->packet.secs = htons(interval);
+ else
+ ip->client->packet.secs = htons(65535);
+ ip->client->secs = ip->client->packet.secs;
+
+ note("DHCPDISCOVER on %s to %s port %d interval %d",
+ ip->name, inet_ntoa(sockaddr_broadcast.sin_addr),
+ ntohs(sockaddr_broadcast.sin_port),
+ (int)ip->client->interval);
+
+ /* Send out a packet. */
+ (void)send_packet(ip, &ip->client->packet, ip->client->packet_length,
+ inaddr_any, &sockaddr_broadcast, NULL);
+
+ add_timeout(cur_time + ip->client->interval, send_discover, ip);
+}
+
+/*
+ * state_panic gets called if we haven't received any offers in a preset
+ * amount of time. When this happens, we try to use existing leases
+ * that haven't yet expired, and failing that, we call the client script
+ * and hope it can do something.
+ */
+void
+state_panic(void *ipp)
+{
+ struct interface_info *ip = ipp;
+ struct client_lease *loop = ip->client->active;
+ struct client_lease *lp;
+
+ note("No DHCPOFFERS received.");
+
+ /* We may not have an active lease, but we may have some
+ predefined leases that we can try. */
+ if (!ip->client->active && ip->client->leases)
+ goto activate_next;
+
+ /* Run through the list of leases and see if one can be used. */
+ while (ip->client->active) {
+ if (ip->client->active->expiry > cur_time) {
+ note("Trying recorded lease %s",
+ piaddr(ip->client->active->address));
+ /* Run the client script with the existing
+ parameters. */
+ script_init("TIMEOUT",
+ ip->client->active->medium);
+ script_write_params("new_", ip->client->active);
+ if (ip->client->alias)
+ script_write_params("alias_",
+ ip->client->alias);
+
+ /* If the old lease is still good and doesn't
+ yet need renewal, go into BOUND state and
+ timeout at the renewal time. */
+ if (!script_go()) {
+ if (cur_time <
+ ip->client->active->renewal) {
+ ip->client->state = S_BOUND;
+ note("bound: renewal in %d seconds.",
+ (int)(ip->client->active->renewal -
+ cur_time));
+ add_timeout(
+ ip->client->active->renewal,
+ state_bound, ip);
+ } else {
+ ip->client->state = S_BOUND;
+ note("bound: immediate renewal.");
+ state_bound(ip);
+ }
+ reinitialize_interfaces();
+ go_daemon();
+ return;
+ }
+ }
+
+ /* If there are no other leases, give up. */
+ if (!ip->client->leases) {
+ ip->client->leases = ip->client->active;
+ ip->client->active = NULL;
+ break;
+ }
+
+activate_next:
+ /* Otherwise, put the active lease at the end of the
+ lease list, and try another lease.. */
+ for (lp = ip->client->leases; lp->next; lp = lp->next)
+ ;
+ lp->next = ip->client->active;
+ if (lp->next)
+ lp->next->next = NULL;
+ ip->client->active = ip->client->leases;
+ ip->client->leases = ip->client->leases->next;
+
+ /* If we already tried this lease, we've exhausted the
+ set of leases, so we might as well give up for
+ now. */
+ if (ip->client->active == loop)
+ break;
+ else if (!loop)
+ loop = ip->client->active;
+ }
+
+ /* No leases were available, or what was available didn't work, so
+ tell the shell script that we failed to allocate an address,
+ and try again later. */
+ note("No working leases in persistent database - sleeping.\n");
+ script_init("FAIL", NULL);
+ if (ip->client->alias)
+ script_write_params("alias_", ip->client->alias);
+ script_go();
+ ip->client->state = S_INIT;
+ add_timeout(cur_time + ip->client->config->retry_interval, state_init,
+ ip);
+ go_daemon();
+}
+
+void
+send_request(void *ipp)
+{
+ struct interface_info *ip = ipp;
+ struct sockaddr_in destination;
+ struct in_addr from;
+ int interval;
+
+ /* Figure out how long it's been since we started transmitting. */
+ interval = cur_time - ip->client->first_sending;
+
+ /* If we're in the INIT-REBOOT or REQUESTING state and we're
+ past the reboot timeout, go to INIT and see if we can
+ DISCOVER an address... */
+ /* XXX In the INIT-REBOOT state, if we don't get an ACK, it
+ means either that we're on a network with no DHCP server,
+ or that our server is down. In the latter case, assuming
+ that there is a backup DHCP server, DHCPDISCOVER will get
+ us a new address, but we could also have successfully
+ reused our old address. In the former case, we're hosed
+ anyway. This is not a win-prone situation. */
+ if ((ip->client->state == S_REBOOTING ||
+ ip->client->state == S_REQUESTING) &&
+ interval > ip->client->config->reboot_timeout) {
+cancel:
+ ip->client->state = S_INIT;
+ cancel_timeout(send_request, ip);
+ state_init(ip);
+ return;
+ }
+
+ /* If we're in the reboot state, make sure the media is set up
+ correctly. */
+ if (ip->client->state == S_REBOOTING &&
+ !ip->client->medium &&
+ ip->client->active->medium ) {
+ script_init("MEDIUM", ip->client->active->medium);
+
+ /* If the medium we chose won't fly, go to INIT state. */
+ if (script_go())
+ goto cancel;
+
+ /* Record the medium. */
+ ip->client->medium = ip->client->active->medium;
+ }
+
+ /* If the lease has expired, relinquish the address and go back
+ to the INIT state. */
+ if (ip->client->state != S_REQUESTING &&
+ cur_time > ip->client->active->expiry) {
+ /* Run the client script with the new parameters. */
+ script_init("EXPIRE", NULL);
+ script_write_params("old_", ip->client->active);
+ if (ip->client->alias)
+ script_write_params("alias_", ip->client->alias);
+ script_go();
+
+ /* Now do a preinit on the interface so that we can
+ discover a new address. */
+ script_init("PREINIT", NULL);
+ if (ip->client->alias)
+ script_write_params("alias_", ip->client->alias);
+ script_go();
+
+ ip->client->state = S_INIT;
+ state_init(ip);
+ return;
+ }
+
+ /* Do the exponential backoff... */
+ if (!ip->client->interval)
+ ip->client->interval = ip->client->config->initial_interval;
+ else
+ ip->client->interval += ((arc4random() >> 2) %
+ (2 * ip->client->interval));
+
+ /* Don't backoff past cutoff. */
+ if (ip->client->interval >
+ ip->client->config->backoff_cutoff)
+ ip->client->interval =
+ ((ip->client->config->backoff_cutoff / 2) +
+ ((arc4random() >> 2) % ip->client->interval));
+
+ /* If the backoff would take us to the expiry time, just set the
+ timeout to the expiry time. */
+ if (ip->client->state != S_REQUESTING &&
+ cur_time + ip->client->interval >
+ ip->client->active->expiry)
+ ip->client->interval =
+ ip->client->active->expiry - cur_time + 1;
+
+ /* If the lease T2 time has elapsed, or if we're not yet bound,
+ broadcast the DHCPREQUEST rather than unicasting. */
+ memset(&destination, 0, sizeof(destination));
+ if (ip->client->state == S_REQUESTING ||
+ ip->client->state == S_REBOOTING ||
+ cur_time > ip->client->active->rebind)
+ destination.sin_addr.s_addr = INADDR_BROADCAST;
+ else
+ memcpy(&destination.sin_addr.s_addr,
+ ip->client->destination.iabuf,
+ sizeof(destination.sin_addr.s_addr));
+ destination.sin_port = htons(REMOTE_PORT);
+ destination.sin_family = AF_INET;
+ destination.sin_len = sizeof(destination);
+
+ if (ip->client->state != S_REQUESTING)
+ memcpy(&from, ip->client->active->address.iabuf,
+ sizeof(from));
+ else
+ from.s_addr = INADDR_ANY;
+
+ /* Record the number of seconds since we started sending. */
+ if (ip->client->state == S_REQUESTING)
+ ip->client->packet.secs = ip->client->secs;
+ else {
+ if (interval < 65536)
+ ip->client->packet.secs = htons(interval);
+ else
+ ip->client->packet.secs = htons(65535);
+ }
+
+ note("DHCPREQUEST on %s to %s port %d", ip->name,
+ inet_ntoa(destination.sin_addr), ntohs(destination.sin_port));
+
+ /* Send out a packet. */
+ (void) send_packet(ip, &ip->client->packet, ip->client->packet_length,
+ from, &destination, NULL);
+
+ add_timeout(cur_time + ip->client->interval, send_request, ip);
+}
+
+void
+send_decline(void *ipp)
+{
+ struct interface_info *ip = ipp;
+
+ note("DHCPDECLINE on %s to %s port %d", ip->name,
+ inet_ntoa(sockaddr_broadcast.sin_addr),
+ ntohs(sockaddr_broadcast.sin_port));
+
+ /* Send out a packet. */
+ (void) send_packet(ip, &ip->client->packet, ip->client->packet_length,
+ inaddr_any, &sockaddr_broadcast, NULL);
+}
+
+void
+make_discover(struct interface_info *ip, struct client_lease *lease)
+{
+ unsigned char discover = DHCPDISCOVER;
+ struct tree_cache *options[256];
+ struct tree_cache option_elements[256];
+ int i;
+
+ memset(option_elements, 0, sizeof(option_elements));
+ memset(options, 0, sizeof(options));
+ memset(&ip->client->packet, 0, sizeof(ip->client->packet));
+
+ /* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */
+ i = DHO_DHCP_MESSAGE_TYPE;
+ options[i] = &option_elements[i];
+ options[i]->value = &discover;
+ options[i]->len = sizeof(discover);
+ options[i]->buf_size = sizeof(discover);
+ options[i]->timeout = 0xFFFFFFFF;
+
+ /* Request the options we want */
+ i = DHO_DHCP_PARAMETER_REQUEST_LIST;
+ options[i] = &option_elements[i];
+ options[i]->value = ip->client->config->requested_options;
+ options[i]->len = ip->client->config->requested_option_count;
+ options[i]->buf_size =
+ ip->client->config->requested_option_count;
+ options[i]->timeout = 0xFFFFFFFF;
+
+ /* If we had an address, try to get it again. */
+ if (lease) {
+ ip->client->requested_address = lease->address;
+ i = DHO_DHCP_REQUESTED_ADDRESS;
+ options[i] = &option_elements[i];
+ options[i]->value = lease->address.iabuf;
+ options[i]->len = lease->address.len;
+ options[i]->buf_size = lease->address.len;
+ options[i]->timeout = 0xFFFFFFFF;
+ } else
+ ip->client->requested_address.len = 0;
+
+ /* Send any options requested in the config file. */
+ for (i = 0; i < 256; i++)
+ if (!options[i] &&
+ ip->client->config->send_options[i].data) {
+ options[i] = &option_elements[i];
+ options[i]->value =
+ ip->client->config->send_options[i].data;
+ options[i]->len =
+ ip->client->config->send_options[i].len;
+ options[i]->buf_size =
+ ip->client->config->send_options[i].len;
+ options[i]->timeout = 0xFFFFFFFF;
+ }
+
+ /* Set up the option buffer... */
+ ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
+ options, 0, 0, 0, NULL, 0);
+ if (ip->client->packet_length < BOOTP_MIN_LEN)
+ ip->client->packet_length = BOOTP_MIN_LEN;
+
+ ip->client->packet.op = BOOTREQUEST;
+ ip->client->packet.htype = ip->hw_address.htype;
+ ip->client->packet.hlen = ip->hw_address.hlen;
+ ip->client->packet.hops = 0;
+ ip->client->packet.xid = arc4random();
+ ip->client->packet.secs = 0; /* filled in by send_discover. */
+ ip->client->packet.flags = 0;
+
+ memset(&(ip->client->packet.ciaddr),
+ 0, sizeof(ip->client->packet.ciaddr));
+ memset(&(ip->client->packet.yiaddr),
+ 0, sizeof(ip->client->packet.yiaddr));
+ memset(&(ip->client->packet.siaddr),
+ 0, sizeof(ip->client->packet.siaddr));
+ memset(&(ip->client->packet.giaddr),
+ 0, sizeof(ip->client->packet.giaddr));
+ memcpy(ip->client->packet.chaddr,
+ ip->hw_address.haddr, ip->hw_address.hlen);
+}
+
+
+void
+make_request(struct interface_info *ip, struct client_lease * lease)
+{
+ unsigned char request = DHCPREQUEST;
+ struct tree_cache *options[256];
+ struct tree_cache option_elements[256];
+ int i;
+
+ memset(options, 0, sizeof(options));
+ memset(&ip->client->packet, 0, sizeof(ip->client->packet));
+
+ /* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */
+ i = DHO_DHCP_MESSAGE_TYPE;
+ options[i] = &option_elements[i];
+ options[i]->value = &request;
+ options[i]->len = sizeof(request);
+ options[i]->buf_size = sizeof(request);
+ options[i]->timeout = 0xFFFFFFFF;
+
+ /* Request the options we want */
+ i = DHO_DHCP_PARAMETER_REQUEST_LIST;
+ options[i] = &option_elements[i];
+ options[i]->value = ip->client->config->requested_options;
+ options[i]->len = ip->client->config->requested_option_count;
+ options[i]->buf_size =
+ ip->client->config->requested_option_count;
+ options[i]->timeout = 0xFFFFFFFF;
+
+ /* If we are requesting an address that hasn't yet been assigned
+ to us, use the DHCP Requested Address option. */
+ if (ip->client->state == S_REQUESTING) {
+ /* Send back the server identifier... */
+ i = DHO_DHCP_SERVER_IDENTIFIER;
+ options[i] = &option_elements[i];
+ options[i]->value = lease->options[i].data;
+ options[i]->len = lease->options[i].len;
+ options[i]->buf_size = lease->options[i].len;
+ options[i]->timeout = 0xFFFFFFFF;
+ }
+ if (ip->client->state == S_REQUESTING ||
+ ip->client->state == S_REBOOTING) {
+ ip->client->requested_address = lease->address;
+ i = DHO_DHCP_REQUESTED_ADDRESS;
+ options[i] = &option_elements[i];
+ options[i]->value = lease->address.iabuf;
+ options[i]->len = lease->address.len;
+ options[i]->buf_size = lease->address.len;
+ options[i]->timeout = 0xFFFFFFFF;
+ } else
+ ip->client->requested_address.len = 0;
+
+ /* Send any options requested in the config file. */
+ for (i = 0; i < 256; i++)
+ if (!options[i] &&
+ ip->client->config->send_options[i].data) {
+ options[i] = &option_elements[i];
+ options[i]->value =
+ ip->client->config->send_options[i].data;
+ options[i]->len =
+ ip->client->config->send_options[i].len;
+ options[i]->buf_size =
+ ip->client->config->send_options[i].len;
+ options[i]->timeout = 0xFFFFFFFF;
+ }
+
+ /* Set up the option buffer... */
+ ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
+ options, 0, 0, 0, NULL, 0);
+ if (ip->client->packet_length < BOOTP_MIN_LEN)
+ ip->client->packet_length = BOOTP_MIN_LEN;
+
+ ip->client->packet.op = BOOTREQUEST;
+ ip->client->packet.htype = ip->hw_address.htype;
+ ip->client->packet.hlen = ip->hw_address.hlen;
+ ip->client->packet.hops = 0;
+ ip->client->packet.xid = ip->client->xid;
+ ip->client->packet.secs = 0; /* Filled in by send_request. */
+
+ /* If we own the address we're requesting, put it in ciaddr;
+ otherwise set ciaddr to zero. */
+ if (ip->client->state == S_BOUND ||
+ ip->client->state == S_RENEWING ||
+ ip->client->state == S_REBINDING) {
+ memcpy(&ip->client->packet.ciaddr,
+ lease->address.iabuf, lease->address.len);
+ ip->client->packet.flags = 0;
+ } else {
+ memset(&ip->client->packet.ciaddr, 0,
+ sizeof(ip->client->packet.ciaddr));
+ ip->client->packet.flags = 0;
+ }
+
+ memset(&ip->client->packet.yiaddr, 0,
+ sizeof(ip->client->packet.yiaddr));
+ memset(&ip->client->packet.siaddr, 0,
+ sizeof(ip->client->packet.siaddr));
+ memset(&ip->client->packet.giaddr, 0,
+ sizeof(ip->client->packet.giaddr));
+ memcpy(ip->client->packet.chaddr,
+ ip->hw_address.haddr, ip->hw_address.hlen);
+}
+
+void
+make_decline(struct interface_info *ip, struct client_lease *lease)
+{
+ struct tree_cache *options[256], message_type_tree;
+ struct tree_cache requested_address_tree;
+ struct tree_cache server_id_tree, client_id_tree;
+ unsigned char decline = DHCPDECLINE;
+ int i;
+
+ memset(options, 0, sizeof(options));
+ memset(&ip->client->packet, 0, sizeof(ip->client->packet));
+
+ /* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */
+ i = DHO_DHCP_MESSAGE_TYPE;
+ options[i] = &message_type_tree;
+ options[i]->value = &decline;
+ options[i]->len = sizeof(decline);
+ options[i]->buf_size = sizeof(decline);
+ options[i]->timeout = 0xFFFFFFFF;
+
+ /* Send back the server identifier... */
+ i = DHO_DHCP_SERVER_IDENTIFIER;
+ options[i] = &server_id_tree;
+ options[i]->value = lease->options[i].data;
+ options[i]->len = lease->options[i].len;
+ options[i]->buf_size = lease->options[i].len;
+ options[i]->timeout = 0xFFFFFFFF;
+
+ /* Send back the address we're declining. */
+ i = DHO_DHCP_REQUESTED_ADDRESS;
+ options[i] = &requested_address_tree;
+ options[i]->value = lease->address.iabuf;
+ options[i]->len = lease->address.len;
+ options[i]->buf_size = lease->address.len;
+ options[i]->timeout = 0xFFFFFFFF;
+
+ /* Send the uid if the user supplied one. */
+ i = DHO_DHCP_CLIENT_IDENTIFIER;
+ if (ip->client->config->send_options[i].len) {
+ options[i] = &client_id_tree;
+ options[i]->value = ip->client->config->send_options[i].data;
+ options[i]->len = ip->client->config->send_options[i].len;
+ options[i]->buf_size = ip->client->config->send_options[i].len;
+ options[i]->timeout = 0xFFFFFFFF;
+ }
+
+
+ /* Set up the option buffer... */
+ ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
+ options, 0, 0, 0, NULL, 0);
+ if (ip->client->packet_length < BOOTP_MIN_LEN)
+ ip->client->packet_length = BOOTP_MIN_LEN;
+
+ ip->client->packet.op = BOOTREQUEST;
+ ip->client->packet.htype = ip->hw_address.htype;
+ ip->client->packet.hlen = ip->hw_address.hlen;
+ ip->client->packet.hops = 0;
+ ip->client->packet.xid = ip->client->xid;
+ ip->client->packet.secs = 0; /* Filled in by send_request. */
+ ip->client->packet.flags = 0;
+
+ /* ciaddr must always be zero. */
+ memset(&ip->client->packet.ciaddr, 0,
+ sizeof(ip->client->packet.ciaddr));
+ memset(&ip->client->packet.yiaddr, 0,
+ sizeof(ip->client->packet.yiaddr));
+ memset(&ip->client->packet.siaddr, 0,
+ sizeof(ip->client->packet.siaddr));
+ memset(&ip->client->packet.giaddr, 0,
+ sizeof(ip->client->packet.giaddr));
+ memcpy(ip->client->packet.chaddr,
+ ip->hw_address.haddr, ip->hw_address.hlen);
+}
+
+void
+free_client_lease(struct client_lease *lease)
+{
+ int i;
+
+ if (lease->server_name)
+ free(lease->server_name);
+ if (lease->filename)
+ free(lease->filename);
+ for (i = 0; i < 256; i++) {
+ if (lease->options[i].len)
+ free(lease->options[i].data);
+ }
+ free(lease);
+}
+
+FILE *leaseFile;
+
+void
+rewrite_client_leases(void)
+{
+ struct client_lease *lp;
+
+ if (!leaseFile) {
+ leaseFile = fopen(path_dhclient_db, "w");
+ if (!leaseFile)
+ error("can't create %s: %m", path_dhclient_db);
+ } else {
+ fflush(leaseFile);
+ rewind(leaseFile);
+ }
+
+ for (lp = ifi->client->leases; lp; lp = lp->next)
+ write_client_lease(ifi, lp, 1);
+ if (ifi->client->active)
+ write_client_lease(ifi, ifi->client->active, 1);
+
+ fflush(leaseFile);
+ ftruncate(fileno(leaseFile), ftello(leaseFile));
+ fsync(fileno(leaseFile));
+}
+
+void
+write_client_lease(struct interface_info *ip, struct client_lease *lease,
+ int rewrite)
+{
+ static int leases_written;
+ struct tm *t;
+ int i;
+
+ if (!rewrite) {
+ if (leases_written++ > 20) {
+ rewrite_client_leases();
+ leases_written = 0;
+ }
+ }
+
+ /* If the lease came from the config file, we don't need to stash
+ a copy in the lease database. */
+ if (lease->is_static)
+ return;
+
+ if (!leaseFile) { /* XXX */
+ leaseFile = fopen(path_dhclient_db, "w");
+ if (!leaseFile)
+ error("can't create %s: %m", path_dhclient_db);
+ }
+
+ fprintf(leaseFile, "lease {\n");
+ if (lease->is_bootp)
+ fprintf(leaseFile, " bootp;\n");
+ fprintf(leaseFile, " interface \"%s\";\n", ip->name);
+ fprintf(leaseFile, " fixed-address %s;\n", piaddr(lease->address));
+ if (lease->filename)
+ fprintf(leaseFile, " filename \"%s\";\n", lease->filename);
+ if (lease->server_name)
+ fprintf(leaseFile, " server-name \"%s\";\n",
+ lease->server_name);
+ if (lease->medium)
+ fprintf(leaseFile, " medium \"%s\";\n", lease->medium->string);
+ for (i = 0; i < 256; i++)
+ if (lease->options[i].len)
+ fprintf(leaseFile, " option %s %s;\n",
+ dhcp_options[i].name,
+ pretty_print_option(i, lease->options[i].data,
+ lease->options[i].len, 1, 1));
+
+ t = gmtime(&lease->renewal);
+ fprintf(leaseFile, " renew %d %d/%d/%d %02d:%02d:%02d;\n",
+ t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec);
+ t = gmtime(&lease->rebind);
+ fprintf(leaseFile, " rebind %d %d/%d/%d %02d:%02d:%02d;\n",
+ t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec);
+ t = gmtime(&lease->expiry);
+ fprintf(leaseFile, " expire %d %d/%d/%d %02d:%02d:%02d;\n",
+ t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec);
+ fprintf(leaseFile, "}\n");
+ fflush(leaseFile);
+}
+
+void
+script_init(char *reason, struct string_list *medium)
+{
+ size_t len, mediumlen = 0;
+ struct imsg_hdr hdr;
+ struct buf *buf;
+ int errs;
+
+ if (medium != NULL && medium->string != NULL)
+ mediumlen = strlen(medium->string);
+
+ hdr.code = IMSG_SCRIPT_INIT;
+ hdr.len = sizeof(struct imsg_hdr) +
+ sizeof(size_t) + mediumlen +
+ sizeof(size_t) + strlen(reason);
+
+ if ((buf = buf_open(hdr.len)) == NULL)
+ error("buf_open: %m");
+
+ errs = 0;
+ errs += buf_add(buf, &hdr, sizeof(hdr));
+ errs += buf_add(buf, &mediumlen, sizeof(mediumlen));
+ if (mediumlen > 0)
+ errs += buf_add(buf, medium->string, mediumlen);
+ len = strlen(reason);
+ errs += buf_add(buf, &len, sizeof(len));
+ errs += buf_add(buf, reason, len);
+
+ if (errs)
+ error("buf_add: %m");
+
+ if (buf_close(privfd, buf) == -1)
+ error("buf_close: %m");
+}
+
+void
+priv_script_init(char *reason, char *medium)
+{
+ struct interface_info *ip = ifi;
+
+ if (ip) {
+ ip->client->scriptEnvsize = 100;
+ if (ip->client->scriptEnv == NULL)
+ ip->client->scriptEnv =
+ malloc(ip->client->scriptEnvsize * sizeof(char *));
+ if (ip->client->scriptEnv == NULL)
+ error("script_init: no memory for environment");
+
+ ip->client->scriptEnv[0] = strdup(CLIENT_PATH);
+ if (ip->client->scriptEnv[0] == NULL)
+ error("script_init: no memory for environment");
+
+ ip->client->scriptEnv[1] = NULL;
+
+ script_set_env(ip->client, "", "interface", ip->name);
+
+ if (medium)
+ script_set_env(ip->client, "", "medium", medium);
+
+ script_set_env(ip->client, "", "reason", reason);
+ }
+}
+
+void
+priv_script_write_params(char *prefix, struct client_lease *lease)
+{
+ struct interface_info *ip = ifi;
+ u_int8_t dbuf[1500], *dp = NULL;
+ int i, len;
+ char tbuf[128];
+
+ script_set_env(ip->client, prefix, "ip_address",
+ piaddr(lease->address));
+
+ if (ip->client->config->default_actions[DHO_SUBNET_MASK] ==
+ ACTION_SUPERSEDE) {
+ dp = ip->client->config->defaults[DHO_SUBNET_MASK].data;
+ len = ip->client->config->defaults[DHO_SUBNET_MASK].len;
+ } else {
+ dp = lease->options[DHO_SUBNET_MASK].data;
+ len = lease->options[DHO_SUBNET_MASK].len;
+ }
+ if (len && (len < sizeof(lease->address.iabuf))) {
+ struct iaddr netmask, subnet, broadcast;
+
+ memcpy(netmask.iabuf, dp, len);
+ netmask.len = len;
+ subnet = subnet_number(lease->address, netmask);
+ if (subnet.len) {
+ script_set_env(ip->client, prefix, "network_number",
+ piaddr(subnet));
+ if (!lease->options[DHO_BROADCAST_ADDRESS].len) {
+ broadcast = broadcast_addr(subnet, netmask);
+ if (broadcast.len)
+ script_set_env(ip->client, prefix,
+ "broadcast_address",
+ piaddr(broadcast));
+ }
+ }
+ }
+
+ if (lease->filename)
+ script_set_env(ip->client, prefix, "filename", lease->filename);
+ if (lease->server_name)
+ script_set_env(ip->client, prefix, "server_name",
+ lease->server_name);
+ for (i = 0; i < 256; i++) {
+ len = 0;
+
+ if (ip->client->config->defaults[i].len) {
+ if (lease->options[i].len) {
+ switch (
+ ip->client->config->default_actions[i]) {
+ case ACTION_DEFAULT:
+ dp = lease->options[i].data;
+ len = lease->options[i].len;
+ break;
+ case ACTION_SUPERSEDE:
+supersede:
+ dp = ip->client->
+ config->defaults[i].data;
+ len = ip->client->
+ config->defaults[i].len;
+ break;
+ case ACTION_PREPEND:
+ len = ip->client->
+ config->defaults[i].len +
+ lease->options[i].len;
+ if (len > sizeof(dbuf)) {
+ warning("no space to %s %s",
+ "prepend option",
+ dhcp_options[i].name);
+ goto supersede;
+ }
+ dp = dbuf;
+ memcpy(dp,
+ ip->client->
+ config->defaults[i].data,
+ ip->client->
+ config->defaults[i].len);
+ memcpy(dp + ip->client->
+ config->defaults[i].len,
+ lease->options[i].data,
+ lease->options[i].len);
+ dp[len] = '\0';
+ break;
+ case ACTION_APPEND:
+ len = ip->client->
+ config->defaults[i].len +
+ lease->options[i].len;
+ if (len > sizeof(dbuf)) {
+ warning("no space to %s %s",
+ "append option",
+ dhcp_options[i].name);
+ goto supersede;
+ }
+ dp = dbuf;
+ memcpy(dp,
+ lease->options[i].data,
+ lease->options[i].len);
+ memcpy(dp + lease->options[i].len,
+ ip->client->
+ config->defaults[i].data,
+ ip->client->
+ config->defaults[i].len);
+ dp[len] = '\0';
+ }
+ } else {
+ dp = ip->client->
+ config->defaults[i].data;
+ len = ip->client->
+ config->defaults[i].len;
+ }
+ } else if (lease->options[i].len) {
+ len = lease->options[i].len;
+ dp = lease->options[i].data;
+ } else {
+ len = 0;
+ }
+ if (len) {
+ char name[256];
+
+ if (dhcp_option_ev_name(name, sizeof(name),
+ &dhcp_options[i]))
+ script_set_env(ip->client, prefix, name,
+ pretty_print_option(i, dp, len, 0, 0));
+ }
+ }
+ snprintf(tbuf, sizeof(tbuf), "%d", (int)lease->expiry);
+ script_set_env(ip->client, prefix, "expiry", tbuf);
+}
+
+void
+script_write_params(char *prefix, struct client_lease *lease)
+{
+ size_t fn_len = 0, sn_len = 0, pr_len = 0;
+ struct imsg_hdr hdr;
+ struct buf *buf;
+ int errs, i;
+
+ if (lease->filename != NULL)
+ fn_len = strlen(lease->filename);
+ if (lease->server_name != NULL)
+ sn_len = strlen(lease->server_name);
+ if (prefix != NULL)
+ pr_len = strlen(prefix);
+
+ hdr.code = IMSG_SCRIPT_WRITE_PARAMS;
+ hdr.len = sizeof(hdr) + sizeof(struct client_lease) +
+ sizeof(size_t) + fn_len + sizeof(size_t) + sn_len +
+ sizeof(size_t) + pr_len;
+
+ for (i = 0; i < 256; i++)
+ hdr.len += sizeof(int) + lease->options[i].len;
+
+ scripttime = time(NULL);
+
+ if ((buf = buf_open(hdr.len)) == NULL)
+ error("buf_open: %m");
+
+ errs = 0;
+ errs += buf_add(buf, &hdr, sizeof(hdr));
+ errs += buf_add(buf, lease, sizeof(struct client_lease));
+ errs += buf_add(buf, &fn_len, sizeof(fn_len));
+ errs += buf_add(buf, lease->filename, fn_len);
+ errs += buf_add(buf, &sn_len, sizeof(sn_len));
+ errs += buf_add(buf, lease->server_name, sn_len);
+ errs += buf_add(buf, &pr_len, sizeof(pr_len));
+ errs += buf_add(buf, prefix, pr_len);
+
+ for (i = 0; i < 256; i++) {
+ errs += buf_add(buf, &lease->options[i].len,
+ sizeof(lease->options[i].len));
+ errs += buf_add(buf, lease->options[i].data,
+ lease->options[i].len);
+ }
+
+ if (errs)
+ error("buf_add: %m");
+
+ if (buf_close(privfd, buf) == -1)
+ error("buf_close: %m");
+}
+
+int
+script_go(void)
+{
+ struct imsg_hdr hdr;
+ struct buf *buf;
+ int ret;
+
+ scripttime = time(NULL);
+
+ hdr.code = IMSG_SCRIPT_GO;
+ hdr.len = sizeof(struct imsg_hdr);
+
+ if ((buf = buf_open(hdr.len)) == NULL)
+ error("buf_open: %m");
+
+ if (buf_add(buf, &hdr, sizeof(hdr)))
+ error("buf_add: %m");
+
+ if (buf_close(privfd, buf) == -1)
+ error("buf_close: %m");
+
+ bzero(&hdr, sizeof(hdr));
+ buf_read(privfd, &hdr, sizeof(hdr));
+ if (hdr.code != IMSG_SCRIPT_GO_RET)
+ error("unexpected msg type %u", hdr.code);
+ if (hdr.len != sizeof(hdr) + sizeof(int))
+ error("received corrupted message");
+ buf_read(privfd, &ret, sizeof(ret));
+
+ return (ret);
+}
+
+int
+priv_script_go(void)
+{
+ char *scriptName, *argv[2], **envp, *epp[3], reason[] = "REASON=NBI";
+ static char client_path[] = CLIENT_PATH;
+ struct interface_info *ip = ifi;
+ int pid, wpid, wstatus;
+
+ scripttime = time(NULL);
+
+ if (ip) {
+ scriptName = ip->client->config->script_name;
+ envp = ip->client->scriptEnv;
+ } else {
+ scriptName = top_level_config.script_name;
+ epp[0] = reason;
+ epp[1] = client_path;
+ epp[2] = NULL;
+ envp = epp;
+ }
+
+ argv[0] = scriptName;
+ argv[1] = NULL;
+
+ pid = fork();
+ if (pid < 0) {
+ error("fork: %m");
+ wstatus = 0;
+ } else if (pid) {
+ do {
+ wpid = wait(&wstatus);
+ } while (wpid != pid && wpid > 0);
+ if (wpid < 0) {
+ error("wait: %m");
+ wstatus = 0;
+ }
+ } else {
+ execve(scriptName, argv, envp);
+ error("execve (%s, ...): %m", scriptName);
+ }
+
+ if (ip)
+ script_flush_env(ip->client);
+
+ return (wstatus & 0xff);
+}
+
+void
+script_set_env(struct client_state *client, const char *prefix,
+ const char *name, const char *value)
+{
+ int i, j, namelen;
+
+ namelen = strlen(name);
+
+ for (i = 0; client->scriptEnv[i]; i++)
+ if (strncmp(client->scriptEnv[i], name, namelen) == 0 &&
+ client->scriptEnv[i][namelen] == '=')
+ break;
+
+ if (client->scriptEnv[i])
+ /* Reuse the slot. */
+ free(client->scriptEnv[i]);
+ else {
+ /* New variable. Expand if necessary. */
+ if (i >= client->scriptEnvsize - 1) {
+ char **newscriptEnv;
+ int newscriptEnvsize = client->scriptEnvsize + 50;
+
+ newscriptEnv = realloc(client->scriptEnv,
+ newscriptEnvsize);
+ if (newscriptEnv == NULL) {
+ free(client->scriptEnv);
+ client->scriptEnv = NULL;
+ client->scriptEnvsize = 0;
+ error("script_set_env: no memory for variable");
+ }
+ client->scriptEnv = newscriptEnv;
+ client->scriptEnvsize = newscriptEnvsize;
+ }
+ /* need to set the NULL pointer at end of array beyond
+ the new slot. */
+ client->scriptEnv[i + 1] = NULL;
+ }
+ /* Allocate space and format the variable in the appropriate slot. */
+ client->scriptEnv[i] = malloc(strlen(prefix) + strlen(name) + 1 +
+ strlen(value) + 1);
+ if (client->scriptEnv[i] == NULL)
+ error("script_set_env: no memory for variable assignment");
+
+ /* No `` or $() command substitution allowed in environment values! */
+ for (j=0; j < strlen(value); j++)
+ switch (value[j]) {
+ case '`':
+ case '$':
+ error("illegal character (%c) in value '%s'", value[j],
+ value);
+ /* not reached */
+ }
+ snprintf(client->scriptEnv[i], strlen(prefix) + strlen(name) +
+ 1 + strlen(value) + 1, "%s%s=%s", prefix, name, value);
+}
+
+void
+script_flush_env(struct client_state *client)
+{
+ int i;
+
+ for (i = 0; client->scriptEnv[i]; i++) {
+ free(client->scriptEnv[i]);
+ client->scriptEnv[i] = NULL;
+ }
+ client->scriptEnvsize = 0;
+}
+
+int
+dhcp_option_ev_name(char *buf, size_t buflen, struct option *option)
+{
+ int i;
+
+ for (i = 0; option->name[i]; i++) {
+ if (i + 1 == buflen)
+ return 0;
+ if (option->name[i] == '-')
+ buf[i] = '_';
+ else
+ buf[i] = option->name[i];
+ }
+
+ buf[i] = 0;
+ return 1;
+}
+
+void
+go_daemon(void)
+{
+ static int state = 0;
+
+ if (no_daemon || state)
+ return;
+
+ state = 1;
+
+ /* Stop logging to stderr... */
+ log_perror = 0;
+
+ if (daemon(1, 0) == -1)
+ error("daemon");
+
+ /* we are chrooted, daemon(3) fails to open /dev/null */
+ if (nullfd != -1) {
+ dup2(nullfd, STDIN_FILENO);
+ dup2(nullfd, STDOUT_FILENO);
+ dup2(nullfd, STDERR_FILENO);
+ close(nullfd);
+ nullfd = -1;
+ }
+}
+
+int
+check_option(struct client_lease *l, int option)
+{
+ char *opbuf;
+ char *sbuf;
+
+ /* we use this, since this is what gets passed to dhclient-script */
+
+ opbuf = pretty_print_option(option, l->options[option].data,
+ l->options[option].len, 0, 0);
+
+ sbuf = option_as_string(option, l->options[option].data,
+ l->options[option].len);
+
+ switch (option) {
+ case DHO_SUBNET_MASK:
+ case DHO_TIME_SERVERS:
+ case DHO_NAME_SERVERS:
+ case DHO_ROUTERS:
+ case DHO_DOMAIN_NAME_SERVERS:
+ case DHO_LOG_SERVERS:
+ case DHO_COOKIE_SERVERS:
+ case DHO_LPR_SERVERS:
+ case DHO_IMPRESS_SERVERS:
+ case DHO_RESOURCE_LOCATION_SERVERS:
+ case DHO_SWAP_SERVER:
+ case DHO_BROADCAST_ADDRESS:
+ case DHO_NIS_SERVERS:
+ case DHO_NTP_SERVERS:
+ case DHO_NETBIOS_NAME_SERVERS:
+ case DHO_NETBIOS_DD_SERVER:
+ case DHO_FONT_SERVERS:
+ case DHO_DHCP_SERVER_IDENTIFIER:
+ case DHO_SMTP_SERVER:
+ case DHO_POP_SERVER:
+ case DHO_NNTP_SERVER:
+ case DHO_WWW_SERVER:
+ case DHO_FINGER_SERVER:
+ case DHO_IRC_SERVER:
+ if (!ipv4addrs(opbuf)) {
+ warning("Invalid IP address in option: %s", opbuf);
+ return (0);
+ }
+ return (1) ;
+ case DHO_HOST_NAME:
+ case DHO_NIS_DOMAIN:
+ if (!res_hnok(sbuf)) {
+ warning("Bogus Host Name option %d: %s (%s)", option,
+ sbuf, opbuf);
+ l->options[option].len = 0;
+ free(l->options[option].data);
+ return (0);
+ }
+ return (1);
+ case DHO_DOMAIN_NAME:
+ if (!res_hnok(sbuf)) {
+ if (!check_search(sbuf)) {
+ warning("Bogus domain search list %d: %s (%s)",
+ option, sbuf, opbuf);
+ l->options[option].len = 0;
+ free(l->options[option].data);
+ }
+ }
+ return (1);
+ case DHO_PAD:
+ case DHO_TIME_OFFSET:
+ case DHO_BOOT_SIZE:
+ case DHO_MERIT_DUMP:
+ case DHO_ROOT_PATH:
+ case DHO_EXTENSIONS_PATH:
+ case DHO_IP_FORWARDING:
+ case DHO_NON_LOCAL_SOURCE_ROUTING:
+ case DHO_POLICY_FILTER:
+ case DHO_MAX_DGRAM_REASSEMBLY:
+ case DHO_DEFAULT_IP_TTL:
+ case DHO_PATH_MTU_AGING_TIMEOUT:
+ case DHO_PATH_MTU_PLATEAU_TABLE:
+ case DHO_INTERFACE_MTU:
+ case DHO_ALL_SUBNETS_LOCAL:
+ case DHO_PERFORM_MASK_DISCOVERY:
+ case DHO_MASK_SUPPLIER:
+ case DHO_ROUTER_DISCOVERY:
+ case DHO_ROUTER_SOLICITATION_ADDRESS:
+ case DHO_STATIC_ROUTES:
+ case DHO_TRAILER_ENCAPSULATION:
+ case DHO_ARP_CACHE_TIMEOUT:
+ case DHO_IEEE802_3_ENCAPSULATION:
+ case DHO_DEFAULT_TCP_TTL:
+ case DHO_TCP_KEEPALIVE_INTERVAL:
+ case DHO_TCP_KEEPALIVE_GARBAGE:
+ case DHO_VENDOR_ENCAPSULATED_OPTIONS:
+ case DHO_NETBIOS_NODE_TYPE:
+ case DHO_NETBIOS_SCOPE:
+ case DHO_X_DISPLAY_MANAGER:
+ case DHO_DHCP_REQUESTED_ADDRESS:
+ case DHO_DHCP_LEASE_TIME:
+ case DHO_DHCP_OPTION_OVERLOAD:
+ case DHO_DHCP_MESSAGE_TYPE:
+ case DHO_DHCP_PARAMETER_REQUEST_LIST:
+ case DHO_DHCP_MESSAGE:
+ case DHO_DHCP_MAX_MESSAGE_SIZE:
+ case DHO_DHCP_RENEWAL_TIME:
+ case DHO_DHCP_REBINDING_TIME:
+ case DHO_DHCP_CLASS_IDENTIFIER:
+ case DHO_DHCP_CLIENT_IDENTIFIER:
+ case DHO_DHCP_USER_CLASS_ID:
+ case DHO_END:
+ return (1);
+ default:
+ warning("unknown dhcp option value 0x%x", option);
+ return (unknown_ok);
+ }
+}
+
+int
+res_hnok(const char *dn)
+{
+ int pch = PERIOD, ch = *dn++;
+
+ while (ch != '\0') {
+ int nch = *dn++;
+
+ if (periodchar(ch)) {
+ ;
+ } else if (periodchar(pch)) {
+ if (!borderchar(ch))
+ return (0);
+ } else if (periodchar(nch) || nch == '\0') {
+ if (!borderchar(ch))
+ return (0);
+ } else {
+ if (!middlechar(ch))
+ return (0);
+ }
+ pch = ch, ch = nch;
+ }
+ return (1);
+}
+
+int
+check_search(const char *srch)
+{
+ int pch = PERIOD, ch = *srch++;
+ int domains = 1;
+
+ /* 256 char limit re resolv.conf(5) */
+ if (strlen(srch) > 256)
+ return (0);
+
+ while (whitechar(ch))
+ ch = *srch++;
+
+ while (ch != '\0') {
+ int nch = *srch++;
+
+ if (periodchar(ch) || whitechar(ch)) {
+ ;
+ } else if (periodchar(pch)) {
+ if (!borderchar(ch))
+ return (0);
+ } else if (periodchar(nch) || nch == '\0') {
+ if (!borderchar(ch))
+ return (0);
+ } else {
+ if (!middlechar(ch))
+ return (0);
+ }
+ if (!whitechar(ch)) {
+ pch = ch;
+ } else {
+ while (whitechar(nch)) {
+ nch = *srch++;
+ }
+ if (nch != '\0')
+ domains++;
+ pch = PERIOD;
+ }
+ ch = nch;
+ }
+ /* 6 domain limit re resolv.conf(5) */
+ if (domains > 6)
+ return (0);
+ return (1);
+}
+
+/* Does buf consist only of dotted decimal ipv4 addrs?
+ * return how many if so,
+ * otherwise, return 0
+ */
+int
+ipv4addrs(char * buf)
+{
+ struct in_addr jnk;
+ int count = 0;
+
+ while (inet_aton(buf, &jnk) == 1){
+ count++;
+ while (periodchar(*buf) || digitchar(*buf))
+ buf++;
+ if (*buf == '\0')
+ return (count);
+ while (*buf == ' ')
+ buf++;
+ }
+ return (0);
+}
+
+
+char *
+option_as_string(unsigned int code, unsigned char *data, int len)
+{
+ static char optbuf[32768]; /* XXX */
+ char *op = optbuf;
+ int opleft = sizeof(optbuf);
+ unsigned char *dp = data;
+
+ if (code > 255)
+ error("option_as_string: bad code %d", code);
+
+ for (; dp < data + len; dp++) {
+ if (!isascii(*dp) || !isprint(*dp)) {
+ if (dp + 1 != data + len || *dp != 0) {
+ snprintf(op, opleft, "\\%03o", *dp);
+ op += 4;
+ opleft -= 4;
+ }
+ } else if (*dp == '"' || *dp == '\'' || *dp == '$' ||
+ *dp == '`' || *dp == '\\') {
+ *op++ = '\\';
+ *op++ = *dp;
+ opleft -= 2;
+ } else {
+ *op++ = *dp;
+ opleft--;
+ }
+ }
+ if (opleft < 1)
+ goto toobig;
+ *op = 0;
+ return optbuf;
+toobig:
+ warning("dhcp option too large");
+ return "<error>";
+}
+
+int
+fork_privchld(int fd, int fd2)
+{
+ struct pollfd pfd[1];
+ int nfds;
+
+ switch (fork()) {
+ case -1:
+ error("cannot fork");
+ case 0:
+ break;
+ default:
+ return (0);
+ }
+
+ setproctitle("%s [priv]", ifi->name);
+
+ dup2(nullfd, STDIN_FILENO);
+ dup2(nullfd, STDOUT_FILENO);
+ dup2(nullfd, STDERR_FILENO);
+ close(nullfd);
+ close(fd2);
+
+ for (;;) {
+ pfd[0].fd = fd;
+ pfd[0].events = POLLIN;
+ if ((nfds = poll(pfd, 1, INFTIM)) == -1)
+ if (errno != EINTR)
+ error("poll error");
+
+ if (nfds == 0 || !(pfd[0].revents & POLLIN))
+ continue;
+
+ dispatch_imsg(fd);
+ }
+}
diff --git a/sbin/dhclient/dhclient.conf b/sbin/dhclient/dhclient.conf
new file mode 100644
index 0000000..147e004
--- /dev/null
+++ b/sbin/dhclient/dhclient.conf
@@ -0,0 +1,36 @@
+send host-name "andare.fugue.com";
+send dhcp-client-identifier 1:0:a0:24:ab:fb:9c;
+send dhcp-lease-time 3600;
+supersede domain-name "fugue.com home.vix.com";
+prepend domain-name-servers 127.0.0.1;
+request subnet-mask, broadcast-address, time-offset, routers,
+ domain-name, domain-name-servers, host-name;
+require subnet-mask, domain-name-servers;
+timeout 60;
+retry 60;
+reboot 10;
+select-timeout 5;
+initial-interval 2;
+script "/etc/dhclient-script";
+media "-link0 -link1 -link2", "link0 link1";
+reject 192.33.137.209;
+
+alias {
+ interface "ep0";
+ fixed-address 192.5.5.213;
+ option subnet-mask 255.255.255.255;
+}
+
+lease {
+ interface "ep0";
+ fixed-address 192.33.137.200;
+ medium "link0 link1";
+ option host-name "andare.swiftmedia.com";
+ option subnet-mask 255.255.255.0;
+ option broadcast-address 192.33.137.255;
+ option routers 192.33.137.250;
+ option domain-name-servers 127.0.0.1;
+ renew 2 2000/1/12 00:00:01;
+ rebind 2 2000/1/12 00:00:01;
+ expire 2 2000/1/12 00:00:01;
+}
diff --git a/sbin/dhclient/dhclient.conf.5 b/sbin/dhclient/dhclient.conf.5
new file mode 100644
index 0000000..167239e
--- /dev/null
+++ b/sbin/dhclient/dhclient.conf.5
@@ -0,0 +1,544 @@
+.\" $OpenBSD: dhclient.conf.5,v 1.5 2004/11/01 23:10:18 henning Exp $
+.\"
+.\" Copyright (c) 1997 The Internet Software Consortium.
+.\" 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+.\" by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+.\" Enterprises. To learn more about the Internet Software Consortium,
+.\" see ``http://www.isc.org/isc''. To learn more about Vixie
+.\" Enterprises, see ``http://www.vix.com''.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 1, 1997
+.Dt DHCLIENT.CONF 5
+.Os
+.Sh NAME
+.Nm dhclient.conf
+.Nd DHCP client configuration file
+.Sh DESCRIPTION
+The
+.Nm
+file contains configuration information for
+.Xr dhclient 8 ,
+the Internet Software Consortium DHCP Client.
+.Pp
+The
+.Nm
+file is a free-form ASCII text file.
+It is parsed by the recursive-descent parser built into
+.Xr dhclient 8 .
+The file may contain extra tabs and newlines for formatting purposes.
+Keywords in the file are case-insensitive.
+Comments may be placed anywhere within the file (except within quotes).
+Comments begin with the
+.Ql #
+character and end at the end of the line.
+.Pp
+The
+.Nm
+file can be used to configure the behaviour of the client in a wide variety
+of ways: protocol timing, information requested from the server, information
+required of the server, defaults to use if the server does not provide
+certain information, values with which to override information provided by
+the server, or values to prepend or append to information provided by the
+server.
+The configuration file can also be preinitialized with addresses to
+use on networks that do not have DHCP servers.
+.Sh PROTOCOL TIMING
+The timing behaviour of the client need not be configured by the user.
+If no timing configuration is provided by the user, a fairly
+reasonable timing behaviour will be used by default - one which
+results in fairly timely updates without placing an inordinate load on
+the server.
+.Pp
+The following statements can be used to adjust the timing behaviour of
+the DHCP client if required, however:
+.Bl -tag -width indent
+.It Ic timeout Ar time ;
+The
+.Ic timeout
+statement determines the amount of time that must pass between the
+time that the client begins to try to determine its address and the
+time that it decides that it is not going to be able to contact a server.
+By default, this timeout is sixty seconds.
+After the timeout has passed, if there are any static leases defined in the
+configuration file, or any leases remaining in the lease database that
+have not yet expired, the client will loop through these leases
+attempting to validate them, and if it finds one that appears to be
+valid, it will use that lease's address.
+If there are no valid static leases or unexpired leases in the lease database,
+the client will restart the protocol after the defined retry interval.
+.It Ic retry Ar time ;
+The
+.Ic retry
+statement determines the time that must pass after the client has
+determined that there is no DHCP server present before it tries again
+to contact a DHCP server.
+By default, this is five minutes.
+.It Ic select-timeout Ar time ;
+It is possible (some might say desirable) for there to be more than
+one DHCP server serving any given network.
+In this case, it is possible that a client may be sent more than one offer
+in response to its initial lease discovery message.
+It may be that one of these offers is preferable to the other
+(e.g., one offer may have the address the client previously used,
+and the other may not).
+.Pp
+The
+.Ic select-timeout
+is the time after the client sends its first lease discovery request
+at which it stops waiting for offers from servers, assuming that it
+has received at least one such offer.
+If no offers have been received by the time the
+.Ic select-timeout
+has expired, the client will accept the first offer that arrives.
+.Pp
+By default, the
+.Ic select-timeout
+is zero seconds - that is, the client will take the first offer it sees.
+.It Ic reboot Ar time ;
+When the client is restarted, it first tries to reacquire the last
+address it had.
+This is called the INIT-REBOOT state.
+If it is still attached to the same network it was attached to when it last
+ran, this is the quickest way to get started.
+The
+.Ic reboot
+statement sets the time that must elapse after the client first tries
+to reacquire its old address before it gives up and tries to discover
+a new address.
+By default, the reboot timeout is ten seconds.
+.It Ic backoff-cutoff Ar time ;
+The client uses an exponential backoff algorithm with some randomness,
+so that if many clients try to configure themselves at the same time,
+they will not make their requests in lockstep.
+The
+.Ic backoff-cutoff
+statement determines the maximum amount of time that the client is
+allowed to back off.
+It defaults to two minutes.
+.It Ic initial-interval Ar time ;
+The
+.Ic initial-interval
+statement sets the amount of time between the first attempt to reach a
+server and the second attempt to reach a server.
+Each time a message is sent, the interval between messages is incremented by
+twice the current interval multiplied by a random number between zero and one.
+If it is greater than the
+.Ic backoff-cutoff
+amount, it is set to that
+amount.
+It defaults to ten seconds.
+.El
+.Sh LEASE REQUIREMENTS AND REQUESTS
+The DHCP protocol allows the client to request that the server send it
+specific information, and not send it other information that it is not
+prepared to accept.
+The protocol also allows the client to reject offers from servers if they
+do not contain information the client needs, or if the information provided
+is not satisfactory.
+.Pp
+There is a variety of data contained in offers that DHCP servers send
+to DHCP clients.
+The data that can be specifically requested is what are called
+.Em DHCP Options .
+DHCP Options are defined in
+.Xr dhcp-options 5 .
+.Bl -tag -width indent
+.It Ic request Oo Ar option Oc Oo , Ar ... option Oc ;
+The
+.Ic request
+statement causes the client to request that any server responding to the
+client send the client its values for the specified options.
+Only the option names should be specified in the request statement - not
+option parameters.
+.It Ic require Oo Ar option Oc Oo , Ar ... option Oc ;
+The
+.Ic require
+statement lists options that must be sent in order for an offer to be accepted.
+Offers that do not contain all the listed options will be ignored.
+.It Ic send No { Oo Ar option declaration Oc Oo , Ar ... option declaration Oc }
+The
+.Ic send
+statement causes the client to send the specified options to the server with
+the specified values.
+These are full option declarations as described in
+.Xr dhcp-options 5 .
+Options that are always sent in the DHCP protocol should not be specified
+here, except that the client can specify a
+.Ar dhcp-lease-time
+option other than the default requested lease time, which is two hours.
+The other obvious use for this statement is to send information to the server
+that will allow it to differentiate between this client and other
+clients or kinds of clients.
+.El
+.Sh OPTION MODIFIERS
+In some cases, a client may receive option data from the server which
+is not really appropriate for that client, or may not receive
+information that it needs, and for which a useful default value exists.
+It may also receive information which is useful, but which needs to be
+supplemented with local information.
+To handle these needs, several option modifiers are available.
+.Bl -tag -width indent
+.It Xo
+.Ic default No { Op Ar option declaration
+.Oo , Ar ... option declaration Oc }
+.Xc
+If for some set of options the client should use the value supplied by
+the server, but needs to use some default value if no value was supplied
+by the server, these values can be defined in the
+.Ic default
+statement.
+.It Xo
+.Ic supersede No { Op Ar option declaration
+.Oo , Ar ... option declaration Oc }
+.Xc
+If for some set of options the client should always use its own value
+rather than any value supplied by the server, these values can be defined
+in the
+.Ic supersede
+statement.
+.It Xo
+.Ic prepend No { Op Ar option declaration
+.Oo , Ar ... option declaration Oc }
+.Xc
+If for some set of options the client should use a value you supply,
+and then use the values supplied by the server, if any,
+these values can be defined in the
+.Ic prepend
+statement.
+The
+.Ic prepend
+statement can only be used for options which allow more than one value to
+be given.
+This restriction is not enforced - if violated, the results are unpredictable.
+.It Xo
+.Ic append No { Op Ar option declaration
+.Oo , Ar ... option declaration Oc }
+.Xc
+If for some set of options the client should first use the values
+supplied by the server, if any, and then use values you supply, these
+values can be defined in the
+.Ic append
+statement.
+The
+.Ic append
+statement can only be used for options which allow more than one value to
+be given.
+This restriction is not enforced - if you ignore it,
+the behaviour will be unpredictable.
+.El
+.Sh LEASE DECLARATIONS
+The lease declaration:
+.Pp
+.D1 Ic lease No { Ar lease-declaration Oo Ar ... lease-declaration Oc }
+.Pp
+The DHCP client may decide after some period of time (see
+.Sx PROTOCOL TIMING )
+that it is not going to succeed in contacting a server.
+At that time, it consults its own database of old leases and tests each one
+that has not yet timed out by pinging the listed router for that lease to
+see if that lease could work.
+It is possible to define one or more
+.Em fixed
+leases in the client configuration file for networks where there is no DHCP
+or BOOTP service, so that the client can still automatically configure its
+address.
+This is done with the
+.Ic lease
+statement.
+.Pp
+NOTE: the lease statement is also used in the
+.Pa dhclient.leases
+file in order to record leases that have been received from DHCP servers.
+Some of the syntax for leases as described below is only needed in the
+.Pa dhclient.leases
+file.
+Such syntax is documented here for completeness.
+.Pp
+A lease statement consists of the
+.Ic lease
+keyword, followed by a left
+curly brace, followed by one or more lease declaration statements,
+followed by a right curly brace.
+The following lease declarations are possible:
+.Bl -tag -width indent
+.It Ic bootp ;
+The
+.Ic bootp
+statement is used to indicate that the lease was acquired using the
+BOOTP protocol rather than the DHCP protocol.
+It is never necessary to specify this in the client configuration file.
+The client uses this syntax in its lease database file.
+.It Ic interface Qq Ar string ;
+The
+.Ic interface
+lease statement is used to indicate the interface on which the lease is valid.
+If set, this lease will only be tried on a particular interface.
+When the client receives a lease from a server, it always records the
+interface number on which it received that lease.
+If predefined leases are specified in the
+.Nm
+file, the interface should also be specified, although this is not required.
+.It Ic fixed-address Ar ip-address ;
+The
+.Ic fixed-address
+statement is used to set the IP address of a particular lease.
+This is required for all lease statements.
+The IP address must be specified as a dotted quad (e.g.,
+.Li 12.34.56.78 ) .
+.It Ic filename Qq Ar string ;
+The
+.Ic filename
+statement specifies the name of the boot filename to use.
+This is not used by the standard client configuration script, but is
+included for completeness.
+.It Ic server-name Qq Ar string ;
+The
+.Ic server-name
+statement specifies the name of the boot server name to use.
+This is also not used by the standard client configuration script.
+.It Ic option Ar option-declaration ;
+The
+.Ic option
+statement is used to specify the value of an option supplied by the server,
+or, in the case of predefined leases declared in
+.Nm ,
+the value that the user wishes the client configuration script to use if the
+predefined lease is used.
+.It Ic script Qq Ar script-name ;
+The
+.Ic script
+statement is used to specify the pathname of the DHCP client configuration
+script.
+This script is used by the DHCP client to set each interface's initial
+configuration prior to requesting an address, to test the address once it
+has been offered, and to set the interface's final configuration once a
+lease has been acquired.
+If no lease is acquired, the script is used to test predefined leases, if
+any, and also called once if no valid lease can be identified.
+For more information, see
+.Xr dhclient.leases 5 .
+.It Ic medium Qq Ar "media setup" ;
+The
+.Ic medium
+statement can be used on systems where network interfaces cannot
+automatically determine the type of network to which they are connected.
+The
+.Ar "media setup"
+string is a system-dependent parameter which is passed
+to the DHCP client configuration script when initializing the interface.
+On
+.Ux
+and
+.Ux Ns -like
+systems, the argument is passed on the
+.Xr ifconfig 8
+command line
+when configuring the interface.
+.Pp
+The DHCP client automatically declares this parameter if it used a
+media type (see the
+.Ic media
+statement) when configuring the interface in order to obtain a lease.
+This statement should be used in predefined leases only if the network
+interface requires media type configuration.
+.It Ic renew Ar date ;
+.It Ic rebind Ar date ;
+.It Ic expire Ar date ;
+The
+.Ic renew
+statement defines the time at which the DHCP client should begin trying to
+contact its server to renew a lease that it is using.
+The
+.Ic rebind
+statement defines the time at which the DHCP client should begin to try to
+contact
+.Em any
+DHCP server in order to renew its lease.
+The
+.Ic expire
+statement defines the time at which the DHCP client must stop using a lease
+if it has not been able to contact a server in order to renew it.
+.El
+.Pp
+These declarations are automatically set in leases acquired by the
+DHCP client, but must also be configured in predefined leases - a
+predefined lease whose expiry time has passed will not be used by the
+DHCP client.
+.Pp
+Dates are specified as follows:
+.Bd -ragged -offset indent
+.Ar <weekday>
+.Sm off
+.Ar <year> No / Ar <month> No / Ar <day>
+.Ar <hour> : <minute> : <second>
+.Sm on
+.Ed
+.Pp
+The weekday is present to make it easy for a human to tell when a
+lease expires - it is specified as a number from zero to six, with zero
+being Sunday.
+When declaring a predefined lease, it can always be specified as zero.
+The year is specified with the century, so it should generally be four
+digits except for really long leases.
+The month is specified as a number starting with 1 for January.
+The day of the month is likewise specified starting with 1.
+The hour is a number between 0 and 23,
+the minute a number between 0 and 59,
+and the second also a number between 0 and 59.
+.Sh ALIAS DECLARATIONS
+.Ic alias No { Ar declarations ... No }
+.Pp
+Some DHCP clients running TCP/IP roaming protocols may require that in
+addition to the lease they may acquire via DHCP, their interface also
+be configured with a predefined IP alias so that they can have a
+permanent IP address even while roaming.
+The Internet Software Consortium DHCP client does not support roaming with
+fixed addresses directly, but in order to facilitate such experimentation,
+the DHCP client can be set up to configure an IP alias using the
+.Ic alias
+declaration.
+.Pp
+The
+.Ic alias
+declaration resembles a lease declaration, except that options other than
+the subnet-mask option are ignored by the standard client configuration
+script, and expiry times are ignored.
+A typical alias declaration includes an interface declaration, a fixed-address
+declaration for the IP alias address, and a subnet-mask option declaration.
+A medium statement should never be included in an alias declaration.
+.Sh OTHER DECLARATIONS
+.Bl -tag -width indent
+.It Ic reject Ar ip-address ;
+The
+.Ic reject
+statement causes the DHCP client to reject offers from servers who use
+the specified address as a server identifier.
+This can be used to avoid being configured by rogue or misconfigured DHCP
+servers, although it should be a last resort - better to track down
+the bad DHCP server and fix it.
+.It Ic interface Qo Ar name Qc { Ar declarations ... No }
+A client with more than one network interface may require different
+behaviour depending on which interface is being configured.
+All timing parameters and declarations other than lease and alias
+declarations can be enclosed in an interface declaration, and those
+parameters will then be used only for the interface that matches the
+specified name.
+Interfaces for which there is no interface declaration will use the
+parameters declared outside of any interface declaration,
+or the default settings.
+.It Ic media Qo Ar "media setup" Qc Oo , Qo Ar "media setup" Qc , Ar ... Oc ;
+The
+.Ic media
+statement defines one or more media configuration parameters which may
+be tried while attempting to acquire an IP address.
+The DHCP client will cycle through each media setup string on the list,
+configuring the interface using that setup and attempting to boot,
+and then trying the next one.
+This can be used for network interfaces which are not capable of sensing
+the media type unaided - whichever media type succeeds in getting a request
+to the server and hearing the reply is probably right (no guarantees).
+.Pp
+The media setup is only used for the initial phase of address
+acquisition (the DHCPDISCOVER and DHCPOFFER packets).
+Once an address has been acquired, the DHCP client will record it in its
+lease database and will record the media type used to acquire the address.
+Whenever the client tries to renew the lease, it will use that same media type.
+The lease must expire before the client will go back to cycling through media
+types.
+.El
+.Sh EXAMPLES
+The following configuration file is used on a laptop
+which has an IP alias of
+.Li 192.5.5.213 ,
+and has one interface,
+.Li ep0
+(a 3Com 3C589C).
+Booting intervals have been shortened somewhat from the default, because
+the client is known to spend most of its time on networks with little DHCP
+activity.
+The laptop does roam to multiple networks.
+.Bd -literal -offset indent
+timeout 60;
+retry 60;
+reboot 10;
+select-timeout 5;
+initial-interval 2;
+reject 192.33.137.209;
+
+interface "ep0" {
+ send host-name "andare.fugue.com";
+ send dhcp-client-identifier 1:0:a0:24:ab:fb:9c;
+ send dhcp-lease-time 3600;
+ supersede domain-name "fugue.com rc.vix.com home.vix.com";
+ prepend domain-name-servers 127.0.0.1;
+ request subnet-mask, broadcast-address, time-offset, routers,
+ domain-name, domain-name-servers, host-name;
+ require subnet-mask, domain-name-servers;
+ script "/etc/dhclient-script";
+ media "media 10baseT/UTP", "media 10base2/BNC";
+}
+
+alias {
+ interface "ep0";
+ fixed-address 192.5.5.213;
+ option subnet-mask 255.255.255.255;
+}
+.Ed
+.Pp
+This is a very complicated
+.Nm
+file - in general, yours should be much simpler.
+In many cases, it is sufficient to just create an empty
+.Nm
+file - the defaults are usually fine.
+.Sh SEE ALSO
+.Xr dhclient.leases 5 ,
+.Xr dhcpd.conf 5 ,
+.Xr dhcp-options 5 ,
+.Xr dhclient 8 ,
+.Xr dhcpd 8
+.Rs
+.%R "RFC 2132, RFC 2131"
+.Re
+.Sh AUTHORS
+.An -nosplit
+The
+.Xr dhclient 8
+utility
+was written by
+.An Ted Lemon Aq mellon@vix.com
+under a contract with Vixie Labs.
+.Pp
+The current implementation was reworked by
+.An Henning Brauer Aq henning@openbsd.org .
diff --git a/sbin/dhclient/dhclient.leases.5 b/sbin/dhclient/dhclient.leases.5
new file mode 100644
index 0000000..b1f0f3d
--- /dev/null
+++ b/sbin/dhclient/dhclient.leases.5
@@ -0,0 +1,95 @@
+.\" $OpenBSD: dhclient.leases.5,v 1.4 2004/04/15 08:59:47 jmc Exp $
+.\"
+.\" Copyright (c) 1997 The Internet Software Consortium.
+.\" 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+.\" by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+.\" Enterprises. To learn more about the Internet Software Consortium,
+.\" see ``http://www.isc.org/isc''. To learn more about Vixie
+.\" Enterprises, see ``http://www.vix.com''.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 1, 1997
+.Dt DHCLIENT.LEASES 5
+.Os
+.Sh NAME
+.Nm dhclient.leases
+.Nd DHCP client lease database
+.Sh DESCRIPTION
+The Internet Software Consortium DHCP client keeps a persistent
+database of leases that it has acquired that are still valid.
+The database is a free-form ASCII file containing one valid declaration
+per lease.
+If more than one declaration appears for a given lease,
+the last one in the file is used.
+The file is written as a log, so this is not an unusual occurrence.
+.Pp
+The lease file is named
+.Pa dhclient.leases. Ns Ar IFNAME ,
+where
+.Ar IFNAME
+represents the network interface the DHCP client acquired the lease on.
+For example, if
+.Xr dhclient 8
+is configured for the
+.Li em0
+network device,
+the lease file will be named
+.Pa dhclient.leases.em0 .
+.Pp
+The format of the lease declarations is described in
+.Xr dhclient.conf 5 .
+.Sh FILES
+.Bl -tag -width ".Pa /var/db/dhclient.leases. Ns Ar IFNAME"
+.It Pa /var/db/dhclient.leases. Ns Ar IFNAME
+Current lease file.
+.El
+.Sh SEE ALSO
+.Xr dhclient.conf 5 ,
+.Xr dhcpd.conf 5 ,
+.Xr dhcp-options 5 ,
+.Xr dhclient 8 ,
+.Xr dhcpd 8
+.Rs
+.%R "RFC 2132, RFC 2131"
+.Re
+.Sh AUTHORS
+.An -nosplit
+The
+.Xr dhclient 8
+utility
+was written by
+.An Ted Lemon Aq mellon@vix.com
+under a contract with Vixie Labs.
+.Pp
+The current implementation was reworked by
+.An Henning Brauer Aq henning@openbsd.org .
diff --git a/sbin/dhclient/dhcp-options.5 b/sbin/dhclient/dhcp-options.5
new file mode 100644
index 0000000..35b47e6
--- /dev/null
+++ b/sbin/dhclient/dhcp-options.5
@@ -0,0 +1,606 @@
+.\" $OpenBSD: dhcp-options.5,v 1.5 2005/03/02 15:30:42 jmc Exp $
+.\"
+.\" Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+.\" 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+.\" by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+.\" Enterprises. To learn more about the Internet Software Consortium,
+.\" see ``http://www.isc.org/isc''. To learn more about Vixie
+.\" Enterprises, see ``http://www.vix.com''.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 1, 1995
+.Dt DHCP-OPTIONS 5
+.Os
+.Sh NAME
+.Nm dhcp-options
+.Nd Dynamic Host Configuration Protocol options
+.Sh DESCRIPTION
+The Dynamic Host Configuration protocol allows the client to receive
+.Ic options
+from the DHCP server describing the network configuration and various
+services that are available on the network.
+When configuring
+.Xr dhcpd 8
+or
+.Xr dhclient 8 ,
+options must often be declared.
+The syntax for declaring options, and the names and formats of the options
+that can be declared, are documented here.
+.Sh REFERENCE: OPTION STATEMENTS
+DHCP
+.Ic option
+statements always start with the
+.Ic option
+keyword, followed by an option name, followed by option data.
+The option names and data formats are described below.
+It is not necessary to exhaustively specify all DHCP options -
+only those options which are needed by clients must be specified.
+.Pp
+Option data comes in a variety of formats, as defined below:
+.Pp
+The
+.Ar ip-address
+data type can be entered either as an explicit IP address
+(e.g.,
+.Li 239.254.197.10 )
+or as a domain name (e.g.,
+.Li haagen.isc.org ) .
+A domain name must resolve to a single IP address.
+.Pp
+The
+.Ar int32
+data type specifies a signed 32-bit integer.
+The
+.Ar uint32
+data type specifies an unsigned 32-bit integer.
+The
+.Ar int16
+and
+.Ar uint16
+data types specify signed and unsigned 16-bit integers.
+The
+.Ar int8
+and
+.Ar uint8
+data types specify signed and unsigned 8-bit integers.
+Unsigned 8-bit integers are also sometimes referred to as octets.
+.Pp
+The
+.Ar string
+data type specifies an
+.Tn NVT
+.Pq Network Virtual Terminal
+.Tn ASCII
+string, which must be enclosed in double quotes - for example,
+to specify a domain-name option, the syntax would be
+.Pp
+.Dl option domain-name \&"isc.org";
+.Pp
+The
+.Ar flag
+data type specifies a boolean value.
+Booleans can be either
+.Li true
+or
+.Li false
+(or
+.Li on
+or
+.Li off ,
+if that makes more sense to you).
+.Pp
+The
+.Ar data-string
+data type specifies either an
+.Tn NVT ASCII
+string enclosed in double quotes, or a series of octets specified in
+hexadecimal, separated by colons.
+For example:
+.Pp
+.Dl option dhcp-client-identifier \&"CLIENT-FOO";
+or
+.Dl option dhcp-client-identifier 43:4c:49:45:54:2d:46:4f:4f;
+.Pp
+The documentation for the various options mentioned below is taken
+from the IETF draft document on DHCP options, RFC 2132.
+Options which are not listed by name may be defined by the name
+.Li option- Ns Ar nnn ,
+where
+.Ar nnn
+is the decimal number of the option code.
+These options may be followed either by a string, enclosed in quotes, or by
+a series of octets, expressed as two-digit hexadecimal numbers separated
+by colons.
+For example:
+.Bd -literal -offset indent
+option option-133 "my-option-133-text";
+option option-129 1:54:c9:2b:47;
+.Ed
+.Pp
+Because
+.Xr dhcpd 8
+does not know the format of these undefined option codes,
+no checking is done to ensure the correctness of the entered data.
+.Pp
+The standard options are:
+.Ss RFC 1497 Vendor Extensions
+.Bl -tag -width indent
+.It Ic option subnet-mask Ar ip-address ;
+The
+.Ic subnet-mask
+option specifies the client's subnet mask as per RFC 950.
+If no subnet-mask option is provided anywhere in scope, as a last resort
+.Xr dhcpd 8
+will use the subnet mask from the subnet declaration for the network on
+which an address is being assigned.
+However,
+.Em any
+subnet-mask option declaration that is in scope for the address being
+assigned will override the subnet mask specified in the subnet declaration.
+.It Ic option time-offset Ar int32 ;
+The
+.Ic time-offset
+option specifies the offset of the client's subnet in seconds from
+Coordinated Universal Time (UTC).
+.It Xo
+.Ic option routers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic routers
+option specifies a list of IP addresses for routers on the client's subnet.
+Routers should be listed in order of preference.
+.It Xo
+.Ic option time-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic time-server
+option specifies a list of RFC 868 time servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option ien116-name-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic ien116-name-servers
+option specifies a list of IEN 116 name servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option domain-name-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic domain-name-servers
+option specifies a list of Domain Name System (STD 13, RFC 1035) name servers
+available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option log-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic log-servers
+option specifies a list of MIT-LCS UDP log servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option cookie-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic cookie-servers
+option specifies a list of RFC 865 cookie servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option lpr-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic lpr-servers
+option specifies a list of RFC 1179 line printer servers available to the
+client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option impress-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic impress-servers
+option specifies a list of Imagen Impress servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option resource-location-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+This option specifies a list of RFC 887 Resource Location servers available
+to the client.
+Servers should be listed in order of preference.
+.It Ic option host-name Ar string ;
+This option specifies the name of the client.
+The name may or may not be qualified with the local domain name
+(it is preferable to use the
+.Ic domain-name
+option to specify the domain name).
+See RFC 1035 for character set restrictions.
+.It Ic option boot-size Ar uint16 ;
+This option specifies the length in 512-octet blocks of the default
+boot image for the client.
+.It Ic option merit-dump Ar string ;
+This option specifies the pathname of a file to which the client's
+core image should be dumped in the event the client crashes.
+The path is formatted as a character string consisting of characters from
+the
+.Tn NVT ASCII
+character set.
+.It Ic option domain-name Ar string ;
+This option specifies the domain name that the client should use when
+resolving hostnames via the Domain Name System.
+.It Ic option swap-server Ar ip-address ;
+This specifies the IP address of the client's swap server.
+.It Ic option root-path Ar string ;
+This option specifies the pathname that contains the client's root disk.
+The path is formatted as a character string consisting of characters from
+the
+.Tn NVT ASCII
+character set.
+.El
+.Ss IP Layer Parameters per Host
+.Bl -tag -width indent
+.It Ic option ip-forwarding Ar flag ;
+This option specifies whether the client should configure its IP layer
+for packet forwarding.
+A value of 0 means disable IP forwarding, and a value of 1 means enable
+IP forwarding.
+.It Ic option non-local-source-routing Ar flag ;
+This option specifies whether the client should configure its IP
+layer to allow forwarding of datagrams with non-local source routes
+(see Section 3.3.5 of [4] for a discussion of this topic).
+A value of 0 means disallow forwarding of such datagrams, and a value of 1
+means allow forwarding.
+.It Xo
+.Ic option policy-filter Ar ip-address ip-address
+.Oo , Ar ip-address ip-address ... Oc ;
+.Xc
+This option specifies policy filters for non-local source routing.
+The filters consist of a list of IP addresses and masks which specify
+destination/mask pairs with which to filter incoming source routes.
+.Pp
+Any source-routed datagram whose next-hop address does not match one
+of the filters should be discarded by the client.
+.Pp
+See STD 3 (RFC 1122) for further information.
+.It Ic option max-dgram-reassembly Ar uint16 ;
+This option specifies the maximum size datagram that the client should be
+prepared to reassemble.
+The minimum legal value is 576.
+.It Ic option default-ip-ttl Ar uint8 ;
+This option specifies the default time-to-live that the client should
+use on outgoing datagrams.
+.It Ic option path-mtu-aging-timeout Ar uint32 ;
+This option specifies the timeout (in seconds) to use when aging Path
+MTU values discovered by the mechanism defined in RFC 1191.
+.It Xo
+.Ic option path-mtu-plateau-table Ar uint16
+.Oo , Ar uint16 ... Oc ;
+.Xc
+This option specifies a table of MTU sizes to use when performing
+Path MTU Discovery as defined in RFC 1191.
+The table is formatted as a list of 16-bit unsigned integers,
+ordered from smallest to largest.
+The minimum MTU value cannot be smaller than 68.
+.El
+.Ss IP Layer Parameters per Interface
+.Bl -tag -width indent
+.It Ic option interface-mtu Ar uint16 ;
+This option specifies the MTU to use on this interface.
+The minimum legal value for the MTU is 68.
+.It Ic option all-subnets-local Ar flag ;
+This option specifies whether or not the client may assume that all subnets
+of the IP network to which the client is connected use the same MTU as the
+subnet of that network to which the client is directly connected.
+A value of 1 indicates that all subnets share the same MTU.
+A value of 0 means that the client should assume that some subnets of the
+directly connected network may have smaller MTUs.
+.It Ic option broadcast-address Ar ip-address ;
+This option specifies the broadcast address in use on the client's subnet.
+Legal values for broadcast addresses are specified in section 3.2.1.3 of
+STD 3 (RFC 1122).
+.It Ic option perform-mask-discovery Ar flag ;
+This option specifies whether or not the client should perform subnet mask
+discovery using ICMP.
+A value of 0 indicates that the client should not perform mask discovery.
+A value of 1 means that the client should perform mask discovery.
+.It Ic option mask-supplier Ar flag ;
+This option specifies whether or not the client should respond to subnet mask
+requests using ICMP.
+A value of 0 indicates that the client should not respond.
+A value of 1 means that the client should respond.
+.It Ic option router-discovery Ar flag ;
+This option specifies whether or not the client should solicit routers using
+the Router Discovery mechanism defined in RFC 1256.
+A value of 0 indicates that the client should not perform router discovery.
+A value of 1 means that the client should perform router discovery.
+.It Ic option router-solicitation-address Ar ip-address ;
+This option specifies the address to which the client should transmit
+router solicitation requests.
+.It Xo
+.Ic option static-routes Ar ip-address ip-address
+.Oo , Ar ip-address ip-address ... Oc ;
+.Xc
+This option specifies a list of static routes that the client should
+install in its routing cache.
+If multiple routes to the same destination are specified, they are listed
+in descending order of priority.
+.Pp
+The routes consist of a list of IP address pairs.
+The first address is the destination address,
+and the second address is the router for the destination.
+.Pp
+The default route (0.0.0.0) is an illegal destination for a static route.
+To specify the default route, use the
+.Ic routers
+option.
+.El
+.Ss Link Layer Parameters per Interface
+.Bl -tag -width indent
+.It Ic option trailer-encapsulation Ar flag ;
+This option specifies whether or not the client should negotiate the
+use of trailers (RFC 893 [14]) when using the ARP protocol.
+A value of 0 indicates that the client should not attempt to use trailers.
+A value of 1 means that the client should attempt to use trailers.
+.It Ic option arp-cache-timeout Ar uint32 ;
+This option specifies the timeout in seconds for ARP cache entries.
+.It Ic option ieee802-3-encapsulation Ar flag ;
+This option specifies whether or not the client should use Ethernet
+Version 2 (RFC 894) or IEEE 802.3 (RFC 1042) encapsulation if the
+interface is an Ethernet.
+A value of 0 indicates that the client should use RFC 894 encapsulation.
+A value of 1 means that the client should use RFC 1042 encapsulation.
+.El
+.Ss TCP Parameters
+.Bl -tag -width indent
+.It Ic option default-tcp-ttl Ar uint8 ;
+This option specifies the default TTL that the client should use when
+sending TCP segments.
+The minimum value is 1.
+.It Ic option tcp-keepalive-interval Ar uint32 ;
+This option specifies the interval (in seconds) that the client TCP
+should wait before sending a keepalive message on a TCP connection.
+The time is specified as a 32-bit unsigned integer.
+A value of zero indicates that the client should not generate keepalive
+messages on connections unless specifically requested by an application.
+.It Ic option tcp-keepalive-garbage Ar flag ;
+This option specifies whether or not the client should send TCP keepalive
+messages with an octet of garbage for compatibility with older implementations.
+A value of 0 indicates that a garbage octet should not be sent.
+A value of 1 indicates that a garbage octet should be sent.
+.El
+.Ss Application and Service Parameters
+.Bl -tag -width indent
+.It Ic option nis-domain Ar string ;
+This option specifies the name of the client's NIS (Sun Network Information
+Services) domain.
+The domain is formatted as a character string consisting of characters
+from the
+.Tn NVT ASCII
+character set.
+.It Xo
+.Ic option nis-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+This option specifies a list of IP addresses indicating NIS servers
+available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option ntp-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+This option specifies a list of IP addresses indicating NTP (RFC 1035)
+servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option netbios-name-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The NetBIOS name server (NBNS) option specifies a list of RFC 1001/1002
+NBNS name servers listed in order of preference.
+NetBIOS Name Service is currently more commonly referred to as WINS.
+WINS servers can be specified using the
+.Ic netbios-name-servers
+option.
+.It Xo
+.Ic option netbios-dd-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The NetBIOS datagram distribution server (NBDD) option specifies a
+list of RFC 1001/1002 NBDD servers listed in order of preference.
+.It Ic option netbios-node-type Ar uint8 ;
+The NetBIOS node type option allows NetBIOS over TCP/IP clients which
+are configurable to be configured as described in RFC 1001/1002.
+The value is specified as a single octet which identifies the client type.
+.Pp
+Possible node types are:
+.Bl -tag -width indent
+.It 1
+B-node: Broadcast - no WINS
+.It 2
+P-node: Peer - WINS only
+.It 4
+M-node: Mixed - broadcast, then WINS
+.It 8
+H-node: Hybrid - WINS, then broadcast
+.El
+.It Ic option netbios-scope Ar string ;
+The NetBIOS scope option specifies the NetBIOS over TCP/IP scope
+parameter for the client as specified in RFC 1001/1002.
+See RFC 1001, RFC 1002, and RFC 1035 for character-set restrictions.
+.It Xo
+.Ic option font-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+This option specifies a list of X Window System Font servers available
+to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option x-display-manager Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+This option specifies a list of systems that are running the X Window
+System Display Manager and are available to the client.
+Addresses should be listed in order of preference.
+.It Ic option dhcp-client-identifier Ar data-string ;
+This option can be used to specify a DHCP client identifier in a
+host declaration, so that
+.Xr dhcpd 8
+can find the host record by matching against the client identifier.
+.It Ic option nisplus-domain Ar string ;
+This option specifies the name of the client's NIS+ domain.
+The domain is formatted as a character string consisting of characters
+from the
+.Tn NVT ASCII
+character set.
+.It Xo
+.Ic option nisplus-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+This option specifies a list of IP addresses indicating NIS+ servers
+available to the client.
+Servers should be listed in order of preference.
+.It Ic option tftp-server-name Ar string ;
+This option is used to identify a TFTP server and, if supported by the
+client, should have the same effect as the
+.Ic server-name
+declaration.
+BOOTP clients are unlikely to support this option.
+Some DHCP clients will support it, and others actually require it.
+.It Ic option bootfile-name Ar string ;
+This option is used to identify a bootstrap file.
+If supported by the client, it should have the same effect as the
+.Ic filename
+declaration.
+BOOTP clients are unlikely to support this option.
+Some DHCP clients will support it, and others actually require it.
+.It Xo
+.Ic option mobile-ip-home-agent Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+This option specifies a list of IP addresses indicating mobile IP
+home agents available to the client.
+Agents should be listed in order of preference, although normally there
+will be only one such agent.
+.It Xo
+.Ic option smtp-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic smtp-server
+option specifies a list of SMTP servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option pop-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic pop-server
+option specifies a list of POP3 servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option nntp-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic nntp-server
+option specifies a list of NNTP servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option www-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic www-server
+option specifies a list of WWW servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option finger-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic finger-server
+option specifies a list of
+.Xr finger 1
+servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option irc-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic irc-server
+option specifies a list of IRC servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option streettalk-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic streettalk-server
+option specifies a list of StreetTalk servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option streettalk-directory-assistance-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The StreetTalk Directory Assistance (STDA) server option specifies a
+list of STDA servers available to the client.
+Servers should be listed in order of preference.
+.El
+.Sh SEE ALSO
+.Xr dhclient.conf 5 ,
+.Xr dhcpd.conf 5 ,
+.Xr dhcpd.leases 5 ,
+.Xr dhclient 8 ,
+.Xr dhcpd 8
+.Rs
+.%R "RFC 2131, RFC 2132"
+.Re
+.Sh AUTHORS
+.An -nosplit
+The
+.Xr dhcpd 8
+utility
+was written by
+.An Ted Lemon Aq mellon@vix.com
+under a contract with Vixie Labs.
+.Pp
+The current implementation was reworked by
+.An Henning Brauer Aq henning@openbsd.org .
diff --git a/sbin/dhclient/dhcp.h b/sbin/dhclient/dhcp.h
new file mode 100644
index 0000000..db1c05c
--- /dev/null
+++ b/sbin/dhclient/dhcp.h
@@ -0,0 +1,175 @@
+/* $OpenBSD: dhcp.h,v 1.5 2004/05/04 15:49:49 deraadt Exp $ */
+/* $FreeBSD$ */
+
+/* Protocol structures... */
+
+/*
+ * Copyright (c) 1995, 1996 The Internet Software Consortium.
+ * 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#define DHCP_UDP_OVERHEAD (14 + /* Ethernet header */ \
+ 20 + /* IP header */ \
+ 8) /* UDP header */
+#define DHCP_SNAME_LEN 64
+#define DHCP_FILE_LEN 128
+#define DHCP_FIXED_NON_UDP 236
+#define DHCP_FIXED_LEN (DHCP_FIXED_NON_UDP + DHCP_UDP_OVERHEAD)
+ /* Everything but options. */
+#define DHCP_MTU_MAX 1500
+#define DHCP_OPTION_LEN (DHCP_MTU_MAX - DHCP_FIXED_LEN)
+
+#define BOOTP_MIN_LEN 300
+#define DHCP_MIN_LEN 548
+
+struct dhcp_packet {
+ u_int8_t op; /* Message opcode/type */
+ u_int8_t htype; /* Hardware addr type (see net/if_types.h) */
+ u_int8_t hlen; /* Hardware addr length */
+ u_int8_t hops; /* Number of relay agent hops from client */
+ u_int32_t xid; /* Transaction ID */
+ u_int16_t secs; /* Seconds since client started looking */
+ u_int16_t flags; /* Flag bits */
+ struct in_addr ciaddr; /* Client IP address (if already in use) */
+ struct in_addr yiaddr; /* Client IP address */
+ struct in_addr siaddr; /* IP address of next server to talk to */
+ struct in_addr giaddr; /* DHCP relay agent IP address */
+ unsigned char chaddr[16]; /* Client hardware address */
+ char sname[DHCP_SNAME_LEN]; /* Server name */
+ char file[DHCP_FILE_LEN]; /* Boot filename */
+ unsigned char options[DHCP_OPTION_LEN];
+ /* Optional parameters
+ (actual length dependent on MTU). */
+};
+
+/* BOOTP (rfc951) message types */
+#define BOOTREQUEST 1
+#define BOOTREPLY 2
+
+/* Possible values for flags field... */
+#define BOOTP_BROADCAST 32768L
+
+/* Possible values for hardware type (htype) field... */
+#define HTYPE_ETHER 1 /* Ethernet */
+#define HTYPE_IEEE802 6 /* IEEE 802.2 Token Ring... */
+#define HTYPE_FDDI 8 /* FDDI... */
+
+/* Magic cookie validating dhcp options field (and bootp vendor
+ extensions field). */
+#define DHCP_OPTIONS_COOKIE "\143\202\123\143"
+
+/* DHCP Option codes: */
+
+#define DHO_PAD 0
+#define DHO_SUBNET_MASK 1
+#define DHO_TIME_OFFSET 2
+#define DHO_ROUTERS 3
+#define DHO_TIME_SERVERS 4
+#define DHO_NAME_SERVERS 5
+#define DHO_DOMAIN_NAME_SERVERS 6
+#define DHO_LOG_SERVERS 7
+#define DHO_COOKIE_SERVERS 8
+#define DHO_LPR_SERVERS 9
+#define DHO_IMPRESS_SERVERS 10
+#define DHO_RESOURCE_LOCATION_SERVERS 11
+#define DHO_HOST_NAME 12
+#define DHO_BOOT_SIZE 13
+#define DHO_MERIT_DUMP 14
+#define DHO_DOMAIN_NAME 15
+#define DHO_SWAP_SERVER 16
+#define DHO_ROOT_PATH 17
+#define DHO_EXTENSIONS_PATH 18
+#define DHO_IP_FORWARDING 19
+#define DHO_NON_LOCAL_SOURCE_ROUTING 20
+#define DHO_POLICY_FILTER 21
+#define DHO_MAX_DGRAM_REASSEMBLY 22
+#define DHO_DEFAULT_IP_TTL 23
+#define DHO_PATH_MTU_AGING_TIMEOUT 24
+#define DHO_PATH_MTU_PLATEAU_TABLE 25
+#define DHO_INTERFACE_MTU 26
+#define DHO_ALL_SUBNETS_LOCAL 27
+#define DHO_BROADCAST_ADDRESS 28
+#define DHO_PERFORM_MASK_DISCOVERY 29
+#define DHO_MASK_SUPPLIER 30
+#define DHO_ROUTER_DISCOVERY 31
+#define DHO_ROUTER_SOLICITATION_ADDRESS 32
+#define DHO_STATIC_ROUTES 33
+#define DHO_TRAILER_ENCAPSULATION 34
+#define DHO_ARP_CACHE_TIMEOUT 35
+#define DHO_IEEE802_3_ENCAPSULATION 36
+#define DHO_DEFAULT_TCP_TTL 37
+#define DHO_TCP_KEEPALIVE_INTERVAL 38
+#define DHO_TCP_KEEPALIVE_GARBAGE 39
+#define DHO_NIS_DOMAIN 40
+#define DHO_NIS_SERVERS 41
+#define DHO_NTP_SERVERS 42
+#define DHO_VENDOR_ENCAPSULATED_OPTIONS 43
+#define DHO_NETBIOS_NAME_SERVERS 44
+#define DHO_NETBIOS_DD_SERVER 45
+#define DHO_NETBIOS_NODE_TYPE 46
+#define DHO_NETBIOS_SCOPE 47
+#define DHO_FONT_SERVERS 48
+#define DHO_X_DISPLAY_MANAGER 49
+#define DHO_DHCP_REQUESTED_ADDRESS 50
+#define DHO_DHCP_LEASE_TIME 51
+#define DHO_DHCP_OPTION_OVERLOAD 52
+#define DHO_DHCP_MESSAGE_TYPE 53
+#define DHO_DHCP_SERVER_IDENTIFIER 54
+#define DHO_DHCP_PARAMETER_REQUEST_LIST 55
+#define DHO_DHCP_MESSAGE 56
+#define DHO_DHCP_MAX_MESSAGE_SIZE 57
+#define DHO_DHCP_RENEWAL_TIME 58
+#define DHO_DHCP_REBINDING_TIME 59
+#define DHO_DHCP_CLASS_IDENTIFIER 60
+#define DHO_DHCP_CLIENT_IDENTIFIER 61
+#define DHO_SMTP_SERVER 69
+#define DHO_POP_SERVER 70
+#define DHO_NNTP_SERVER 71
+#define DHO_WWW_SERVER 72
+#define DHO_FINGER_SERVER 73
+#define DHO_IRC_SERVER 74
+#define DHO_DHCP_USER_CLASS_ID 77
+#define DHO_END 255
+
+/* DHCP message types. */
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
diff --git a/sbin/dhclient/dhcpd.h b/sbin/dhclient/dhcpd.h
new file mode 100644
index 0000000..8097f14
--- /dev/null
+++ b/sbin/dhclient/dhcpd.h
@@ -0,0 +1,437 @@
+/* $OpenBSD: dhcpd.h,v 1.33 2004/05/06 22:29:15 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999
+ * The Internet Software Consortium. 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/types.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <paths.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "dhcp.h"
+#include "tree.h"
+
+#define LOCAL_PORT 68
+#define REMOTE_PORT 67
+
+struct option_data {
+ int len;
+ u_int8_t *data;
+};
+
+struct string_list {
+ struct string_list *next;
+ char *string;
+};
+
+struct iaddr {
+ int len;
+ unsigned char iabuf[16];
+};
+
+struct iaddrlist {
+ struct iaddrlist *next;
+ struct iaddr addr;
+};
+
+struct packet {
+ struct dhcp_packet *raw;
+ int packet_length;
+ int packet_type;
+ int options_valid;
+ int client_port;
+ struct iaddr client_addr;
+ struct interface_info *interface;
+ struct hardware *haddr;
+ struct option_data options[256];
+};
+
+struct hardware {
+ u_int8_t htype;
+ u_int8_t hlen;
+ u_int8_t haddr[16];
+};
+
+struct client_lease {
+ struct client_lease *next;
+ time_t expiry, renewal, rebind;
+ struct iaddr address;
+ char *server_name;
+ char *filename;
+ struct string_list *medium;
+ unsigned int is_static : 1;
+ unsigned int is_bootp : 1;
+ struct option_data options[256];
+};
+
+/* Possible states in which the client can be. */
+enum dhcp_state {
+ S_REBOOTING,
+ S_INIT,
+ S_SELECTING,
+ S_REQUESTING,
+ S_BOUND,
+ S_RENEWING,
+ S_REBINDING
+};
+
+struct client_config {
+ struct option_data defaults[256];
+ enum {
+ ACTION_DEFAULT,
+ ACTION_SUPERSEDE,
+ ACTION_PREPEND,
+ ACTION_APPEND
+ } default_actions[256];
+
+ struct option_data send_options[256];
+ u_int8_t required_options[256];
+ u_int8_t requested_options[256];
+ int requested_option_count;
+ time_t timeout;
+ time_t initial_interval;
+ time_t retry_interval;
+ time_t select_interval;
+ time_t reboot_timeout;
+ time_t backoff_cutoff;
+ struct string_list *media;
+ char *script_name;
+ enum { IGNORE, ACCEPT, PREFER }
+ bootp_policy;
+ struct string_list *medium;
+ struct iaddrlist *reject_list;
+};
+
+struct client_state {
+ struct client_lease *active;
+ struct client_lease *new;
+ struct client_lease *offered_leases;
+ struct client_lease *leases;
+ struct client_lease *alias;
+ enum dhcp_state state;
+ struct iaddr destination;
+ u_int32_t xid;
+ u_int16_t secs;
+ time_t first_sending;
+ time_t interval;
+ struct string_list *medium;
+ struct dhcp_packet packet;
+ int packet_length;
+ struct iaddr requested_address;
+ struct client_config *config;
+ char **scriptEnv;
+ int scriptEnvsize;
+ struct string_list *env;
+ int envc;
+};
+
+struct interface_info {
+ struct interface_info *next;
+ struct hardware hw_address;
+ struct in_addr primary_address;
+ char name[IFNAMSIZ];
+ int rfdesc;
+ int wfdesc;
+ unsigned char *rbuf;
+ size_t rbuf_max;
+ size_t rbuf_offset;
+ size_t rbuf_len;
+ struct ifreq *ifp;
+ struct client_state *client;
+ int noifmedia;
+ int errors;
+ int dead;
+ u_int16_t index;
+};
+
+struct timeout {
+ struct timeout *next;
+ time_t when;
+ void (*func)(void *);
+ void *what;
+};
+
+struct protocol {
+ struct protocol *next;
+ int fd;
+ void (*handler)(struct protocol *);
+ void *local;
+};
+
+#define DEFAULT_HASH_SIZE 97
+
+struct hash_bucket {
+ struct hash_bucket *next;
+ unsigned char *name;
+ int len;
+ unsigned char *value;
+};
+
+struct hash_table {
+ int hash_count;
+ struct hash_bucket *buckets[DEFAULT_HASH_SIZE];
+};
+
+/* Default path to dhcpd config file. */
+#define _PATH_DHCLIENT_CONF "/etc/dhclient.conf"
+#define _PATH_DHCLIENT_DB "/var/db/dhclient.leases"
+#define DHCPD_LOG_FACILITY LOG_DAEMON
+
+#define MAX_TIME 0x7fffffff
+#define MIN_TIME 0
+
+/* External definitions... */
+
+/* options.c */
+int cons_options(struct packet *, struct dhcp_packet *, int,
+ struct tree_cache **, int, int, int, u_int8_t *, int);
+char *pretty_print_option(unsigned int,
+ unsigned char *, int, int, int);
+void do_packet(struct interface_info *, struct dhcp_packet *,
+ int, unsigned int, struct iaddr, struct hardware *);
+
+/* errwarn.c */
+extern int warnings_occurred;
+void error(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+int warning(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+int note(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+int debug(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+int parse_warn(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+
+/* conflex.c */
+extern int lexline, lexchar;
+extern char *token_line, *tlname;
+extern char comments[4096];
+extern int comment_index;
+extern int eol_token;
+void new_parse(char *);
+int next_token(char **, FILE *);
+int peek_token(char **, FILE *);
+
+/* parse.c */
+void skip_to_semi(FILE *);
+int parse_semi(FILE *);
+char *parse_string(FILE *);
+int parse_ip_addr(FILE *, struct iaddr *);
+void parse_hardware_param(FILE *, struct hardware *);
+void parse_lease_time(FILE *, time_t *);
+unsigned char *parse_numeric_aggregate(FILE *, unsigned char *, int *,
+ int, int, int);
+void convert_num(unsigned char *, char *, int, int);
+time_t parse_date(FILE *);
+
+/* tree.c */
+pair cons(caddr_t, pair);
+
+/* alloc.c */
+struct string_list *new_string_list(size_t size);
+struct hash_table *new_hash_table(int);
+struct hash_bucket *new_hash_bucket(void);
+
+/* bpf.c */
+int if_register_bpf(struct interface_info *);
+void if_register_send(struct interface_info *);
+void if_register_receive(struct interface_info *);
+ssize_t send_packet(struct interface_info *, struct dhcp_packet *, size_t,
+ struct in_addr, struct sockaddr_in *, struct hardware *);
+ssize_t receive_packet(struct interface_info *, unsigned char *, size_t,
+ struct sockaddr_in *, struct hardware *);
+
+/* dispatch.c */
+extern void (*bootp_packet_handler)(struct interface_info *,
+ struct dhcp_packet *, int, unsigned int, struct iaddr, struct hardware *);
+void discover_interfaces(struct interface_info *);
+void reinitialize_interfaces(void);
+void dispatch(void);
+void got_one(struct protocol *);
+void add_timeout(time_t, void (*)(void *), void *);
+void cancel_timeout(void (*)(void *), void *);
+void add_protocol(char *, int, void (*)(struct protocol *), void *);
+void remove_protocol(struct protocol *);
+int interface_link_status(char *);
+
+/* hash.c */
+struct hash_table *new_hash(void);
+void add_hash(struct hash_table *, unsigned char *, int, unsigned char *);
+unsigned char *hash_lookup(struct hash_table *, unsigned char *, int);
+
+/* tables.c */
+extern struct option dhcp_options[256];
+extern unsigned char dhcp_option_default_priority_list[];
+extern int sizeof_dhcp_option_default_priority_list;
+extern struct hash_table universe_hash;
+extern struct universe dhcp_universe;
+void initialize_universes(void);
+
+/* convert.c */
+u_int32_t getULong(unsigned char *);
+int32_t getLong(unsigned char *);
+u_int16_t getUShort(unsigned char *);
+int16_t getShort(unsigned char *);
+void putULong(unsigned char *, u_int32_t);
+void putLong(unsigned char *, int32_t);
+void putUShort(unsigned char *, unsigned int);
+void putShort(unsigned char *, int);
+
+/* inet.c */
+struct iaddr subnet_number(struct iaddr, struct iaddr);
+struct iaddr broadcast_addr(struct iaddr, struct iaddr);
+int addr_eq(struct iaddr, struct iaddr);
+char *piaddr(struct iaddr);
+
+/* dhclient.c */
+extern char *path_dhclient_conf;
+extern char *path_dhclient_db;
+extern time_t cur_time;
+extern int log_priority;
+extern int log_perror;
+
+extern struct client_config top_level_config;
+
+void dhcpoffer(struct packet *);
+void dhcpack(struct packet *);
+void dhcpnak(struct packet *);
+
+void send_discover(void *);
+void send_request(void *);
+void send_decline(void *);
+
+void state_reboot(void *);
+void state_init(void *);
+void state_selecting(void *);
+void state_requesting(void *);
+void state_bound(void *);
+void state_panic(void *);
+
+void bind_lease(struct interface_info *);
+
+void make_discover(struct interface_info *, struct client_lease *);
+void make_request(struct interface_info *, struct client_lease *);
+void make_decline(struct interface_info *, struct client_lease *);
+
+void free_client_lease(struct client_lease *);
+void rewrite_client_leases(void);
+void write_client_lease(struct interface_info *, struct client_lease *, int);
+
+void priv_script_init(char *, char *);
+void priv_script_write_params(char *, struct client_lease *);
+int priv_script_go(void);
+
+void script_init(char *, struct string_list *);
+void script_write_params(char *, struct client_lease *);
+int script_go(void);
+void client_envadd(struct client_state *,
+ const char *, const char *, const char *, ...);
+void script_set_env(struct client_state *, const char *, const char *,
+ const char *);
+void script_flush_env(struct client_state *);
+int dhcp_option_ev_name(char *, size_t, struct option *);
+
+struct client_lease *packet_to_lease(struct packet *);
+void go_daemon(void);
+void client_location_changed(void);
+
+void bootp(struct packet *);
+void dhcp(struct packet *);
+
+/* packet.c */
+void assemble_hw_header(struct interface_info *, unsigned char *,
+ int *, struct hardware *);
+void assemble_udp_ip_header(unsigned char *, int *, u_int32_t, u_int32_t,
+ unsigned int, unsigned char *, int);
+ssize_t decode_hw_header(unsigned char *, int, struct hardware *);
+ssize_t decode_udp_ip_header(unsigned char *, int, struct sockaddr_in *,
+ unsigned char *, int);
+
+/* ethernet.c */
+void assemble_ethernet_header(struct interface_info *, unsigned char *,
+ int *, struct hardware *);
+ssize_t decode_ethernet_header(struct interface_info *, unsigned char *,
+ int, struct hardware *);
+
+/* clparse.c */
+int read_client_conf(void);
+void read_client_leases(void);
+void parse_client_statement(FILE *, struct interface_info *,
+ struct client_config *);
+int parse_X(FILE *, u_int8_t *, int);
+int parse_option_list(FILE *, u_int8_t *);
+void parse_interface_declaration(FILE *, struct client_config *);
+struct interface_info *interface_or_dummy(char *);
+void make_client_state(struct interface_info *);
+void make_client_config(struct interface_info *, struct client_config *);
+void parse_client_lease_statement(FILE *, int);
+void parse_client_lease_declaration(FILE *, struct client_lease *,
+ struct interface_info **);
+struct option *parse_option_decl(FILE *, struct option_data *);
+void parse_string_list(FILE *, struct string_list **, int);
+void parse_reject_statement(FILE *, struct client_config *);
+
+/* privsep.c */
+struct buf *buf_open(size_t);
+int buf_add(struct buf *, void *, size_t);
+int buf_close(int, struct buf *);
+ssize_t buf_read(int, void *, size_t);
+void dispatch_imsg(int);
diff --git a/sbin/dhclient/dhctoken.h b/sbin/dhclient/dhctoken.h
new file mode 100644
index 0000000..7b23242
--- /dev/null
+++ b/sbin/dhclient/dhctoken.h
@@ -0,0 +1,136 @@
+/* $OpenBSD: dhctoken.h,v 1.2 2004/02/04 12:16:56 henning Exp $ */
+
+/* Tokens for config file lexer and parser. */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999
+ * The Internet Software Consortium. 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#define SEMI ';'
+#define DOT '.'
+#define COLON ':'
+#define COMMA ','
+#define SLASH '/'
+#define LBRACE '{'
+#define RBRACE '}'
+
+#define FIRST_TOKEN HOST
+#define HOST 256
+#define HARDWARE 257
+#define FILENAME 258
+#define FIXED_ADDR 259
+#define OPTION 260
+#define ETHERNET 261
+#define STRING 262
+#define NUMBER 263
+#define NUMBER_OR_NAME 264
+#define NAME 265
+#define TIMESTAMP 266
+#define STARTS 267
+#define ENDS 268
+#define UID 269
+#define CLASS 270
+#define LEASE 271
+#define RANGE 272
+#define PACKET 273
+#define CIADDR 274
+#define YIADDR 275
+#define SIADDR 276
+#define GIADDR 277
+#define SUBNET 278
+#define NETMASK 279
+#define DEFAULT_LEASE_TIME 280
+#define MAX_LEASE_TIME 281
+#define VENDOR_CLASS 282
+#define USER_CLASS 283
+#define SHARED_NETWORK 284
+#define SERVER_NAME 285
+#define DYNAMIC_BOOTP 286
+#define SERVER_IDENTIFIER 287
+#define DYNAMIC_BOOTP_LEASE_CUTOFF 288
+#define DYNAMIC_BOOTP_LEASE_LENGTH 289
+#define BOOT_UNKNOWN_CLIENTS 290
+#define NEXT_SERVER 291
+#define TOKEN_RING 292
+#define GROUP 293
+#define ONE_LEASE_PER_CLIENT 294
+#define GET_LEASE_HOSTNAMES 295
+#define USE_HOST_DECL_NAMES 296
+#define SEND 297
+#define CLIENT_IDENTIFIER 298
+#define REQUEST 299
+#define REQUIRE 300
+#define TIMEOUT 301
+#define RETRY 302
+#define SELECT_TIMEOUT 303
+#define SCRIPT 304
+#define INTERFACE 305
+#define RENEW 306
+#define REBIND 307
+#define EXPIRE 308
+#define UNKNOWN_CLIENTS 309
+#define ALLOW 310
+#define BOOTP 311
+#define DENY 312
+#define BOOTING 313
+#define DEFAULT 314
+#define MEDIA 315
+#define MEDIUM 316
+#define ALIAS 317
+#define REBOOT 318
+#define ABANDONED 319
+#define BACKOFF_CUTOFF 320
+#define INITIAL_INTERVAL 321
+#define NAMESERVER 322
+#define DOMAIN 323
+#define SEARCH 324
+#define SUPERSEDE 325
+#define APPEND 326
+#define PREPEND 327
+#define HOSTNAME 328
+#define CLIENT_HOSTNAME 329
+#define REJECT 330
+#define FDDI 331
+#define USE_LEASE_ADDR_FOR_DEFAULT_ROUTE 332
+#define AUTHORITATIVE 333
+#define TOKEN_NOT 334
+#define ALWAYS_REPLY_RFC1048 335
+
+#define is_identifier(x) ((x) >= FIRST_TOKEN && \
+ (x) != STRING && \
+ (x) != NUMBER && \
+ (x) != EOF)
diff --git a/sbin/dhclient/dispatch.c b/sbin/dhclient/dispatch.c
new file mode 100644
index 0000000..2fec913
--- /dev/null
+++ b/sbin/dhclient/dispatch.c
@@ -0,0 +1,498 @@
+/* $OpenBSD: dispatch.c,v 1.31 2004/09/21 04:07:03 david Exp $ */
+
+/*
+ * Copyright 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999
+ * The Internet Software Consortium. 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "dhcpd.h"
+
+#include <sys/ioctl.h>
+
+#include <net/if_media.h>
+#include <ifaddrs.h>
+#include <poll.h>
+
+struct protocol *protocols;
+struct timeout *timeouts;
+static struct timeout *free_timeouts;
+static int interfaces_invalidated;
+void (*bootp_packet_handler)(struct interface_info *,
+ struct dhcp_packet *, int, unsigned int,
+ struct iaddr, struct hardware *);
+
+static int interface_status(struct interface_info *ifinfo);
+
+/*
+ * Use getifaddrs() to get a list of all the attached interfaces. For
+ * each interface that's of type INET and not the loopback interface,
+ * register that interface with the network I/O software, figure out
+ * what subnet it's on, and add it to the list of interfaces.
+ */
+void
+discover_interfaces(struct interface_info *iface)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct sockaddr_in foo;
+ struct ifreq *tif;
+
+ if (getifaddrs(&ifap) != 0)
+ error("getifaddrs failed");
+
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+ if ((ifa->ifa_flags & IFF_LOOPBACK) ||
+ (ifa->ifa_flags & IFF_POINTOPOINT) ||
+ (!(ifa->ifa_flags & IFF_UP)))
+ continue;
+
+ if (strcmp(iface->name, ifa->ifa_name))
+ continue;
+
+ /*
+ * If we have the capability, extract link information
+ * and record it in a linked list.
+ */
+ if (ifa->ifa_addr->sa_family == AF_LINK) {
+ struct sockaddr_dl *foo =
+ (struct sockaddr_dl *)ifa->ifa_addr;
+
+ iface->index = foo->sdl_index;
+ iface->hw_address.hlen = foo->sdl_alen;
+ iface->hw_address.htype = HTYPE_ETHER; /* XXX */
+ memcpy(iface->hw_address.haddr,
+ LLADDR(foo), foo->sdl_alen);
+ } else if (ifa->ifa_addr->sa_family == AF_INET) {
+ struct iaddr addr;
+
+ memcpy(&foo, ifa->ifa_addr, sizeof(foo));
+ if (foo.sin_addr.s_addr == htonl(INADDR_LOOPBACK))
+ continue;
+ if (!iface->ifp) {
+ int len = IFNAMSIZ + ifa->ifa_addr->sa_len;
+ if ((tif = malloc(len)) == NULL)
+ error("no space to remember ifp");
+ strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ);
+ memcpy(&tif->ifr_addr, ifa->ifa_addr,
+ ifa->ifa_addr->sa_len);
+ iface->ifp = tif;
+ iface->primary_address = foo.sin_addr;
+ }
+ addr.len = 4;
+ memcpy(addr.iabuf, &foo.sin_addr.s_addr, addr.len);
+ }
+ }
+
+ if (!iface->ifp)
+ error("%s: not found", iface->name);
+
+ /* Register the interface... */
+ if_register_receive(iface);
+ if_register_send(iface);
+ add_protocol(iface->name, iface->rfdesc, got_one, iface);
+ freeifaddrs(ifap);
+}
+
+void
+reinitialize_interfaces(void)
+{
+ interfaces_invalidated = 1;
+}
+
+/*
+ * Wait for packets to come in using poll(). When a packet comes in,
+ * call receive_packet to receive the packet and possibly strip hardware
+ * addressing information from it, and then call through the
+ * bootp_packet_handler hook to try to do something with it.
+ */
+void
+dispatch(void)
+{
+ int count, i, to_msec, nfds = 0;
+ struct protocol *l;
+ struct pollfd *fds;
+ time_t howlong;
+
+ for (l = protocols; l; l = l->next)
+ nfds++;
+
+ fds = malloc(nfds * sizeof(struct pollfd));
+ if (fds == NULL)
+ error("Can't allocate poll structures.");
+
+ do {
+ /*
+ * Call any expired timeouts, and then if there's still
+ * a timeout registered, time out the select call then.
+ */
+another:
+ if (timeouts) {
+ struct timeout *t;
+
+ if (timeouts->when <= cur_time) {
+ t = timeouts;
+ timeouts = timeouts->next;
+ (*(t->func))(t->what);
+ t->next = free_timeouts;
+ free_timeouts = t;
+ goto another;
+ }
+
+ /*
+ * Figure timeout in milliseconds, and check for
+ * potential overflow, so we can cram into an
+ * int for poll, while not polling with a
+ * negative timeout and blocking indefinitely.
+ */
+ howlong = timeouts->when - cur_time;
+ if (howlong > INT_MAX / 1000)
+ howlong = INT_MAX / 1000;
+ to_msec = howlong * 1000;
+ } else
+ to_msec = -1;
+
+ /* Set up the descriptors to be polled. */
+ for (i = 0, l = protocols; l; l = l->next) {
+ struct interface_info *ip = l->local;
+
+ if (ip && (l->handler != got_one || !ip->dead)) {
+ fds[i].fd = l->fd;
+ fds[i].events = POLLIN;
+ fds[i].revents = 0;
+ i++;
+ }
+ }
+
+ if (i == 0)
+ error("No live interfaces to poll on - exiting.");
+
+ /* Wait for a packet or a timeout... XXX */
+ count = poll(fds, nfds, to_msec);
+
+ /* Not likely to be transitory... */
+ if (count == -1) {
+ if (errno == EAGAIN || errno == EINTR) {
+ time(&cur_time);
+ continue;
+ } else
+ error("poll: %m");
+ }
+
+ /* Get the current time... */
+ time(&cur_time);
+
+ i = 0;
+ for (l = protocols; l; l = l->next) {
+ struct interface_info *ip;
+ ip = l->local;
+ if ((fds[i].revents & (POLLIN | POLLHUP))) {
+ fds[i].revents = 0;
+ if (ip && (l->handler != got_one ||
+ !ip->dead))
+ (*(l->handler))(l);
+ if (interfaces_invalidated)
+ break;
+ }
+ i++;
+ }
+ interfaces_invalidated = 0;
+ } while (1);
+}
+
+
+void
+got_one(struct protocol *l)
+{
+ struct sockaddr_in from;
+ struct hardware hfrom;
+ struct iaddr ifrom;
+ ssize_t result;
+ union {
+ /*
+ * Packet input buffer. Must be as large as largest
+ * possible MTU.
+ */
+ unsigned char packbuf[4095];
+ struct dhcp_packet packet;
+ } u;
+ struct interface_info *ip = l->local;
+
+ if ((result = receive_packet(ip, u.packbuf, sizeof(u), &from,
+ &hfrom)) == -1) {
+ warning("receive_packet failed on %s: %s", ip->name,
+ strerror(errno));
+ ip->errors++;
+ if ((!interface_status(ip)) ||
+ (ip->noifmedia && ip->errors > 20)) {
+ /* our interface has gone away. */
+ warning("Interface %s no longer appears valid.",
+ ip->name);
+ ip->dead = 1;
+ interfaces_invalidated = 1;
+ close(l->fd);
+ remove_protocol(l);
+ free(ip);
+ }
+ return;
+ }
+ if (result == 0)
+ return;
+
+ if (bootp_packet_handler) {
+ ifrom.len = 4;
+ memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
+
+ (*bootp_packet_handler)(ip, &u.packet, result,
+ from.sin_port, ifrom, &hfrom);
+ }
+}
+
+int
+interface_status(struct interface_info *ifinfo)
+{
+ char *ifname = ifinfo->name;
+ int ifsock = ifinfo->rfdesc;
+ struct ifreq ifr;
+ struct ifmediareq ifmr;
+
+ /* get interface flags */
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
+ syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS) on %s: %m", ifname);
+ goto inactive;
+ }
+
+ /*
+ * if one of UP and RUNNING flags is dropped,
+ * the interface is not active.
+ */
+ if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
+ goto inactive;
+
+ /* Next, check carrier on the interface, if possible */
+ if (ifinfo->noifmedia)
+ goto active;
+ memset(&ifmr, 0, sizeof(ifmr));
+ strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
+ if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
+ if (errno != EINVAL) {
+ syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m",
+ ifname);
+
+ ifinfo->noifmedia = 1;
+ goto active;
+ }
+ /*
+ * EINVAL (or ENOTTY) simply means that the interface
+ * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
+ */
+ ifinfo->noifmedia = 1;
+ goto active;
+ }
+ if (ifmr.ifm_status & IFM_AVALID) {
+ switch (ifmr.ifm_active & IFM_NMASK) {
+ case IFM_ETHER:
+ if (ifmr.ifm_status & IFM_ACTIVE)
+ goto active;
+ else
+ goto inactive;
+ break;
+ default:
+ goto inactive;
+ }
+ }
+inactive:
+ return (0);
+active:
+ return (1);
+}
+
+void
+add_timeout(time_t when, void (*where)(void *), void *what)
+{
+ struct timeout *t, *q;
+
+ /* See if this timeout supersedes an existing timeout. */
+ t = NULL;
+ for (q = timeouts; q; q = q->next) {
+ if (q->func == where && q->what == what) {
+ if (t)
+ t->next = q->next;
+ else
+ timeouts = q->next;
+ break;
+ }
+ t = q;
+ }
+
+ /* If we didn't supersede a timeout, allocate a timeout
+ structure now. */
+ if (!q) {
+ if (free_timeouts) {
+ q = free_timeouts;
+ free_timeouts = q->next;
+ q->func = where;
+ q->what = what;
+ } else {
+ q = malloc(sizeof(struct timeout));
+ if (!q)
+ error("Can't allocate timeout structure!");
+ q->func = where;
+ q->what = what;
+ }
+ }
+
+ q->when = when;
+
+ /* Now sort this timeout into the timeout list. */
+
+ /* Beginning of list? */
+ if (!timeouts || timeouts->when > q->when) {
+ q->next = timeouts;
+ timeouts = q;
+ return;
+ }
+
+ /* Middle of list? */
+ for (t = timeouts; t->next; t = t->next) {
+ if (t->next->when > q->when) {
+ q->next = t->next;
+ t->next = q;
+ return;
+ }
+ }
+
+ /* End of list. */
+ t->next = q;
+ q->next = NULL;
+}
+
+void
+cancel_timeout(void (*where)(void *), void *what)
+{
+ struct timeout *t, *q;
+
+ /* Look for this timeout on the list, and unlink it if we find it. */
+ t = NULL;
+ for (q = timeouts; q; q = q->next) {
+ if (q->func == where && q->what == what) {
+ if (t)
+ t->next = q->next;
+ else
+ timeouts = q->next;
+ break;
+ }
+ t = q;
+ }
+
+ /* If we found the timeout, put it on the free list. */
+ if (q) {
+ q->next = free_timeouts;
+ free_timeouts = q;
+ }
+}
+
+/* Add a protocol to the list of protocols... */
+void
+add_protocol(char *name, int fd, void (*handler)(struct protocol *),
+ void *local)
+{
+ struct protocol *p;
+
+ p = malloc(sizeof(*p));
+ if (!p)
+ error("can't allocate protocol struct for %s", name);
+
+ p->fd = fd;
+ p->handler = handler;
+ p->local = local;
+ p->next = protocols;
+ protocols = p;
+}
+
+void
+remove_protocol(struct protocol *proto)
+{
+ struct protocol *p, *next, *prev;
+
+ prev = NULL;
+ for (p = protocols; p; p = next) {
+ next = p->next;
+ if (p == proto) {
+ if (prev)
+ prev->next = p->next;
+ else
+ protocols = p->next;
+ free(p);
+ }
+ }
+}
+
+int
+interface_link_status(char *ifname)
+{
+ struct ifmediareq ifmr;
+ int sock;
+
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ error("Can't create socket");
+
+ memset(&ifmr, 0, sizeof(ifmr));
+ strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
+ if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
+ /* EINVAL -> link state unknown. treat as active */
+ if (errno != EINVAL)
+ syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m",
+ ifname);
+ close(sock);
+ return (1);
+ }
+ close(sock);
+
+ if (ifmr.ifm_status & IFM_AVALID) {
+ if ((ifmr.ifm_active & IFM_NMASK) == IFM_ETHER) {
+ if (ifmr.ifm_status & IFM_ACTIVE)
+ return (1);
+ else
+ return (0);
+ }
+ }
+ return (1);
+}
diff --git a/sbin/dhclient/errwarn.c b/sbin/dhclient/errwarn.c
new file mode 100644
index 0000000..de34583
--- /dev/null
+++ b/sbin/dhclient/errwarn.c
@@ -0,0 +1,237 @@
+/* $OpenBSD: errwarn.c,v 1.7 2004/05/04 22:23:01 mickey Exp $ */
+
+/* Errors and warnings... */
+
+/*
+ * Copyright (c) 1996 The Internet Software Consortium.
+ * All Rights Reserved.
+ * Copyright (c) 1995 RadioMail Corporation. 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 RadioMail Corporation, the Internet Software
+ * Consortium 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 RADIOMAIL CORPORATION, THE INTERNET
+ * SOFTWARE CONSORTIUM 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 RADIOMAIL CORPORATION 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 software was written for RadioMail Corporation by Ted Lemon
+ * under a contract with Vixie Enterprises. Further modifications have
+ * been made for the Internet Software Consortium under a contract
+ * with Vixie Laboratories.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+
+#include "dhcpd.h"
+
+static void do_percentm(char *obuf, size_t size, char *ibuf);
+
+static char mbuf[1024];
+static char fbuf[1024];
+
+int warnings_occurred;
+
+/*
+ * Log an error message, then exit.
+ */
+void
+error(char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm(fbuf, sizeof(fbuf), fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_ERR, "%s", mbuf);
+#endif
+
+ /* Also log it to stderr? */
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ }
+
+ syslog(LOG_CRIT, "exiting.");
+ if (log_perror) {
+ fprintf(stderr, "exiting.\n");
+ fflush(stderr);
+ }
+ exit(1);
+}
+
+/*
+ * Log a warning message...
+ */
+int
+warning(char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm(fbuf, sizeof(fbuf), fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_ERR, "%s", mbuf);
+#endif
+
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ }
+
+ return (0);
+}
+
+/*
+ * Log a note...
+ */
+int
+note(char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm(fbuf, sizeof(fbuf), fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_INFO, "%s", mbuf);
+#endif
+
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ }
+
+ return (0);
+}
+
+/*
+ * Log a debug message...
+ */
+int
+debug(char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm(fbuf, sizeof(fbuf), fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_DEBUG, "%s", mbuf);
+#endif
+
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ }
+
+ return (0);
+}
+
+/*
+ * Find %m in the input string and substitute an error message string.
+ */
+static void
+do_percentm(char *obuf, size_t size, char *ibuf)
+{
+ char ch;
+ char *s = ibuf;
+ char *t = obuf;
+ size_t prlen;
+ size_t fmt_left;
+ int saved_errno = errno;
+
+ /*
+ * We wouldn't need this mess if printf handled %m, or if
+ * strerror() had been invented before syslog().
+ */
+ for (fmt_left = size; (ch = *s); ++s) {
+ if (ch == '%' && s[1] == 'm') {
+ ++s;
+ prlen = snprintf(t, fmt_left, "%s",
+ strerror(saved_errno));
+ if (prlen >= fmt_left)
+ prlen = fmt_left - 1;
+ t += prlen;
+ fmt_left -= prlen;
+ } else {
+ if (fmt_left > 1) {
+ *t++ = ch;
+ fmt_left--;
+ }
+ }
+ }
+ *t = '\0';
+}
+
+int
+parse_warn(char *fmt, ...)
+{
+ va_list list;
+ static char spaces[] =
+ " "
+ " "; /* 80 spaces */
+
+ do_percentm(mbuf, sizeof(mbuf), fmt);
+ snprintf(fbuf, sizeof(fbuf), "%s line %d: %s", tlname, lexline, mbuf);
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_ERR, "%s", mbuf);
+ syslog(log_priority | LOG_ERR, "%s", token_line);
+ if (lexline < 81)
+ syslog(log_priority | LOG_ERR,
+ "%s^", &spaces[sizeof(spaces) - lexchar]);
+#endif
+
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ write(2, token_line, strlen(token_line));
+ write(2, "\n", 1);
+ write(2, spaces, lexchar - 1);
+ write(2, "^\n", 2);
+ }
+
+ warnings_occurred = 1;
+
+ return (0);
+}
diff --git a/sbin/dhclient/hash.c b/sbin/dhclient/hash.c
new file mode 100644
index 0000000..040236a
--- /dev/null
+++ b/sbin/dhclient/hash.c
@@ -0,0 +1,122 @@
+/* $OpenBSD: hash.c,v 1.9 2004/05/10 15:30:47 deraadt Exp $ */
+
+/* Routines for manipulating hash tables... */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+ * 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "dhcpd.h"
+
+static int do_hash(unsigned char *, int, int);
+
+struct hash_table *
+new_hash(void)
+{
+ struct hash_table *rv = new_hash_table(DEFAULT_HASH_SIZE);
+
+ if (!rv)
+ return (rv);
+ memset(&rv->buckets[0], 0,
+ DEFAULT_HASH_SIZE * sizeof(struct hash_bucket *));
+ return (rv);
+}
+
+static int
+do_hash(unsigned char *name, int len, int size)
+{
+ unsigned char *s = name;
+ int accum = 0, i = len;
+
+ while (i--) {
+ /* Add the character in... */
+ accum += *s++;
+ /* Add carry back in... */
+ while (accum > 255)
+ accum = (accum & 255) + (accum >> 8);
+ }
+ return (accum % size);
+}
+
+void add_hash(struct hash_table *table, unsigned char *name, int len,
+ unsigned char *pointer)
+{
+ struct hash_bucket *bp;
+ int hashno;
+
+ if (!table)
+ return;
+ if (!len)
+ len = strlen((char *)name);
+
+ hashno = do_hash(name, len, table->hash_count);
+ bp = new_hash_bucket();
+
+ if (!bp) {
+ warning("Can't add %s to hash table.", name);
+ return;
+ }
+ bp->name = name;
+ bp->value = pointer;
+ bp->next = table->buckets[hashno];
+ bp->len = len;
+ table->buckets[hashno] = bp;
+}
+
+unsigned char *
+hash_lookup(struct hash_table *table, unsigned char *name, int len)
+{
+ struct hash_bucket *bp;
+ int hashno;
+
+ if (!table)
+ return (NULL);
+
+ if (!len)
+ len = strlen((char *)name);
+
+ hashno = do_hash(name, len, table->hash_count);
+
+ for (bp = table->buckets[hashno]; bp; bp = bp->next)
+ if (len == bp->len && !memcmp(bp->name, name, len))
+ return (bp->value);
+
+ return (NULL);
+}
diff --git a/sbin/dhclient/inet.c b/sbin/dhclient/inet.c
new file mode 100644
index 0000000..4b7b1ce
--- /dev/null
+++ b/sbin/dhclient/inet.c
@@ -0,0 +1,121 @@
+/* $OpenBSD: inet.c,v 1.7 2004/05/04 21:48:16 deraadt Exp $ */
+
+/*
+ * Subroutines to manipulate internet addresses in a safely portable
+ * way...
+ */
+
+/*
+ * Copyright (c) 1996 The Internet Software Consortium. 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "dhcpd.h"
+
+/*
+ * Return just the network number of an internet address...
+ */
+struct iaddr
+subnet_number(struct iaddr addr, struct iaddr mask)
+{
+ struct iaddr rv;
+ int i;
+
+ rv.len = 0;
+
+ /* Both addresses must have the same length... */
+ if (addr.len != mask.len)
+ return (rv);
+
+ rv.len = addr.len;
+ for (i = 0; i < rv.len; i++)
+ rv.iabuf[i] = addr.iabuf[i] & mask.iabuf[i];
+ return (rv);
+}
+
+/*
+ * Given a subnet number and netmask, return the address on that subnet
+ * for which the host portion of the address is all ones (the standard
+ * broadcast address).
+ */
+struct iaddr
+broadcast_addr(struct iaddr subnet, struct iaddr mask)
+{
+ struct iaddr rv;
+ int i;
+
+ if (subnet.len != mask.len) {
+ rv.len = 0;
+ return (rv);
+ }
+
+ for (i = 0; i < subnet.len; i++)
+ rv.iabuf[i] = subnet.iabuf[i] | (~mask.iabuf[i] & 255);
+ rv.len = subnet.len;
+
+ return (rv);
+}
+
+int
+addr_eq(struct iaddr addr1, struct iaddr addr2)
+{
+ if (addr1.len != addr2.len)
+ return (0);
+ return (memcmp(addr1.iabuf, addr2.iabuf, addr1.len) == 0);
+}
+
+char *
+piaddr(struct iaddr addr)
+{
+ static char pbuf[32];
+ struct in_addr a;
+ char *s;
+
+ memcpy(&a, &(addr.iabuf), sizeof(struct in_addr));
+
+ if (addr.len == 0)
+ strlcpy(pbuf, "<null address>", sizeof(pbuf));
+ else {
+ s = inet_ntoa(a);
+ if (s != NULL)
+ strlcpy(pbuf, s, sizeof(pbuf));
+ else
+ strlcpy(pbuf, "<invalid address>", sizeof(pbuf));
+ }
+ return (pbuf);
+}
diff --git a/sbin/dhclient/options.c b/sbin/dhclient/options.c
new file mode 100644
index 0000000..09aa4d8
--- /dev/null
+++ b/sbin/dhclient/options.c
@@ -0,0 +1,720 @@
+/* $OpenBSD: options.c,v 1.15 2004/12/26 03:17:07 deraadt Exp $ */
+
+/* DHCP options parsing and reassembly. */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+ * 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <ctype.h>
+
+#define DHCP_OPTION_DATA
+#include "dhcpd.h"
+
+int bad_options = 0;
+int bad_options_max = 5;
+
+void parse_options(struct packet *);
+void parse_option_buffer(struct packet *, unsigned char *, int);
+int store_options(unsigned char *, int, struct tree_cache **,
+ unsigned char *, int, int, int, int);
+
+
+/*
+ * Parse all available options out of the specified packet.
+ */
+void
+parse_options(struct packet *packet)
+{
+ /* Initially, zero all option pointers. */
+ memset(packet->options, 0, sizeof(packet->options));
+
+ /* If we don't see the magic cookie, there's nothing to parse. */
+ if (memcmp(packet->raw->options, DHCP_OPTIONS_COOKIE, 4)) {
+ packet->options_valid = 0;
+ return;
+ }
+
+ /*
+ * Go through the options field, up to the end of the packet or
+ * the End field.
+ */
+ parse_option_buffer(packet, &packet->raw->options[4],
+ packet->packet_length - DHCP_FIXED_NON_UDP - 4);
+
+ /*
+ * If we parsed a DHCP Option Overload option, parse more
+ * options out of the buffer(s) containing them.
+ */
+ if (packet->options_valid &&
+ packet->options[DHO_DHCP_OPTION_OVERLOAD].data) {
+ if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
+ parse_option_buffer(packet,
+ (unsigned char *)packet->raw->file,
+ sizeof(packet->raw->file));
+ if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
+ parse_option_buffer(packet,
+ (unsigned char *)packet->raw->sname,
+ sizeof(packet->raw->sname));
+ }
+}
+
+/*
+ * Parse options out of the specified buffer, storing addresses of
+ * option values in packet->options and setting packet->options_valid if
+ * no errors are encountered.
+ */
+void
+parse_option_buffer(struct packet *packet,
+ unsigned char *buffer, int length)
+{
+ unsigned char *s, *t, *end = buffer + length;
+ int len, code;
+
+ for (s = buffer; *s != DHO_END && s < end; ) {
+ code = s[0];
+
+ /* Pad options don't have a length - just skip them. */
+ if (code == DHO_PAD) {
+ s++;
+ continue;
+ }
+ if (s + 2 > end) {
+ len = 65536;
+ goto bogus;
+ }
+
+ /*
+ * All other fields (except end, see above) have a
+ * one-byte length.
+ */
+ len = s[1];
+
+ /*
+ * If the length is outrageous, silently skip the rest,
+ * and mark the packet bad. Unfortunately some crappy
+ * dhcp servers always seem to give us garbage on the
+ * end of a packet. so rather than keep refusing, give
+ * up and try to take one after seeing a few without
+ * anything good.
+ */
+ if (s + len + 2 > end) {
+ bogus:
+ bad_options++;
+ warning("option %s (%d) %s.",
+ dhcp_options[code].name, len,
+ "larger than buffer");
+ if (bad_options == bad_options_max) {
+ packet->options_valid = 1;
+ bad_options = 0;
+ warning("Many bogus options seen in offers. "
+ "Taking this offer in spite of bogus "
+ "options - hope for the best!");
+ } else {
+ warning("rejecting bogus offer.");
+ packet->options_valid = 0;
+ }
+ return;
+ }
+ /*
+ * If we haven't seen this option before, just make
+ * space for it and copy it there.
+ */
+ if (!packet->options[code].data) {
+ if (!(t = calloc(1, len + 1)))
+ error("Can't allocate storage for option %s.",
+ dhcp_options[code].name);
+ /*
+ * Copy and NUL-terminate the option (in case
+ * it's an ASCII string.
+ */
+ memcpy(t, &s[2], len);
+ t[len] = 0;
+ packet->options[code].len = len;
+ packet->options[code].data = t;
+ } else {
+ /*
+ * If it's a repeat, concatenate it to whatever
+ * we last saw. This is really only required
+ * for clients, but what the heck...
+ */
+ t = calloc(1, len + packet->options[code].len + 1);
+ if (!t)
+ error("Can't expand storage for option %s.",
+ dhcp_options[code].name);
+ memcpy(t, packet->options[code].data,
+ packet->options[code].len);
+ memcpy(t + packet->options[code].len,
+ &s[2], len);
+ packet->options[code].len += len;
+ t[packet->options[code].len] = 0;
+ free(packet->options[code].data);
+ packet->options[code].data = t;
+ }
+ s += len + 2;
+ }
+ packet->options_valid = 1;
+}
+
+/*
+ * cons options into a big buffer, and then split them out into the
+ * three separate buffers if needed. This allows us to cons up a set of
+ * vendor options using the same routine.
+ */
+int
+cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
+ int mms, struct tree_cache **options,
+ int overload, /* Overload flags that may be set. */
+ int terminate, int bootpp, u_int8_t *prl, int prl_len)
+{
+ unsigned char priority_list[300], buffer[4096];
+ int priority_len, main_buffer_size, mainbufix, bufix;
+ int option_size, length;
+
+ /*
+ * If the client has provided a maximum DHCP message size, use
+ * that; otherwise, if it's BOOTP, only 64 bytes; otherwise use
+ * up to the minimum IP MTU size (576 bytes).
+ *
+ * XXX if a BOOTP client specifies a max message size, we will
+ * honor it.
+ */
+ if (!mms &&
+ inpacket &&
+ inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data &&
+ (inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].len >=
+ sizeof(u_int16_t)))
+ mms = getUShort(
+ inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data);
+
+ if (mms)
+ main_buffer_size = mms - DHCP_FIXED_LEN;
+ else if (bootpp)
+ main_buffer_size = 64;
+ else
+ main_buffer_size = 576 - DHCP_FIXED_LEN;
+
+ if (main_buffer_size > sizeof(buffer))
+ main_buffer_size = sizeof(buffer);
+
+ /* Preload the option priority list with mandatory options. */
+ priority_len = 0;
+ priority_list[priority_len++] = DHO_DHCP_MESSAGE_TYPE;
+ priority_list[priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
+ priority_list[priority_len++] = DHO_DHCP_LEASE_TIME;
+ priority_list[priority_len++] = DHO_DHCP_MESSAGE;
+
+ /*
+ * If the client has provided a list of options that it wishes
+ * returned, use it to prioritize. Otherwise, prioritize based
+ * on the default priority list.
+ */
+ if (inpacket &&
+ inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data) {
+ int prlen =
+ inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].len;
+ if (prlen + priority_len > sizeof(priority_list))
+ prlen = sizeof(priority_list) - priority_len;
+
+ memcpy(&priority_list[priority_len],
+ inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data,
+ prlen);
+ priority_len += prlen;
+ prl = priority_list;
+ } else if (prl) {
+ if (prl_len + priority_len > sizeof(priority_list))
+ prl_len = sizeof(priority_list) - priority_len;
+
+ memcpy(&priority_list[priority_len], prl, prl_len);
+ priority_len += prl_len;
+ prl = priority_list;
+ } else {
+ memcpy(&priority_list[priority_len],
+ dhcp_option_default_priority_list,
+ sizeof_dhcp_option_default_priority_list);
+ priority_len += sizeof_dhcp_option_default_priority_list;
+ }
+
+ /* Copy the options into the big buffer... */
+ option_size = store_options(
+ buffer,
+ (main_buffer_size - 7 + ((overload & 1) ? DHCP_FILE_LEN : 0) +
+ ((overload & 2) ? DHCP_SNAME_LEN : 0)),
+ options, priority_list, priority_len, main_buffer_size,
+ (main_buffer_size + ((overload & 1) ? DHCP_FILE_LEN : 0)),
+ terminate);
+
+ /* Put the cookie up front... */
+ memcpy(outpacket->options, DHCP_OPTIONS_COOKIE, 4);
+ mainbufix = 4;
+
+ /*
+ * If we're going to have to overload, store the overload option
+ * at the beginning. If we can, though, just store the whole
+ * thing in the packet's option buffer and leave it at that.
+ */
+ if (option_size <= main_buffer_size - mainbufix) {
+ memcpy(&outpacket->options[mainbufix],
+ buffer, option_size);
+ mainbufix += option_size;
+ if (mainbufix < main_buffer_size)
+ outpacket->options[mainbufix++] = DHO_END;
+ length = DHCP_FIXED_NON_UDP + mainbufix;
+ } else {
+ outpacket->options[mainbufix++] = DHO_DHCP_OPTION_OVERLOAD;
+ outpacket->options[mainbufix++] = 1;
+ if (option_size >
+ main_buffer_size - mainbufix + DHCP_FILE_LEN)
+ outpacket->options[mainbufix++] = 3;
+ else
+ outpacket->options[mainbufix++] = 1;
+
+ memcpy(&outpacket->options[mainbufix],
+ buffer, main_buffer_size - mainbufix);
+ bufix = main_buffer_size - mainbufix;
+ length = DHCP_FIXED_NON_UDP + mainbufix;
+ if (overload & 1) {
+ if (option_size - bufix <= DHCP_FILE_LEN) {
+ memcpy(outpacket->file,
+ &buffer[bufix], option_size - bufix);
+ mainbufix = option_size - bufix;
+ if (mainbufix < DHCP_FILE_LEN)
+ outpacket->file[mainbufix++] = (char)DHO_END;
+ while (mainbufix < DHCP_FILE_LEN)
+ outpacket->file[mainbufix++] = (char)DHO_PAD;
+ } else {
+ memcpy(outpacket->file,
+ &buffer[bufix], DHCP_FILE_LEN);
+ bufix += DHCP_FILE_LEN;
+ }
+ }
+ if ((overload & 2) && option_size < bufix) {
+ memcpy(outpacket->sname,
+ &buffer[bufix], option_size - bufix);
+
+ mainbufix = option_size - bufix;
+ if (mainbufix < DHCP_SNAME_LEN)
+ outpacket->file[mainbufix++] = (char)DHO_END;
+ while (mainbufix < DHCP_SNAME_LEN)
+ outpacket->file[mainbufix++] = (char)DHO_PAD;
+ }
+ }
+ return (length);
+}
+
+/*
+ * Store all the requested options into the requested buffer.
+ */
+int
+store_options(unsigned char *buffer, int buflen, struct tree_cache **options,
+ unsigned char *priority_list, int priority_len, int first_cutoff,
+ int second_cutoff, int terminate)
+{
+ int bufix = 0, option_stored[256], i, ix, tto;
+
+ /* Zero out the stored-lengths array. */
+ memset(option_stored, 0, sizeof(option_stored));
+
+ /*
+ * Copy out the options in the order that they appear in the
+ * priority list...
+ */
+ for (i = 0; i < priority_len; i++) {
+ /* Code for next option to try to store. */
+ int code = priority_list[i];
+ int optstart;
+
+ /*
+ * Number of bytes left to store (some may already have
+ * been stored by a previous pass).
+ */
+ int length;
+
+ /* If no data is available for this option, skip it. */
+ if (!options[code]) {
+ continue;
+ }
+
+ /*
+ * The client could ask for things that are mandatory,
+ * in which case we should avoid storing them twice...
+ */
+ if (option_stored[code])
+ continue;
+ option_stored[code] = 1;
+
+ /* We should now have a constant length for the option. */
+ length = options[code]->len;
+
+ /* Do we add a NUL? */
+ if (terminate && dhcp_options[code].format[0] == 't') {
+ length++;
+ tto = 1;
+ } else
+ tto = 0;
+
+ /* Try to store the option. */
+
+ /*
+ * If the option's length is more than 255, we must
+ * store it in multiple hunks. Store 255-byte hunks
+ * first. However, in any case, if the option data will
+ * cross a buffer boundary, split it across that
+ * boundary.
+ */
+ ix = 0;
+
+ optstart = bufix;
+ while (length) {
+ unsigned char incr = length > 255 ? 255 : length;
+
+ /*
+ * If this hunk of the buffer will cross a
+ * boundary, only go up to the boundary in this
+ * pass.
+ */
+ if (bufix < first_cutoff &&
+ bufix + incr > first_cutoff)
+ incr = first_cutoff - bufix;
+ else if (bufix < second_cutoff &&
+ bufix + incr > second_cutoff)
+ incr = second_cutoff - bufix;
+
+ /*
+ * If this option is going to overflow the
+ * buffer, skip it.
+ */
+ if (bufix + 2 + incr > buflen) {
+ bufix = optstart;
+ break;
+ }
+
+ /* Everything looks good - copy it in! */
+ buffer[bufix] = code;
+ buffer[bufix + 1] = incr;
+ if (tto && incr == length) {
+ memcpy(buffer + bufix + 2,
+ options[code]->value + ix, incr - 1);
+ buffer[bufix + 2 + incr - 1] = 0;
+ } else
+ memcpy(buffer + bufix + 2,
+ options[code]->value + ix, incr);
+ length -= incr;
+ ix += incr;
+ bufix += 2 + incr;
+ }
+ }
+ return (bufix);
+}
+
+/*
+ * Format the specified option so that a human can easily read it.
+ */
+char *
+pretty_print_option(unsigned int code, unsigned char *data, int len,
+ int emit_commas, int emit_quotes)
+{
+ static char optbuf[32768]; /* XXX */
+ int hunksize = 0, numhunk = -1, numelem = 0;
+ char fmtbuf[32], *op = optbuf;
+ int i, j, k, opleft = sizeof(optbuf);
+ unsigned char *dp = data;
+ struct in_addr foo;
+ char comma;
+
+ /* Code should be between 0 and 255. */
+ if (code > 255)
+ error("pretty_print_option: bad code %d", code);
+
+ if (emit_commas)
+ comma = ',';
+ else
+ comma = ' ';
+
+ /* Figure out the size of the data. */
+ for (i = 0; dhcp_options[code].format[i]; i++) {
+ if (!numhunk) {
+ warning("%s: Excess information in format string: %s",
+ dhcp_options[code].name,
+ &(dhcp_options[code].format[i]));
+ break;
+ }
+ numelem++;
+ fmtbuf[i] = dhcp_options[code].format[i];
+ switch (dhcp_options[code].format[i]) {
+ case 'A':
+ --numelem;
+ fmtbuf[i] = 0;
+ numhunk = 0;
+ break;
+ case 'X':
+ for (k = 0; k < len; k++)
+ if (!isascii(data[k]) ||
+ !isprint(data[k]))
+ break;
+ if (k == len) {
+ fmtbuf[i] = 't';
+ numhunk = -2;
+ } else {
+ fmtbuf[i] = 'x';
+ hunksize++;
+ comma = ':';
+ numhunk = 0;
+ }
+ fmtbuf[i + 1] = 0;
+ break;
+ case 't':
+ fmtbuf[i] = 't';
+ fmtbuf[i + 1] = 0;
+ numhunk = -2;
+ break;
+ case 'I':
+ case 'l':
+ case 'L':
+ hunksize += 4;
+ break;
+ case 's':
+ case 'S':
+ hunksize += 2;
+ break;
+ case 'b':
+ case 'B':
+ case 'f':
+ hunksize++;
+ break;
+ case 'e':
+ break;
+ default:
+ warning("%s: garbage in format string: %s",
+ dhcp_options[code].name,
+ &(dhcp_options[code].format[i]));
+ break;
+ }
+ }
+
+ /* Check for too few bytes... */
+ if (hunksize > len) {
+ warning("%s: expecting at least %d bytes; got %d",
+ dhcp_options[code].name, hunksize, len);
+ return ("<error>");
+ }
+ /* Check for too many bytes... */
+ if (numhunk == -1 && hunksize < len)
+ warning("%s: %d extra bytes",
+ dhcp_options[code].name, len - hunksize);
+
+ /* If this is an array, compute its size. */
+ if (!numhunk)
+ numhunk = len / hunksize;
+ /* See if we got an exact number of hunks. */
+ if (numhunk > 0 && numhunk * hunksize < len)
+ warning("%s: %d extra bytes at end of array",
+ dhcp_options[code].name, len - numhunk * hunksize);
+
+ /* A one-hunk array prints the same as a single hunk. */
+ if (numhunk < 0)
+ numhunk = 1;
+
+ /* Cycle through the array (or hunk) printing the data. */
+ for (i = 0; i < numhunk; i++) {
+ for (j = 0; j < numelem; j++) {
+ int opcount;
+ switch (fmtbuf[j]) {
+ case 't':
+ if (emit_quotes) {
+ *op++ = '"';
+ opleft--;
+ }
+ for (; dp < data + len; dp++) {
+ if (!isascii(*dp) ||
+ !isprint(*dp)) {
+ if (dp + 1 != data + len ||
+ *dp != 0) {
+ snprintf(op, opleft,
+ "\\%03o", *dp);
+ op += 4;
+ opleft -= 4;
+ }
+ } else if (*dp == '"' ||
+ *dp == '\'' ||
+ *dp == '$' ||
+ *dp == '`' ||
+ *dp == '\\') {
+ *op++ = '\\';
+ *op++ = *dp;
+ opleft -= 2;
+ } else {
+ *op++ = *dp;
+ opleft--;
+ }
+ }
+ if (emit_quotes) {
+ *op++ = '"';
+ opleft--;
+ }
+
+ *op = 0;
+ break;
+ case 'I':
+ foo.s_addr = htonl(getULong(dp));
+ opcount = strlcpy(op, inet_ntoa(foo), opleft);
+ if (opcount >= opleft)
+ goto toobig;
+ opleft -= opcount;
+ dp += 4;
+ break;
+ case 'l':
+ opcount = snprintf(op, opleft, "%ld",
+ (long)getLong(dp));
+ if (opcount >= opleft || opcount == -1)
+ goto toobig;
+ opleft -= opcount;
+ dp += 4;
+ break;
+ case 'L':
+ opcount = snprintf(op, opleft, "%ld",
+ (unsigned long)getULong(dp));
+ if (opcount >= opleft || opcount == -1)
+ goto toobig;
+ opleft -= opcount;
+ dp += 4;
+ break;
+ case 's':
+ opcount = snprintf(op, opleft, "%d",
+ getShort(dp));
+ if (opcount >= opleft || opcount == -1)
+ goto toobig;
+ opleft -= opcount;
+ dp += 2;
+ break;
+ case 'S':
+ opcount = snprintf(op, opleft, "%d",
+ getUShort(dp));
+ if (opcount >= opleft || opcount == -1)
+ goto toobig;
+ opleft -= opcount;
+ dp += 2;
+ break;
+ case 'b':
+ opcount = snprintf(op, opleft, "%d",
+ *(char *)dp++);
+ if (opcount >= opleft || opcount == -1)
+ goto toobig;
+ opleft -= opcount;
+ break;
+ case 'B':
+ opcount = snprintf(op, opleft, "%d", *dp++);
+ if (opcount >= opleft || opcount == -1)
+ goto toobig;
+ opleft -= opcount;
+ break;
+ case 'x':
+ opcount = snprintf(op, opleft, "%x", *dp++);
+ if (opcount >= opleft || opcount == -1)
+ goto toobig;
+ opleft -= opcount;
+ break;
+ case 'f':
+ opcount = strlcpy(op,
+ *dp++ ? "true" : "false", opleft);
+ if (opcount >= opleft)
+ goto toobig;
+ opleft -= opcount;
+ break;
+ default:
+ warning("Unexpected format code %c", fmtbuf[j]);
+ }
+ op += strlen(op);
+ opleft -= strlen(op);
+ if (opleft < 1)
+ goto toobig;
+ if (j + 1 < numelem && comma != ':') {
+ *op++ = ' ';
+ opleft--;
+ }
+ }
+ if (i + 1 < numhunk) {
+ *op++ = comma;
+ opleft--;
+ }
+ if (opleft < 1)
+ goto toobig;
+
+ }
+ return (optbuf);
+ toobig:
+ warning("dhcp option too large");
+ return ("<error>");
+}
+
+void
+do_packet(struct interface_info *interface, struct dhcp_packet *packet,
+ int len, unsigned int from_port, struct iaddr from, struct hardware *hfrom)
+{
+ struct packet tp;
+ int i;
+
+ if (packet->hlen > sizeof(packet->chaddr)) {
+ note("Discarding packet with invalid hlen.");
+ return;
+ }
+
+ memset(&tp, 0, sizeof(tp));
+ tp.raw = packet;
+ tp.packet_length = len;
+ tp.client_port = from_port;
+ tp.client_addr = from;
+ tp.interface = interface;
+ tp.haddr = hfrom;
+
+ parse_options(&tp);
+ if (tp.options_valid &&
+ tp.options[DHO_DHCP_MESSAGE_TYPE].data)
+ tp.packet_type = tp.options[DHO_DHCP_MESSAGE_TYPE].data[0];
+ if (tp.packet_type)
+ dhcp(&tp);
+ else
+ bootp(&tp);
+
+ /* Free the data associated with the options. */
+ for (i = 0; i < 256; i++)
+ if (tp.options[i].len && tp.options[i].data)
+ free(tp.options[i].data);
+}
diff --git a/sbin/dhclient/packet.c b/sbin/dhclient/packet.c
new file mode 100644
index 0000000..484953c
--- /dev/null
+++ b/sbin/dhclient/packet.c
@@ -0,0 +1,256 @@
+/* $OpenBSD: packet.c,v 1.9 2004/05/04 18:58:50 deraadt Exp $ */
+
+/* Packet assembly code, originally contributed by Archie Cobbs. */
+
+/*
+ * Copyright (c) 1995, 1996, 1999 The Internet Software Consortium.
+ * 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "dhcpd.h"
+
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/if_ether.h>
+
+#define ETHER_HEADER_SIZE (ETHER_ADDR_LEN * 2 + sizeof(u_int16_t))
+
+u_int32_t checksum(unsigned char *, unsigned, u_int32_t);
+u_int32_t wrapsum(u_int32_t);
+
+void assemble_ethernet_header(struct interface_info *, unsigned char *,
+ int *, struct hardware *);
+ssize_t decode_ethernet_header(struct interface_info *, unsigned char *,
+ int bufix, struct hardware *);
+
+u_int32_t
+checksum(unsigned char *buf, unsigned nbytes, u_int32_t sum)
+{
+ int i;
+
+ /* Checksum all the pairs of bytes first... */
+ for (i = 0; i < (nbytes & ~1U); i += 2) {
+ sum += (u_int16_t)ntohs(*((u_int16_t *)(buf + i)));
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ }
+
+ /*
+ * If there's a single byte left over, checksum it, too.
+ * Network byte order is big-endian, so the remaining byte is
+ * the high byte.
+ */
+ if (i < nbytes) {
+ sum += buf[i] << 8;
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ }
+
+ return (sum);
+}
+
+u_int32_t
+wrapsum(u_int32_t sum)
+{
+ sum = ~sum & 0xFFFF;
+ return (htons(sum));
+}
+
+void
+assemble_hw_header(struct interface_info *interface, unsigned char *buf,
+ int *bufix, struct hardware *to)
+{
+ struct ether_header eh;
+
+ if (to != NULL && to->hlen == 6) /* XXX */
+ memcpy(eh.ether_dhost, to->haddr, sizeof(eh.ether_dhost));
+ else
+ memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
+ if (interface->hw_address.hlen == sizeof(eh.ether_shost))
+ memcpy(eh.ether_shost, interface->hw_address.haddr,
+ sizeof(eh.ether_shost));
+ else
+ memset(eh.ether_shost, 0x00, sizeof(eh.ether_shost));
+
+ eh.ether_type = htons(ETHERTYPE_IP);
+
+ memcpy(&buf[*bufix], &eh, ETHER_HEADER_SIZE);
+ *bufix += ETHER_HEADER_SIZE;
+}
+
+void
+assemble_udp_ip_header(unsigned char *buf, int *bufix, u_int32_t from,
+ u_int32_t to, unsigned int port, unsigned char *data, int len)
+{
+ struct ip ip;
+ struct udphdr udp;
+
+ ip.ip_v = 4;
+ ip.ip_hl = 5;
+ ip.ip_tos = IPTOS_LOWDELAY;
+ ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
+ ip.ip_id = 0;
+ ip.ip_off = 0;
+ ip.ip_ttl = 16;
+ ip.ip_p = IPPROTO_UDP;
+ ip.ip_sum = 0;
+ ip.ip_src.s_addr = from;
+ ip.ip_dst.s_addr = to;
+
+ ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
+ memcpy(&buf[*bufix], &ip, sizeof(ip));
+ *bufix += sizeof(ip);
+
+ udp.uh_sport = htons(LOCAL_PORT); /* XXX */
+ udp.uh_dport = port; /* XXX */
+ udp.uh_ulen = htons(sizeof(udp) + len);
+ memset(&udp.uh_sum, 0, sizeof(udp.uh_sum));
+
+ udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
+ checksum(data, len, checksum((unsigned char *)&ip.ip_src,
+ 2 * sizeof(ip.ip_src),
+ IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen)))));
+
+ memcpy(&buf[*bufix], &udp, sizeof(udp));
+ *bufix += sizeof(udp);
+}
+
+ssize_t
+decode_hw_header(unsigned char *buf, int bufix, struct hardware *from)
+{
+ struct ether_header eh;
+
+ memcpy(&eh, buf + bufix, ETHER_HEADER_SIZE);
+
+ memcpy(from->haddr, eh.ether_shost, sizeof(eh.ether_shost));
+ from->htype = ARPHRD_ETHER;
+ from->hlen = sizeof(eh.ether_shost);
+
+ return (sizeof(eh));
+}
+
+ssize_t
+decode_udp_ip_header(unsigned char *buf, int bufix, struct sockaddr_in *from,
+ unsigned char *data, int buflen)
+{
+ struct ip *ip;
+ struct udphdr *udp;
+ u_int32_t ip_len = (buf[bufix] & 0xf) << 2;
+ u_int32_t sum, usum;
+ static int ip_packets_seen;
+ static int ip_packets_bad_checksum;
+ static int udp_packets_seen;
+ static int udp_packets_bad_checksum;
+ static int udp_packets_length_checked;
+ static int udp_packets_length_overflow;
+ int len = 0;
+
+ ip = (struct ip *)(buf + bufix);
+ udp = (struct udphdr *)(buf + bufix + ip_len);
+
+ /* Check the IP header checksum - it should be zero. */
+ ip_packets_seen++;
+ if (wrapsum(checksum(buf + bufix, ip_len, 0)) != 0) {
+ ip_packets_bad_checksum++;
+ if (ip_packets_seen > 4 &&
+ (ip_packets_seen / ip_packets_bad_checksum) < 2) {
+ note("%d bad IP checksums seen in %d packets",
+ ip_packets_bad_checksum, ip_packets_seen);
+ ip_packets_seen = ip_packets_bad_checksum = 0;
+ }
+ return (-1);
+ }
+
+ if (ntohs(ip->ip_len) != buflen)
+ debug("ip length %d disagrees with bytes received %d.",
+ ntohs(ip->ip_len), buflen);
+
+ memcpy(&from->sin_addr, &ip->ip_src, 4);
+
+ /*
+ * Compute UDP checksums, including the ``pseudo-header'', the
+ * UDP header and the data. If the UDP checksum field is zero,
+ * we're not supposed to do a checksum.
+ */
+ if (!data) {
+ data = buf + bufix + ip_len + sizeof(*udp);
+ len = ntohs(udp->uh_ulen) - sizeof(*udp);
+ udp_packets_length_checked++;
+ if (len + data > buf + bufix + buflen) {
+ udp_packets_length_overflow++;
+ if (udp_packets_length_checked > 4 &&
+ (udp_packets_length_checked /
+ udp_packets_length_overflow) < 2) {
+ note("%d udp packets in %d too long - dropped",
+ udp_packets_length_overflow,
+ udp_packets_length_checked);
+ udp_packets_length_overflow =
+ udp_packets_length_checked = 0;
+ }
+ return (-1);
+ }
+ if (len + data != buf + bufix + buflen)
+ debug("accepting packet with data after udp payload.");
+ }
+
+ usum = udp->uh_sum;
+ udp->uh_sum = 0;
+
+ sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp),
+ checksum(data, len, checksum((unsigned char *)&ip->ip_src,
+ 2 * sizeof(ip->ip_src),
+ IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen)))));
+
+ udp_packets_seen++;
+ if (usum && usum != sum) {
+ udp_packets_bad_checksum++;
+ if (udp_packets_seen > 4 &&
+ (udp_packets_seen / udp_packets_bad_checksum) < 2) {
+ note("%d bad udp checksums in %d packets",
+ udp_packets_bad_checksum, udp_packets_seen);
+ udp_packets_seen = udp_packets_bad_checksum = 0;
+ }
+ return (-1);
+ }
+
+ memcpy(&from->sin_port, &udp->uh_sport, sizeof(udp->uh_sport));
+
+ return (ip_len + sizeof(*udp));
+}
diff --git a/sbin/dhclient/parse.c b/sbin/dhclient/parse.c
new file mode 100644
index 0000000..6a54fd0
--- /dev/null
+++ b/sbin/dhclient/parse.c
@@ -0,0 +1,580 @@
+/* $OpenBSD: parse.c,v 1.11 2004/05/05 23:07:47 deraadt Exp $ */
+
+/* Common parser code for dhcpd and dhclient. */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+ * 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "dhcpd.h"
+#include "dhctoken.h"
+
+/* Skip to the semicolon ending the current statement. If we encounter
+ * braces, the matching closing brace terminates the statement. If we
+ * encounter a right brace but haven't encountered a left brace, return
+ * leaving the brace in the token buffer for the caller. If we see a
+ * semicolon and haven't seen a left brace, return. This lets us skip
+ * over:
+ *
+ * statement;
+ * statement foo bar { }
+ * statement foo bar { statement { } }
+ * statement}
+ *
+ * ...et cetera.
+ */
+void
+skip_to_semi(FILE *cfile)
+{
+ int brace_count = 0, token;
+ char *val;
+
+ do {
+ token = peek_token(&val, cfile);
+ if (token == RBRACE) {
+ if (brace_count) {
+ token = next_token(&val, cfile);
+ if (!--brace_count)
+ return;
+ } else
+ return;
+ } else if (token == LBRACE) {
+ brace_count++;
+ } else if (token == SEMI && !brace_count) {
+ token = next_token(&val, cfile);
+ return;
+ } else if (token == '\n') {
+ /*
+ * EOL only happens when parsing
+ * /etc/resolv.conf, and we treat it like a
+ * semicolon because the resolv.conf file is
+ * line-oriented.
+ */
+ token = next_token(&val, cfile);
+ return;
+ }
+ token = next_token(&val, cfile);
+ } while (token != EOF);
+}
+
+int
+parse_semi(FILE *cfile)
+{
+ int token;
+ char *val;
+
+ token = next_token(&val, cfile);
+ if (token != SEMI) {
+ parse_warn("semicolon expected.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * string-parameter :== STRING SEMI
+ */
+char *
+parse_string(FILE *cfile)
+{
+ char *val, *s;
+ int token;
+
+ token = next_token(&val, cfile);
+ if (token != STRING) {
+ parse_warn("filename must be a string");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ s = malloc(strlen(val) + 1);
+ if (!s)
+ error("no memory for string %s.", val);
+ strlcpy(s, val, strlen(val) + 1);
+
+ if (!parse_semi(cfile))
+ return (NULL);
+ return (s);
+}
+
+int
+parse_ip_addr(FILE *cfile, struct iaddr *addr)
+{
+ addr->len = 4;
+ if (parse_numeric_aggregate(cfile, addr->iabuf,
+ &addr->len, DOT, 10, 8))
+ return (1);
+ return (0);
+}
+
+/*
+ * hardware-parameter :== HARDWARE ETHERNET csns SEMI
+ * csns :== NUMBER | csns COLON NUMBER
+ */
+void
+parse_hardware_param(FILE *cfile, struct hardware *hardware)
+{
+ unsigned char *t;
+ int token, hlen;
+ char *val;
+
+ token = next_token(&val, cfile);
+ switch (token) {
+ case ETHERNET:
+ hardware->htype = HTYPE_ETHER;
+ break;
+ case TOKEN_RING:
+ hardware->htype = HTYPE_IEEE802;
+ break;
+ case FDDI:
+ hardware->htype = HTYPE_FDDI;
+ break;
+ default:
+ parse_warn("expecting a network hardware type");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ /*
+ * Parse the hardware address information. Technically, it
+ * would make a lot of sense to restrict the length of the data
+ * we'll accept here to the length of a particular hardware
+ * address type. Unfortunately, there are some broken clients
+ * out there that put bogus data in the chaddr buffer, and we
+ * accept that data in the lease file rather than simply failing
+ * on such clients. Yuck.
+ */
+ hlen = 0;
+ t = parse_numeric_aggregate(cfile, NULL, &hlen, COLON, 16, 8);
+ if (!t)
+ return;
+ if (hlen > sizeof(hardware->haddr)) {
+ free(t);
+ parse_warn("hardware address too long");
+ } else {
+ hardware->hlen = hlen;
+ memcpy((unsigned char *)&hardware->haddr[0], t,
+ hardware->hlen);
+ if (hlen < sizeof(hardware->haddr))
+ memset(&hardware->haddr[hlen], 0,
+ sizeof(hardware->haddr) - hlen);
+ free(t);
+ }
+
+ token = next_token(&val, cfile);
+ if (token != SEMI) {
+ parse_warn("expecting semicolon.");
+ skip_to_semi(cfile);
+ }
+}
+
+/*
+ * lease-time :== NUMBER SEMI
+ */
+void
+parse_lease_time(FILE *cfile, time_t *timep)
+{
+ char *val;
+ int token;
+
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+ parse_warn("Expecting numeric lease time");
+ skip_to_semi(cfile);
+ return;
+ }
+ convert_num((unsigned char *)timep, val, 10, 32);
+ /* Unswap the number - convert_num returns stuff in NBO. */
+ *timep = ntohl(*timep); /* XXX */
+
+ parse_semi(cfile);
+}
+
+/*
+ * No BNF for numeric aggregates - that's defined by the caller. What
+ * this function does is to parse a sequence of numbers separated by the
+ * token specified in separator. If max is zero, any number of numbers
+ * will be parsed; otherwise, exactly max numbers are expected. Base
+ * and size tell us how to internalize the numbers once they've been
+ * tokenized.
+ */
+unsigned char *
+parse_numeric_aggregate(FILE *cfile, unsigned char *buf, int *max,
+ int separator, int base, int size)
+{
+ unsigned char *bufp = buf, *s = NULL;
+ int token, count = 0;
+ char *val, *t;
+ pair c = NULL;
+
+ if (!bufp && *max) {
+ bufp = malloc(*max * size / 8);
+ if (!bufp)
+ error("can't allocate space for numeric aggregate");
+ } else
+ s = bufp;
+
+ do {
+ if (count) {
+ token = peek_token(&val, cfile);
+ if (token != separator) {
+ if (!*max)
+ break;
+ if (token != RBRACE && token != LBRACE)
+ token = next_token(&val, cfile);
+ parse_warn("too few numbers.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ token = next_token(&val, cfile);
+ }
+ token = next_token(&val, cfile);
+
+ if (token == EOF) {
+ parse_warn("unexpected end of file");
+ break;
+ }
+
+ /* Allow NUMBER_OR_NAME if base is 16. */
+ if (token != NUMBER &&
+ (base != 16 || token != NUMBER_OR_NAME)) {
+ parse_warn("expecting numeric value.");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ /*
+ * If we can, convert the number now; otherwise, build a
+ * linked list of all the numbers.
+ */
+ if (s) {
+ convert_num(s, val, base, size);
+ s += size / 8;
+ } else {
+ t = malloc(strlen(val) + 1);
+ if (!t)
+ error("no temp space for number.");
+ strlcpy(t, val, strlen(val) + 1);
+ c = cons(t, c);
+ }
+ } while (++count != *max);
+
+ /* If we had to cons up a list, convert it now. */
+ if (c) {
+ bufp = malloc(count * size / 8);
+ if (!bufp)
+ error("can't allocate space for numeric aggregate.");
+ s = bufp + count - size / 8;
+ *max = count;
+ }
+ while (c) {
+ pair cdr = c->cdr;
+ convert_num(s, (char *)c->car, base, size);
+ s -= size / 8;
+ /* Free up temp space. */
+ free(c->car);
+ free(c);
+ c = cdr;
+ }
+ return (bufp);
+}
+
+void
+convert_num(unsigned char *buf, char *str, int base, int size)
+{
+ int negative = 0, tval, max;
+ u_int32_t val = 0;
+ char *ptr = str;
+
+ if (*ptr == '-') {
+ negative = 1;
+ ptr++;
+ }
+
+ /* If base wasn't specified, figure it out from the data. */
+ if (!base) {
+ if (ptr[0] == '0') {
+ if (ptr[1] == 'x') {
+ base = 16;
+ ptr += 2;
+ } else if (isascii(ptr[1]) && isdigit(ptr[1])) {
+ base = 8;
+ ptr += 1;
+ } else
+ base = 10;
+ } else
+ base = 10;
+ }
+
+ do {
+ tval = *ptr++;
+ /* XXX assumes ASCII... */
+ if (tval >= 'a')
+ tval = tval - 'a' + 10;
+ else if (tval >= 'A')
+ tval = tval - 'A' + 10;
+ else if (tval >= '0')
+ tval -= '0';
+ else {
+ warning("Bogus number: %s.", str);
+ break;
+ }
+ if (tval >= base) {
+ warning("Bogus number: %s: digit %d not in base %d",
+ str, tval, base);
+ break;
+ }
+ val = val * base + tval;
+ } while (*ptr);
+
+ if (negative)
+ max = (1 << (size - 1));
+ else
+ max = (1 << (size - 1)) + ((1 << (size - 1)) - 1);
+ if (val > max) {
+ switch (base) {
+ case 8:
+ warning("value %s%o exceeds max (%d) for precision.",
+ negative ? "-" : "", val, max);
+ break;
+ case 16:
+ warning("value %s%x exceeds max (%d) for precision.",
+ negative ? "-" : "", val, max);
+ break;
+ default:
+ warning("value %s%u exceeds max (%d) for precision.",
+ negative ? "-" : "", val, max);
+ break;
+ }
+ }
+
+ if (negative)
+ switch (size) {
+ case 8:
+ *buf = -(unsigned long)val;
+ break;
+ case 16:
+ putShort(buf, -(unsigned long)val);
+ break;
+ case 32:
+ putLong(buf, -(unsigned long)val);
+ break;
+ default:
+ warning("Unexpected integer size: %d", size);
+ break;
+ }
+ else
+ switch (size) {
+ case 8:
+ *buf = (u_int8_t)val;
+ break;
+ case 16:
+ putUShort(buf, (u_int16_t)val);
+ break;
+ case 32:
+ putULong(buf, val);
+ break;
+ default:
+ warning("Unexpected integer size: %d", size);
+ break;
+ }
+}
+
+/*
+ * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
+ * NUMBER COLON NUMBER COLON NUMBER SEMI
+ *
+ * Dates are always in GMT; first number is day of week; next is
+ * year/month/day; next is hours:minutes:seconds on a 24-hour
+ * clock.
+ */
+time_t
+parse_date(FILE *cfile)
+{
+ static int months[11] = { 31, 59, 90, 120, 151, 181,
+ 212, 243, 273, 304, 334 };
+ int guess, token;
+ struct tm tm;
+ char *val;
+
+ /* Day of week... */
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+ parse_warn("numeric day of week expected.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (0);
+ }
+ tm.tm_wday = atoi(val);
+
+ /* Year... */
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+ parse_warn("numeric year expected.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (0);
+ }
+ tm.tm_year = atoi(val);
+ if (tm.tm_year > 1900)
+ tm.tm_year -= 1900;
+
+ /* Slash separating year from month... */
+ token = next_token(&val, cfile);
+ if (token != SLASH) {
+ parse_warn("expected slash separating year from month.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (0);
+ }
+
+ /* Month... */
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+ parse_warn("numeric month expected.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (0);
+ }
+ tm.tm_mon = atoi(val) - 1;
+
+ /* Slash separating month from day... */
+ token = next_token(&val, cfile);
+ if (token != SLASH) {
+ parse_warn("expected slash separating month from day.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (0);
+ }
+
+ /* Month... */
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+ parse_warn("numeric day of month expected.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (0);
+ }
+ tm.tm_mday = atoi(val);
+
+ /* Hour... */
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+ parse_warn("numeric hour expected.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (0);
+ }
+ tm.tm_hour = atoi(val);
+
+ /* Colon separating hour from minute... */
+ token = next_token(&val, cfile);
+ if (token != COLON) {
+ parse_warn("expected colon separating hour from minute.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (0);
+ }
+
+ /* Minute... */
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+ parse_warn("numeric minute expected.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (0);
+ }
+ tm.tm_min = atoi(val);
+
+ /* Colon separating minute from second... */
+ token = next_token(&val, cfile);
+ if (token != COLON) {
+ parse_warn("expected colon separating hour from minute.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (0);
+ }
+
+ /* Minute... */
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+ parse_warn("numeric minute expected.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (0);
+ }
+ tm.tm_sec = atoi(val);
+ tm.tm_isdst = 0;
+
+ /* XXX: We assume that mktime does not use tm_yday. */
+ tm.tm_yday = 0;
+
+ /* Make sure the date ends in a semicolon... */
+ token = next_token(&val, cfile);
+ if (token != SEMI) {
+ parse_warn("semicolon expected.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+
+ /* Guess the time value... */
+ guess = ((((((365 * (tm.tm_year - 70) + /* Days in years since '70 */
+ (tm.tm_year - 69) / 4 + /* Leap days since '70 */
+ (tm.tm_mon /* Days in months this year */
+ ? months[tm.tm_mon - 1]
+ : 0) +
+ (tm.tm_mon > 1 && /* Leap day this year */
+ !((tm.tm_year - 72) & 3)) +
+ tm.tm_mday - 1) * 24) + /* Day of month */
+ tm.tm_hour) * 60) +
+ tm.tm_min) * 60) + tm.tm_sec;
+
+ /*
+ * This guess could be wrong because of leap seconds or other
+ * weirdness we don't know about that the system does. For
+ * now, we're just going to accept the guess, but at some point
+ * it might be nice to do a successive approximation here to get
+ * an exact value. Even if the error is small, if the server
+ * is restarted frequently (and thus the lease database is
+ * reread), the error could accumulate into something
+ * significant.
+ */
+ return (guess);
+}
diff --git a/sbin/dhclient/privsep.c b/sbin/dhclient/privsep.c
new file mode 100644
index 0000000..b42572f
--- /dev/null
+++ b/sbin/dhclient/privsep.c
@@ -0,0 +1,238 @@
+/* $OpenBSD: privsep.c,v 1.7 2004/05/10 18:34:42 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE, ABUSE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "dhcpd.h"
+#include "privsep.h"
+
+struct buf *
+buf_open(size_t len)
+{
+ struct buf *buf;
+
+ if ((buf = calloc(1, sizeof(struct buf))) == NULL)
+ return (NULL);
+ if ((buf->buf = malloc(len)) == NULL) {
+ free(buf);
+ return (NULL);
+ }
+ buf->size = len;
+
+ return (buf);
+}
+
+int
+buf_add(struct buf *buf, void *data, size_t len)
+{
+ if (buf->wpos + len > buf->size)
+ return (-1);
+
+ memcpy(buf->buf + buf->wpos, data, len);
+ buf->wpos += len;
+ return (0);
+}
+
+int
+buf_close(int sock, struct buf *buf)
+{
+ ssize_t n;
+
+ do {
+ n = write(sock, buf->buf + buf->rpos, buf->size - buf->rpos);
+ if (n != -1)
+ buf->rpos += n;
+ if (n == 0) { /* connection closed */
+ errno = 0;
+ return (-1);
+ }
+ } while (n == -1 && (errno == EAGAIN || errno == EINTR));
+
+ if (buf->rpos < buf->size)
+ error("short write: wanted %lu got %ld bytes",
+ (unsigned long)buf->size, (long)buf->rpos);
+
+ free(buf->buf);
+ free(buf);
+ return (n);
+}
+
+ssize_t
+buf_read(int sock, void *buf, size_t nbytes)
+{
+ ssize_t n, r = 0;
+ char *p = buf;
+
+ do {
+ n = read(sock, p, nbytes);
+ if (n == 0)
+ error("connection closed");
+ if (n != -1) {
+ r += n;
+ p += n;
+ nbytes -= n;
+ }
+ } while (n == -1 && (errno == EINTR || errno == EAGAIN));
+
+ if (n == -1)
+ error("buf_read: %m");
+
+ if (r < nbytes)
+ error("short read: wanted %lu got %ld bytes",
+ (unsigned long)nbytes, (long)r);
+
+ return (r);
+}
+
+void
+dispatch_imsg(int fd)
+{
+ struct imsg_hdr hdr;
+ char *medium, *reason, *filename,
+ *servername, *prefix;
+ size_t medium_len, reason_len, filename_len,
+ servername_len, prefix_len, totlen;
+ struct client_lease lease;
+ int ret, i, optlen;
+ struct buf *buf;
+
+ buf_read(fd, &hdr, sizeof(hdr));
+
+ switch (hdr.code) {
+ case IMSG_SCRIPT_INIT:
+ if (hdr.len < sizeof(hdr) + sizeof(size_t))
+ error("corrupted message received");
+ buf_read(fd, &medium_len, sizeof(medium_len));
+ if (hdr.len < medium_len + sizeof(size_t) + sizeof(hdr)
+ + sizeof(size_t) || medium_len == SIZE_T_MAX)
+ error("corrupted message received");
+ if (medium_len > 0) {
+ if ((medium = calloc(1, medium_len + 1)) == NULL)
+ error("%m");
+ buf_read(fd, medium, medium_len);
+ } else
+ medium = NULL;
+
+ buf_read(fd, &reason_len, sizeof(reason_len));
+ if (hdr.len < medium_len + reason_len + sizeof(hdr) ||
+ reason_len == SIZE_T_MAX)
+ error("corrupted message received");
+ if (reason_len > 0) {
+ if ((reason = calloc(1, reason_len + 1)) == NULL)
+ error("%m");
+ buf_read(fd, reason, reason_len);
+ } else
+ reason = NULL;
+
+ priv_script_init(reason, medium);
+ free(reason);
+ free(medium);
+ break;
+ case IMSG_SCRIPT_WRITE_PARAMS:
+ bzero(&lease, sizeof lease);
+ totlen = sizeof(hdr) + sizeof(lease) + sizeof(size_t);
+ if (hdr.len < totlen)
+ error("corrupted message received");
+ buf_read(fd, &lease, sizeof(lease));
+
+ buf_read(fd, &filename_len, sizeof(filename_len));
+ totlen += filename_len + sizeof(size_t);
+ if (hdr.len < totlen || filename_len == SIZE_T_MAX)
+ error("corrupted message received");
+ if (filename_len > 0) {
+ if ((filename = calloc(1, filename_len + 1)) == NULL)
+ error("%m");
+ buf_read(fd, filename, filename_len);
+ } else
+ filename = NULL;
+
+ buf_read(fd, &servername_len, sizeof(servername_len));
+ totlen += servername_len + sizeof(size_t);
+ if (hdr.len < totlen || servername_len == SIZE_T_MAX)
+ error("corrupted message received");
+ if (servername_len > 0) {
+ if ((servername =
+ calloc(1, servername_len + 1)) == NULL)
+ error("%m");
+ buf_read(fd, servername, servername_len);
+ } else
+ servername = NULL;
+
+ buf_read(fd, &prefix_len, sizeof(prefix_len));
+ totlen += prefix_len;
+ if (hdr.len < totlen || prefix_len == SIZE_T_MAX)
+ error("corrupted message received");
+ if (prefix_len > 0) {
+ if ((prefix = calloc(1, prefix_len + 1)) == NULL)
+ error("%m");
+ buf_read(fd, prefix, prefix_len);
+ } else
+ prefix = NULL;
+
+ for (i = 0; i < 256; i++) {
+ totlen += sizeof(optlen);
+ if (hdr.len < totlen)
+ error("corrupted message received");
+ buf_read(fd, &optlen, sizeof(optlen));
+ lease.options[i].data = NULL;
+ lease.options[i].len = optlen;
+ if (optlen > 0) {
+ totlen += optlen;
+ if (hdr.len < totlen || optlen == SIZE_T_MAX)
+ error("corrupted message received");
+ lease.options[i].data =
+ calloc(1, optlen + 1);
+ if (lease.options[i].data == NULL)
+ error("%m");
+ buf_read(fd, lease.options[i].data, optlen);
+ }
+ }
+ lease.server_name = servername;
+ lease.filename = filename;
+
+ priv_script_write_params(prefix, &lease);
+
+ free(servername);
+ free(filename);
+ free(prefix);
+ for (i = 0; i < 256; i++)
+ if (lease.options[i].len > 0)
+ free(lease.options[i].data);
+ break;
+ case IMSG_SCRIPT_GO:
+ if (hdr.len != sizeof(hdr))
+ error("corrupted message received");
+
+ ret = priv_script_go();
+
+ hdr.code = IMSG_SCRIPT_GO_RET;
+ hdr.len = sizeof(struct imsg_hdr) + sizeof(int);
+ if ((buf = buf_open(hdr.len)) == NULL)
+ error("buf_open: %m");
+ if (buf_add(buf, &hdr, sizeof(hdr)))
+ error("buf_add: %m");
+ if (buf_add(buf, &ret, sizeof(ret)))
+ error("buf_add: %m");
+ if (buf_close(fd, buf) == -1)
+ error("buf_close: %m");
+ break;
+ default:
+ error("received unknown message, code %d", hdr.code);
+ }
+}
diff --git a/sbin/dhclient/privsep.h b/sbin/dhclient/privsep.h
new file mode 100644
index 0000000..f30284e
--- /dev/null
+++ b/sbin/dhclient/privsep.h
@@ -0,0 +1,47 @@
+/* $OpenBSD: privsep.h,v 1.2 2004/05/04 18:51:18 henning Exp $ */
+
+/*
+ * Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE, ABUSE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <poll.h>
+#include <pwd.h>
+
+struct buf {
+ u_char *buf;
+ size_t size;
+ size_t wpos;
+ size_t rpos;
+};
+
+enum imsg_code {
+ IMSG_NONE,
+ IMSG_SCRIPT_INIT,
+ IMSG_SCRIPT_WRITE_PARAMS,
+ IMSG_SCRIPT_GO,
+ IMSG_SCRIPT_GO_RET
+};
+
+struct imsg_hdr {
+ enum imsg_code code;
+ size_t len;
+};
+
+struct buf *buf_open(size_t);
+int buf_add(struct buf *, void *, size_t);
+int buf_close(int, struct buf *);
+ssize_t buf_read(int sock, void *, size_t);
diff --git a/sbin/dhclient/tables.c b/sbin/dhclient/tables.c
new file mode 100644
index 0000000..2c3add2
--- /dev/null
+++ b/sbin/dhclient/tables.c
@@ -0,0 +1,433 @@
+/* $OpenBSD: tables.c,v 1.4 2004/05/04 20:28:40 deraadt Exp $ */
+
+/* Tables of information... */
+
+/*
+ * Copyright (c) 1995, 1996 The Internet Software Consortium.
+ * 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "dhcpd.h"
+
+/*
+ * DHCP Option names, formats and codes, from RFC1533.
+ *
+ * Format codes:
+ *
+ * e - end of data
+ * I - IP address
+ * l - 32-bit signed integer
+ * L - 32-bit unsigned integer
+ * s - 16-bit signed integer
+ * S - 16-bit unsigned integer
+ * b - 8-bit signed integer
+ * B - 8-bit unsigned integer
+ * t - ASCII text
+ * f - flag (true or false)
+ * A - array of whatever precedes (e.g., IA means array of IP addresses)
+ */
+
+struct universe dhcp_universe;
+struct option dhcp_options[256] = {
+ { "pad", "", &dhcp_universe, 0 },
+ { "subnet-mask", "I", &dhcp_universe, 1 },
+ { "time-offset", "l", &dhcp_universe, 2 },
+ { "routers", "IA", &dhcp_universe, 3 },
+ { "time-servers", "IA", &dhcp_universe, 4 },
+ { "ien116-name-servers", "IA", &dhcp_universe, 5 },
+ { "domain-name-servers", "IA", &dhcp_universe, 6 },
+ { "log-servers", "IA", &dhcp_universe, 7 },
+ { "cookie-servers", "IA", &dhcp_universe, 8 },
+ { "lpr-servers", "IA", &dhcp_universe, 9 },
+ { "impress-servers", "IA", &dhcp_universe, 10 },
+ { "resource-location-servers", "IA", &dhcp_universe, 11 },
+ { "host-name", "t", &dhcp_universe, 12 },
+ { "boot-size", "S", &dhcp_universe, 13 },
+ { "merit-dump", "t", &dhcp_universe, 14 },
+ { "domain-name", "t", &dhcp_universe, 15 },
+ { "swap-server", "I", &dhcp_universe, 16 },
+ { "root-path", "t", &dhcp_universe, 17 },
+ { "extensions-path", "t", &dhcp_universe, 18 },
+ { "ip-forwarding", "f", &dhcp_universe, 19 },
+ { "non-local-source-routing", "f", &dhcp_universe, 20 },
+ { "policy-filter", "IIA", &dhcp_universe, 21 },
+ { "max-dgram-reassembly", "S", &dhcp_universe, 22 },
+ { "default-ip-ttl", "B", &dhcp_universe, 23 },
+ { "path-mtu-aging-timeout", "L", &dhcp_universe, 24 },
+ { "path-mtu-plateau-table", "SA", &dhcp_universe, 25 },
+ { "interface-mtu", "S", &dhcp_universe, 26 },
+ { "all-subnets-local", "f", &dhcp_universe, 27 },
+ { "broadcast-address", "I", &dhcp_universe, 28 },
+ { "perform-mask-discovery", "f", &dhcp_universe, 29 },
+ { "mask-supplier", "f", &dhcp_universe, 30 },
+ { "router-discovery", "f", &dhcp_universe, 31 },
+ { "router-solicitation-address", "I", &dhcp_universe, 32 },
+ { "static-routes", "IIA", &dhcp_universe, 33 },
+ { "trailer-encapsulation", "f", &dhcp_universe, 34 },
+ { "arp-cache-timeout", "L", &dhcp_universe, 35 },
+ { "ieee802-3-encapsulation", "f", &dhcp_universe, 36 },
+ { "default-tcp-ttl", "B", &dhcp_universe, 37 },
+ { "tcp-keepalive-interval", "L", &dhcp_universe, 38 },
+ { "tcp-keepalive-garbage", "f", &dhcp_universe, 39 },
+ { "nis-domain", "t", &dhcp_universe, 40 },
+ { "nis-servers", "IA", &dhcp_universe, 41 },
+ { "ntp-servers", "IA", &dhcp_universe, 42 },
+ { "vendor-encapsulated-options", "X", &dhcp_universe, 43 },
+ { "netbios-name-servers", "IA", &dhcp_universe, 44 },
+ { "netbios-dd-server", "IA", &dhcp_universe, 45 },
+ { "netbios-node-type", "B", &dhcp_universe, 46 },
+ { "netbios-scope", "t", &dhcp_universe, 47 },
+ { "font-servers", "IA", &dhcp_universe, 48 },
+ { "x-display-manager", "IA", &dhcp_universe, 49 },
+ { "dhcp-requested-address", "I", &dhcp_universe, 50 },
+ { "dhcp-lease-time", "L", &dhcp_universe, 51 },
+ { "dhcp-option-overload", "B", &dhcp_universe, 52 },
+ { "dhcp-message-type", "B", &dhcp_universe, 53 },
+ { "dhcp-server-identifier", "I", &dhcp_universe, 54 },
+ { "dhcp-parameter-request-list", "BA", &dhcp_universe, 55 },
+ { "dhcp-message", "t", &dhcp_universe, 56 },
+ { "dhcp-max-message-size", "S", &dhcp_universe, 57 },
+ { "dhcp-renewal-time", "L", &dhcp_universe, 58 },
+ { "dhcp-rebinding-time", "L", &dhcp_universe, 59 },
+ { "dhcp-class-identifier", "t", &dhcp_universe, 60 },
+ { "dhcp-client-identifier", "X", &dhcp_universe, 61 },
+ { "option-62", "X", &dhcp_universe, 62 },
+ { "option-63", "X", &dhcp_universe, 63 },
+ { "nisplus-domain", "t", &dhcp_universe, 64 },
+ { "nisplus-servers", "IA", &dhcp_universe, 65 },
+ { "tftp-server-name", "t", &dhcp_universe, 66 },
+ { "bootfile-name", "t", &dhcp_universe, 67 },
+ { "mobile-ip-home-agent", "IA", &dhcp_universe, 68 },
+ { "smtp-server", "IA", &dhcp_universe, 69 },
+ { "pop-server", "IA", &dhcp_universe, 70 },
+ { "nntp-server", "IA", &dhcp_universe, 71 },
+ { "www-server", "IA", &dhcp_universe, 72 },
+ { "finger-server", "IA", &dhcp_universe, 73 },
+ { "irc-server", "IA", &dhcp_universe, 74 },
+ { "streettalk-server", "IA", &dhcp_universe, 75 },
+ { "streettalk-directory-assistance-server", "IA", &dhcp_universe, 76 },
+ { "user-class", "t", &dhcp_universe, 77 },
+ { "option-78", "X", &dhcp_universe, 78 },
+ { "option-79", "X", &dhcp_universe, 79 },
+ { "option-80", "X", &dhcp_universe, 80 },
+ { "option-81", "X", &dhcp_universe, 81 },
+ { "option-82", "X", &dhcp_universe, 82 },
+ { "option-83", "X", &dhcp_universe, 83 },
+ { "option-84", "X", &dhcp_universe, 84 },
+ { "nds-servers", "IA", &dhcp_universe, 85 },
+ { "nds-tree-name", "X", &dhcp_universe, 86 },
+ { "nds-context", "X", &dhcp_universe, 87 },
+ { "option-88", "X", &dhcp_universe, 88 },
+ { "option-89", "X", &dhcp_universe, 89 },
+ { "option-90", "X", &dhcp_universe, 90 },
+ { "option-91", "X", &dhcp_universe, 91 },
+ { "option-92", "X", &dhcp_universe, 92 },
+ { "option-93", "X", &dhcp_universe, 93 },
+ { "option-94", "X", &dhcp_universe, 94 },
+ { "option-95", "X", &dhcp_universe, 95 },
+ { "option-96", "X", &dhcp_universe, 96 },
+ { "option-97", "X", &dhcp_universe, 97 },
+ { "option-98", "X", &dhcp_universe, 98 },
+ { "option-99", "X", &dhcp_universe, 99 },
+ { "option-100", "X", &dhcp_universe, 100 },
+ { "option-101", "X", &dhcp_universe, 101 },
+ { "option-102", "X", &dhcp_universe, 102 },
+ { "option-103", "X", &dhcp_universe, 103 },
+ { "option-104", "X", &dhcp_universe, 104 },
+ { "option-105", "X", &dhcp_universe, 105 },
+ { "option-106", "X", &dhcp_universe, 106 },
+ { "option-107", "X", &dhcp_universe, 107 },
+ { "option-108", "X", &dhcp_universe, 108 },
+ { "option-109", "X", &dhcp_universe, 109 },
+ { "option-110", "X", &dhcp_universe, 110 },
+ { "option-111", "X", &dhcp_universe, 111 },
+ { "option-112", "X", &dhcp_universe, 112 },
+ { "option-113", "X", &dhcp_universe, 113 },
+ { "option-114", "X", &dhcp_universe, 114 },
+ { "option-115", "X", &dhcp_universe, 115 },
+ { "option-116", "X", &dhcp_universe, 116 },
+ { "option-117", "X", &dhcp_universe, 117 },
+ { "option-118", "X", &dhcp_universe, 118 },
+ { "option-119", "X", &dhcp_universe, 119 },
+ { "option-120", "X", &dhcp_universe, 120 },
+ { "option-121", "X", &dhcp_universe, 121 },
+ { "option-122", "X", &dhcp_universe, 122 },
+ { "option-123", "X", &dhcp_universe, 123 },
+ { "option-124", "X", &dhcp_universe, 124 },
+ { "option-125", "X", &dhcp_universe, 125 },
+ { "option-126", "X", &dhcp_universe, 126 },
+ { "option-127", "X", &dhcp_universe, 127 },
+ { "option-128", "X", &dhcp_universe, 128 },
+ { "option-129", "X", &dhcp_universe, 129 },
+ { "option-130", "X", &dhcp_universe, 130 },
+ { "option-131", "X", &dhcp_universe, 131 },
+ { "option-132", "X", &dhcp_universe, 132 },
+ { "option-133", "X", &dhcp_universe, 133 },
+ { "option-134", "X", &dhcp_universe, 134 },
+ { "option-135", "X", &dhcp_universe, 135 },
+ { "option-136", "X", &dhcp_universe, 136 },
+ { "option-137", "X", &dhcp_universe, 137 },
+ { "option-138", "X", &dhcp_universe, 138 },
+ { "option-139", "X", &dhcp_universe, 139 },
+ { "option-140", "X", &dhcp_universe, 140 },
+ { "option-141", "X", &dhcp_universe, 141 },
+ { "option-142", "X", &dhcp_universe, 142 },
+ { "option-143", "X", &dhcp_universe, 143 },
+ { "option-144", "X", &dhcp_universe, 144 },
+ { "option-145", "X", &dhcp_universe, 145 },
+ { "option-146", "X", &dhcp_universe, 146 },
+ { "option-147", "X", &dhcp_universe, 147 },
+ { "option-148", "X", &dhcp_universe, 148 },
+ { "option-149", "X", &dhcp_universe, 149 },
+ { "option-150", "X", &dhcp_universe, 150 },
+ { "option-151", "X", &dhcp_universe, 151 },
+ { "option-152", "X", &dhcp_universe, 152 },
+ { "option-153", "X", &dhcp_universe, 153 },
+ { "option-154", "X", &dhcp_universe, 154 },
+ { "option-155", "X", &dhcp_universe, 155 },
+ { "option-156", "X", &dhcp_universe, 156 },
+ { "option-157", "X", &dhcp_universe, 157 },
+ { "option-158", "X", &dhcp_universe, 158 },
+ { "option-159", "X", &dhcp_universe, 159 },
+ { "option-160", "X", &dhcp_universe, 160 },
+ { "option-161", "X", &dhcp_universe, 161 },
+ { "option-162", "X", &dhcp_universe, 162 },
+ { "option-163", "X", &dhcp_universe, 163 },
+ { "option-164", "X", &dhcp_universe, 164 },
+ { "option-165", "X", &dhcp_universe, 165 },
+ { "option-166", "X", &dhcp_universe, 166 },
+ { "option-167", "X", &dhcp_universe, 167 },
+ { "option-168", "X", &dhcp_universe, 168 },
+ { "option-169", "X", &dhcp_universe, 169 },
+ { "option-170", "X", &dhcp_universe, 170 },
+ { "option-171", "X", &dhcp_universe, 171 },
+ { "option-172", "X", &dhcp_universe, 172 },
+ { "option-173", "X", &dhcp_universe, 173 },
+ { "option-174", "X", &dhcp_universe, 174 },
+ { "option-175", "X", &dhcp_universe, 175 },
+ { "option-176", "X", &dhcp_universe, 176 },
+ { "option-177", "X", &dhcp_universe, 177 },
+ { "option-178", "X", &dhcp_universe, 178 },
+ { "option-179", "X", &dhcp_universe, 179 },
+ { "option-180", "X", &dhcp_universe, 180 },
+ { "option-181", "X", &dhcp_universe, 181 },
+ { "option-182", "X", &dhcp_universe, 182 },
+ { "option-183", "X", &dhcp_universe, 183 },
+ { "option-184", "X", &dhcp_universe, 184 },
+ { "option-185", "X", &dhcp_universe, 185 },
+ { "option-186", "X", &dhcp_universe, 186 },
+ { "option-187", "X", &dhcp_universe, 187 },
+ { "option-188", "X", &dhcp_universe, 188 },
+ { "option-189", "X", &dhcp_universe, 189 },
+ { "option-190", "X", &dhcp_universe, 190 },
+ { "option-191", "X", &dhcp_universe, 191 },
+ { "option-192", "X", &dhcp_universe, 192 },
+ { "option-193", "X", &dhcp_universe, 193 },
+ { "option-194", "X", &dhcp_universe, 194 },
+ { "option-195", "X", &dhcp_universe, 195 },
+ { "option-196", "X", &dhcp_universe, 196 },
+ { "option-197", "X", &dhcp_universe, 197 },
+ { "option-198", "X", &dhcp_universe, 198 },
+ { "option-199", "X", &dhcp_universe, 199 },
+ { "option-200", "X", &dhcp_universe, 200 },
+ { "option-201", "X", &dhcp_universe, 201 },
+ { "option-202", "X", &dhcp_universe, 202 },
+ { "option-203", "X", &dhcp_universe, 203 },
+ { "option-204", "X", &dhcp_universe, 204 },
+ { "option-205", "X", &dhcp_universe, 205 },
+ { "option-206", "X", &dhcp_universe, 206 },
+ { "option-207", "X", &dhcp_universe, 207 },
+ { "option-208", "X", &dhcp_universe, 208 },
+ { "option-209", "X", &dhcp_universe, 209 },
+ { "option-210", "X", &dhcp_universe, 210 },
+ { "option-211", "X", &dhcp_universe, 211 },
+ { "option-212", "X", &dhcp_universe, 212 },
+ { "option-213", "X", &dhcp_universe, 213 },
+ { "option-214", "X", &dhcp_universe, 214 },
+ { "option-215", "X", &dhcp_universe, 215 },
+ { "option-216", "X", &dhcp_universe, 216 },
+ { "option-217", "X", &dhcp_universe, 217 },
+ { "option-218", "X", &dhcp_universe, 218 },
+ { "option-219", "X", &dhcp_universe, 219 },
+ { "option-220", "X", &dhcp_universe, 220 },
+ { "option-221", "X", &dhcp_universe, 221 },
+ { "option-222", "X", &dhcp_universe, 222 },
+ { "option-223", "X", &dhcp_universe, 223 },
+ { "option-224", "X", &dhcp_universe, 224 },
+ { "option-225", "X", &dhcp_universe, 225 },
+ { "option-226", "X", &dhcp_universe, 226 },
+ { "option-227", "X", &dhcp_universe, 227 },
+ { "option-228", "X", &dhcp_universe, 228 },
+ { "option-229", "X", &dhcp_universe, 229 },
+ { "option-230", "X", &dhcp_universe, 230 },
+ { "option-231", "X", &dhcp_universe, 231 },
+ { "option-232", "X", &dhcp_universe, 232 },
+ { "option-233", "X", &dhcp_universe, 233 },
+ { "option-234", "X", &dhcp_universe, 234 },
+ { "option-235", "X", &dhcp_universe, 235 },
+ { "option-236", "X", &dhcp_universe, 236 },
+ { "option-237", "X", &dhcp_universe, 237 },
+ { "option-238", "X", &dhcp_universe, 238 },
+ { "option-239", "X", &dhcp_universe, 239 },
+ { "option-240", "X", &dhcp_universe, 240 },
+ { "option-241", "X", &dhcp_universe, 241 },
+ { "option-242", "X", &dhcp_universe, 242 },
+ { "option-243", "X", &dhcp_universe, 243 },
+ { "option-244", "X", &dhcp_universe, 244 },
+ { "option-245", "X", &dhcp_universe, 245 },
+ { "option-246", "X", &dhcp_universe, 246 },
+ { "option-247", "X", &dhcp_universe, 247 },
+ { "option-248", "X", &dhcp_universe, 248 },
+ { "option-249", "X", &dhcp_universe, 249 },
+ { "option-250", "X", &dhcp_universe, 250 },
+ { "option-251", "X", &dhcp_universe, 251 },
+ { "option-252", "X", &dhcp_universe, 252 },
+ { "option-253", "X", &dhcp_universe, 253 },
+ { "option-254", "X", &dhcp_universe, 254 },
+ { "option-end", "e", &dhcp_universe, 255 },
+};
+
+/*
+ * Default dhcp option priority list (this is ad hoc and should not be
+ * mistaken for a carefully crafted and optimized list).
+ */
+unsigned char dhcp_option_default_priority_list[] = {
+ DHO_DHCP_REQUESTED_ADDRESS,
+ DHO_DHCP_OPTION_OVERLOAD,
+ DHO_DHCP_MAX_MESSAGE_SIZE,
+ DHO_DHCP_RENEWAL_TIME,
+ DHO_DHCP_REBINDING_TIME,
+ DHO_DHCP_CLASS_IDENTIFIER,
+ DHO_DHCP_CLIENT_IDENTIFIER,
+ DHO_SUBNET_MASK,
+ DHO_TIME_OFFSET,
+ DHO_ROUTERS,
+ DHO_TIME_SERVERS,
+ DHO_NAME_SERVERS,
+ DHO_DOMAIN_NAME_SERVERS,
+ DHO_HOST_NAME,
+ DHO_LOG_SERVERS,
+ DHO_COOKIE_SERVERS,
+ DHO_LPR_SERVERS,
+ DHO_IMPRESS_SERVERS,
+ DHO_RESOURCE_LOCATION_SERVERS,
+ DHO_HOST_NAME,
+ DHO_BOOT_SIZE,
+ DHO_MERIT_DUMP,
+ DHO_DOMAIN_NAME,
+ DHO_SWAP_SERVER,
+ DHO_ROOT_PATH,
+ DHO_EXTENSIONS_PATH,
+ DHO_IP_FORWARDING,
+ DHO_NON_LOCAL_SOURCE_ROUTING,
+ DHO_POLICY_FILTER,
+ DHO_MAX_DGRAM_REASSEMBLY,
+ DHO_DEFAULT_IP_TTL,
+ DHO_PATH_MTU_AGING_TIMEOUT,
+ DHO_PATH_MTU_PLATEAU_TABLE,
+ DHO_INTERFACE_MTU,
+ DHO_ALL_SUBNETS_LOCAL,
+ DHO_BROADCAST_ADDRESS,
+ DHO_PERFORM_MASK_DISCOVERY,
+ DHO_MASK_SUPPLIER,
+ DHO_ROUTER_DISCOVERY,
+ DHO_ROUTER_SOLICITATION_ADDRESS,
+ DHO_STATIC_ROUTES,
+ DHO_TRAILER_ENCAPSULATION,
+ DHO_ARP_CACHE_TIMEOUT,
+ DHO_IEEE802_3_ENCAPSULATION,
+ DHO_DEFAULT_TCP_TTL,
+ DHO_TCP_KEEPALIVE_INTERVAL,
+ DHO_TCP_KEEPALIVE_GARBAGE,
+ DHO_NIS_DOMAIN,
+ DHO_NIS_SERVERS,
+ DHO_NTP_SERVERS,
+ DHO_VENDOR_ENCAPSULATED_OPTIONS,
+ DHO_NETBIOS_NAME_SERVERS,
+ DHO_NETBIOS_DD_SERVER,
+ DHO_NETBIOS_NODE_TYPE,
+ DHO_NETBIOS_SCOPE,
+ DHO_FONT_SERVERS,
+ DHO_X_DISPLAY_MANAGER,
+ DHO_DHCP_PARAMETER_REQUEST_LIST,
+
+ /* Presently-undefined options... */
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
+ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
+ 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118,
+ 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
+ 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
+ 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166,
+ 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178,
+ 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190,
+ 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202,
+ 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214,
+ 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226,
+ 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238,
+ 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250,
+ 251, 252, 253, 254,
+};
+
+int sizeof_dhcp_option_default_priority_list =
+ sizeof(dhcp_option_default_priority_list);
+
+struct hash_table universe_hash;
+
+void
+initialize_universes(void)
+{
+ int i;
+
+ dhcp_universe.name = "dhcp";
+ dhcp_universe.hash = new_hash();
+ if (!dhcp_universe.hash)
+ error("Can't allocate dhcp option hash table.");
+ for (i = 0; i < 256; i++) {
+ dhcp_universe.options[i] = &dhcp_options[i];
+ add_hash(dhcp_universe.hash,
+ (unsigned char *)dhcp_options[i].name, 0,
+ (unsigned char *)&dhcp_options[i]);
+ }
+ universe_hash.hash_count = DEFAULT_HASH_SIZE;
+ add_hash(&universe_hash,
+ (unsigned char *)dhcp_universe.name, 0,
+ (unsigned char *)&dhcp_universe);
+}
diff --git a/sbin/dhclient/tree.c b/sbin/dhclient/tree.c
new file mode 100644
index 0000000..0ed2919
--- /dev/null
+++ b/sbin/dhclient/tree.c
@@ -0,0 +1,59 @@
+/* $OpenBSD: tree.c,v 1.13 2004/05/06 22:29:15 deraadt Exp $ */
+
+/* Routines for manipulating parse trees... */
+
+/*
+ * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium.
+ * 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "dhcpd.h"
+
+extern int h_errno;
+
+pair
+cons(caddr_t car, pair cdr)
+{
+ pair foo = calloc(1, sizeof(*foo));
+ if (!foo)
+ error("no memory for cons.");
+ foo->car = car;
+ foo->cdr = cdr;
+ return (foo);
+}
diff --git a/sbin/dhclient/tree.h b/sbin/dhclient/tree.h
new file mode 100644
index 0000000..04e08e7
--- /dev/null
+++ b/sbin/dhclient/tree.h
@@ -0,0 +1,66 @@
+/* $OpenBSD: tree.h,v 1.5 2004/05/06 22:29:15 deraadt Exp $ */
+
+/* Definitions for address trees... */
+
+/*
+ * Copyright (c) 1995 The Internet Software Consortium. 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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 software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+/* A pair of pointers, suitable for making a linked list. */
+typedef struct _pair {
+ caddr_t car;
+ struct _pair *cdr;
+} *pair;
+
+struct tree_cache {
+ unsigned char *value;
+ int len;
+ int buf_size;
+ time_t timeout;
+};
+
+struct universe {
+ char *name;
+ struct hash_table *hash;
+ struct option *options[256];
+};
+
+struct option {
+ char *name;
+ char *format;
+ struct universe *universe;
+ unsigned char code;
+};
diff --git a/sbin/dmesg/Makefile b/sbin/dmesg/Makefile
new file mode 100644
index 0000000..8472f56
--- /dev/null
+++ b/sbin/dmesg/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= dmesg
+MAN= dmesg.8
+
+WARNS?= 6
+
+LDADD= -lkvm
+DPADD= ${LIBKVM}
+
+.include <bsd.prog.mk>
diff --git a/sbin/dmesg/dmesg.8 b/sbin/dmesg/dmesg.8
new file mode 100644
index 0000000..9d05e6c
--- /dev/null
+++ b/sbin/dmesg/dmesg.8
@@ -0,0 +1,85 @@
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)dmesg.8 8.1 (Berkeley) 6/5/93
+.\" $FreeBSD$
+.\"
+.Dd June 5, 1993
+.Dt DMESG 8
+.Os
+.Sh NAME
+.Nm dmesg
+.Nd "display the system message buffer"
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl M Ar core Op Fl N Ar system
+.Sh DESCRIPTION
+The
+.Nm
+utility displays the contents of the system message buffer.
+If the
+.Fl M
+option is not specified, the buffer is read from the currently running kernel
+via the
+.Xr sysctl 3
+interface.
+Otherwise, the buffer is read from the specified core file,
+using the name list from the specified kernel image (or from
+the default image).
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a
+Show all data in the message buffer.
+This includes any syslog records and
+.Pa /dev/console
+output.
+.It Fl M
+Extract values associated with the name list from the specified core.
+.It Fl N
+If
+.Fl M
+is also specified,
+extract the name list from the specified system instead of the default,
+which is the kernel image the system has booted from.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /var/run/dmesg.boot" -compact
+.It Pa /var/run/dmesg.boot
+usually a snapshot of the buffer contents
+taken soon after file systems are mounted
+at startup time
+.El
+.Sh SEE ALSO
+.Xr sysctl 3 ,
+.Xr syslogd 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.0 .
diff --git a/sbin/dmesg/dmesg.c b/sbin/dmesg/dmesg.c
new file mode 100644
index 0000000..d1f7ef5
--- /dev/null
+++ b/sbin/dmesg/dmesg.c
@@ -0,0 +1,202 @@
+/*-
+ * Copyright (c) 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char sccsid[] = "@(#)dmesg.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/msgbuf.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <locale.h>
+#include <nlist.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <vis.h>
+#include <sys/syslog.h>
+
+char s_msgbufp[] = "_msgbufp";
+
+struct nlist nl[] = {
+#define X_MSGBUF 0
+ { s_msgbufp, 0, 0, 0, 0 },
+ { NULL, 0, 0, 0, 0 },
+};
+
+void usage(void) __dead2;
+
+#define KREAD(addr, var) \
+ kvm_read(kd, addr, &var, sizeof(var)) != sizeof(var)
+
+int
+main(int argc, char *argv[])
+{
+ struct msgbuf *bufp, cur;
+ char *bp, *ep, *memf, *nextp, *nlistf, *p, *q, *visbp;
+ kvm_t *kd;
+ size_t buflen, bufpos;
+ long pri;
+ int all, ch;
+
+ all = 0;
+ (void) setlocale(LC_CTYPE, "");
+ memf = nlistf = NULL;
+ while ((ch = getopt(argc, argv, "aM:N:")) != -1)
+ switch(ch) {
+ case 'a':
+ all++;
+ break;
+ case 'M':
+ memf = optarg;
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ if (argc != 0)
+ usage();
+
+ if (memf == NULL) {
+ /*
+ * Running kernel. Use sysctl. This gives an unwrapped
+ * buffer as a side effect.
+ */
+ if (sysctlbyname("kern.msgbuf", NULL, &buflen, NULL, 0) == -1)
+ err(1, "sysctl kern.msgbuf");
+ if ((bp = malloc(buflen + 2)) == NULL)
+ errx(1, "malloc failed");
+ if (sysctlbyname("kern.msgbuf", bp, &buflen, NULL, 0) == -1)
+ err(1, "sysctl kern.msgbuf");
+ } else {
+ /* Read in kernel message buffer and do sanity checks. */
+ kd = kvm_open(nlistf, memf, NULL, O_RDONLY, "dmesg");
+ if (kd == NULL)
+ exit (1);
+ if (kvm_nlist(kd, nl) == -1)
+ errx(1, "kvm_nlist: %s", kvm_geterr(kd));
+ if (nl[X_MSGBUF].n_type == 0)
+ errx(1, "%s: msgbufp not found",
+ nlistf ? nlistf : "namelist");
+ if (KREAD(nl[X_MSGBUF].n_value, bufp) || KREAD((long)bufp, cur))
+ errx(1, "kvm_read: %s", kvm_geterr(kd));
+ if (cur.msg_magic != MSG_MAGIC)
+ errx(1, "kernel message buffer has different magic "
+ "number");
+ if ((bp = malloc(cur.msg_size + 2)) == NULL)
+ errx(1, "malloc failed");
+
+ /* Unwrap the circular buffer to start from the oldest data. */
+ bufpos = MSGBUF_SEQ_TO_POS(&cur, cur.msg_wseq);
+ if (kvm_read(kd, (long)&cur.msg_ptr[bufpos], bp,
+ cur.msg_size - bufpos) != (ssize_t)(cur.msg_size - bufpos))
+ errx(1, "kvm_read: %s", kvm_geterr(kd));
+ if (bufpos != 0 && kvm_read(kd, (long)cur.msg_ptr,
+ &bp[cur.msg_size - bufpos], bufpos) != (ssize_t)bufpos)
+ errx(1, "kvm_read: %s", kvm_geterr(kd));
+ kvm_close(kd);
+ buflen = cur.msg_size;
+ }
+
+ /*
+ * Ensure that the buffer ends with a newline and a \0 to avoid
+ * complications below. We left space above.
+ */
+ if (buflen == 0 || bp[buflen - 1] != '\n')
+ bp[buflen++] = '\n';
+ bp[buflen] = '\0';
+
+ if ((visbp = malloc(4 * buflen + 1)) == NULL)
+ errx(1, "malloc failed");
+
+ /*
+ * The message buffer is circular, but has been unwrapped so that
+ * the oldest data comes first. The data will be preceded by \0's
+ * if the message buffer was not full.
+ */
+ p = bp;
+ ep = &bp[buflen];
+ if (*p == '\0') {
+ /* Strip leading \0's */
+ while (*p == '\0')
+ p++;
+ } else if (!all) {
+ /* Skip the first line, since it is probably incomplete. */
+ p = memchr(p, '\n', ep - p);
+ p++;
+ }
+ for (; p < ep; p = nextp) {
+ nextp = memchr(p, '\n', ep - p);
+ nextp++;
+
+ /* Skip ^<[0-9]+> syslog sequences. */
+ if (*p == '<') {
+ errno = 0;
+ pri = strtol(p + 1, &q, 10);
+ if (*q == '>' && pri >= 0 && pri < INT_MAX &&
+ errno == 0) {
+ if (LOG_FAC(pri) != LOG_KERN && !all)
+ continue;
+ p = q + 1;
+ }
+ }
+
+ (void)strvisx(visbp, p, nextp - p, 0);
+ (void)printf("%s", visbp);
+ }
+ exit(0);
+}
+
+void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: dmesg [-a] [-M core [-N system]]\n");
+ exit(1);
+}
diff --git a/sbin/dump/Makefile b/sbin/dump/Makefile
new file mode 100644
index 0000000..608162a
--- /dev/null
+++ b/sbin/dump/Makefile
@@ -0,0 +1,23 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+# dump.h header file
+# itime.c reads /etc/dumpdates
+# main.c driver
+# optr.c operator interface
+# dumprmt.c handles remote tape via rmt(8)
+# tape.c handles the mag tape and opening/closing
+# traverse.c traverses the file system
+# unctime.c undo ctime
+#
+# DEBUG use local directory to find ddate and dumpdates
+# TDEBUG trace out the process forking
+
+PROG= dump
+LINKS= ${BINDIR}/dump ${BINDIR}/rdump
+CFLAGS+=-DRDUMP
+SRCS= itime.c main.c optr.c dumprmt.c tape.c traverse.c unctime.c cache.c
+MAN= dump.8
+MLINKS= dump.8 rdump.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/dump/cache.c b/sbin/dump/cache.c
new file mode 100644
index 0000000..906ac27
--- /dev/null
+++ b/sbin/dump/cache.c
@@ -0,0 +1,146 @@
+/*
+ * CACHE.C
+ *
+ * Block cache for dump
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/fs.h>
+#include <ufs/fsdir.h>
+#include <ufs/inode.h>
+#else
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#endif
+
+#include <protocols/dumprestore.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#endif
+#include "dump.h"
+
+typedef struct Block {
+ struct Block *b_HNext; /* must be first field */
+ off_t b_Offset;
+ char *b_Data;
+} Block;
+
+#define HFACTOR 4
+#define BLKFACTOR 4
+
+static char *DataBase;
+static Block **BlockHash;
+static int BlockSize;
+static int HSize;
+static int NBlocks;
+
+static void
+cinit(void)
+{
+ int i;
+ int hi;
+ Block *base;
+
+ if ((BlockSize = sblock->fs_bsize * BLKFACTOR) > MAXBSIZE)
+ BlockSize = MAXBSIZE;
+ NBlocks = cachesize / BlockSize;
+ HSize = NBlocks / HFACTOR;
+
+ msg("Cache %d MB, blocksize = %d\n",
+ NBlocks * BlockSize / (1024 * 1024), BlockSize);
+
+ base = calloc(sizeof(Block), NBlocks);
+ BlockHash = calloc(sizeof(Block *), HSize);
+ DataBase = mmap(NULL, NBlocks * BlockSize,
+ PROT_READ|PROT_WRITE, MAP_ANON, -1, 0);
+ for (i = 0; i < NBlocks; ++i) {
+ base[i].b_Data = DataBase + i * BlockSize;
+ base[i].b_Offset = (off_t)-1;
+ hi = i / HFACTOR;
+ base[i].b_HNext = BlockHash[hi];
+ BlockHash[hi] = &base[i];
+ }
+}
+
+ssize_t
+cread(int fd, void *buf, size_t nbytes, off_t offset)
+{
+ Block *blk;
+ Block **pblk;
+ Block **ppblk;
+ int hi;
+ int n;
+ off_t mask;
+
+ /*
+ * If the cache is disabled, or we do not yet know the filesystem
+ * block size, then revert to pread. Otherwise initialize the
+ * cache as necessary and continue.
+ */
+ if (cachesize <= 0 || sblock->fs_bsize == 0)
+ return(pread(fd, buf, nbytes, offset));
+ if (DataBase == NULL)
+ cinit();
+
+ /*
+ * If the request crosses a cache block boundary, or the
+ * request is larger or equal to the cache block size,
+ * revert to pread(). Full-block-reads are typically
+ * one-time calls and caching would be detrimental.
+ */
+ mask = ~(off_t)(BlockSize - 1);
+ if (nbytes >= BlockSize ||
+ ((offset ^ (offset + nbytes - 1)) & mask) != 0) {
+ return(pread(fd, buf, nbytes, offset));
+ }
+
+ /*
+ * Obtain and access the cache block. Cache a successful
+ * result. If an error occurs, revert to pread() (this might
+ * occur near the end of the media).
+ */
+ hi = (offset / BlockSize) % HSize;
+ pblk = &BlockHash[hi];
+ ppblk = NULL;
+ while ((blk = *pblk) != NULL) {
+ if (((blk->b_Offset ^ offset) & mask) == 0)
+ break;
+ ppblk = pblk;
+ pblk = &blk->b_HNext;
+ }
+ if (blk == NULL) {
+ blk = *ppblk;
+ pblk = ppblk;
+ blk->b_Offset = offset & mask;
+ n = pread(fd, blk->b_Data, BlockSize, blk->b_Offset);
+ if (n != BlockSize) {
+ blk->b_Offset = (off_t)-1;
+ blk = NULL;
+ }
+ }
+ if (blk) {
+ bcopy(blk->b_Data + (offset - blk->b_Offset), buf, nbytes);
+ *pblk = blk->b_HNext;
+ blk->b_HNext = BlockHash[hi];
+ BlockHash[hi] = blk;
+ return(nbytes);
+ } else {
+ return(pread(fd, buf, nbytes, offset));
+ }
+}
+
diff --git a/sbin/dump/dump.8 b/sbin/dump/dump.8
new file mode 100644
index 0000000..079c456
--- /dev/null
+++ b/sbin/dump/dump.8
@@ -0,0 +1,544 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)dump.8 8.3 (Berkeley) 5/1/95
+.\" $FreeBSD$
+.\"
+.Dd December 22, 2005
+.Dt DUMP 8
+.Os
+.Sh NAME
+.Nm dump ,
+.Nm rdump
+.Nd file system backup
+.Sh SYNOPSIS
+.Nm
+.Op Fl 0123456789acLnSu
+.Op Fl B Ar records
+.Op Fl b Ar blocksize
+.Op Fl C Ar cachesize
+.Op Fl D Ar dumpdates
+.Op Fl d Ar density
+.Op Fl f Ar file | Fl P Ar pipecommand
+.Op Fl h Ar level
+.Op Fl s Ar feet
+.Op Fl T Ar date
+.Ar filesystem
+.Nm
+.Fl W | Fl w
+.Pp
+.Nm rdump
+is an alternate name for
+.Nm .
+.Pp
+.in \" XXX
+(The
+.Bx 4.3
+option syntax is implemented for backward compatibility, but
+is not documented here.)
+.Sh DESCRIPTION
+The
+.Nm
+utility examines files
+on a file system
+and determines which files
+need to be backed up.
+These files
+are copied to the given disk, tape or other
+storage medium for safe keeping (see the
+.Fl f
+option below for doing remote backups).
+A dump that is larger than the output medium is broken into
+multiple volumes.
+On most media the size is determined by writing until an
+end-of-media indication is returned.
+This can be enforced
+by using the
+.Fl a
+option.
+.Pp
+On media that cannot reliably return an end-of-media indication
+(such as some cartridge tape drives)
+each volume is of a fixed size;
+the actual size is determined by the tape size and density and/or
+.Fl B
+options.
+By default, the same output file name is used for each volume
+after prompting the operator to change media.
+.Pp
+The file system to be dumped is specified by the argument
+.Ar filesystem
+as either its device-special file or its mount point
+(if that is in a standard entry in
+.Pa /etc/fstab ) .
+.Pp
+The following options are supported by
+.Nm :
+.Bl -tag -width Ds
+.It Fl 0-9
+Dump levels.
+A level 0, full backup,
+guarantees the entire file system is copied
+(but see also the
+.Fl h
+option below).
+A level number above 0,
+incremental backup,
+tells dump to
+copy all files new or modified since the
+last dump of any lower level.
+The default level is 0.
+.It Fl a
+.Dq auto-size .
+Bypass all tape length considerations, and enforce writing
+until an end-of-media indication is returned.
+This fits best for most modern tape drives.
+Use of this option is particularly
+recommended when appending to an existing tape, or using a tape
+drive with hardware compression (where you can never be sure about
+the compression ratio).
+.It Fl B Ar records
+The number of kilobytes per output volume, except that if it is
+not an integer multiple of the output block size,
+the command uses the next smaller such multiple.
+This option overrides the calculation of tape size
+based on length and density.
+.It Fl b Ar blocksize
+The number of kilobytes per output block.
+The default block size is 10.
+.It Fl C Ar cachesize
+Specify the cache size in megabytes.
+This will greatly improve performance
+at the cost of
+.Nm
+possibly not noticing changes in the file system between passes.
+It is
+recommended that you always use this option when dumping a snapshot.
+Beware that
+.Nm
+forks, and the actual memory use may be larger than the specified cache
+size.
+The recommended cache size is between 8 and 32 (megabytes).
+.It Fl c
+Change the defaults for use with a cartridge tape drive, with a density
+of 8000 bpi, and a length of 1700 feet.
+.It Fl D Ar dumpdates
+Specify an alternate path to the
+.Pa dumpdates
+file.
+The default is
+.Pa /etc/dumpdates .
+.It Fl d Ar density
+Set tape density to
+.Ar density .
+The default is 1600BPI.
+.It Fl f Ar file
+Write the backup to
+.Ar file ;
+.Ar file
+may be a special device file
+like
+.Pa /dev/sa0
+(a tape drive),
+.Pa /dev/fd1
+(a floppy disk drive),
+an ordinary file,
+or
+.Sq Fl
+(the standard output).
+Multiple file names may be given as a single argument separated by commas.
+Each file will be used for one dump volume in the order listed;
+if the dump requires more volumes than the number of names given,
+the last file name will used for all remaining volumes after prompting
+for media changes.
+If the name of the file is of the form
+.Dq host:file ,
+or
+.Dq user@host:file ,
+.Nm
+writes to the named file on the remote host using
+.Xr rmt 8 .
+The default path name of the remote
+.Xr rmt 8
+program is
+.\" rmt path, is the path on the remote host
+.Pa /etc/rmt ;
+this can be overridden by the environment variable
+.Ev RMT .
+.It Fl P Ar pipecommand
+Use
+.Xr popen 3
+to execute the
+.Xr sh 1
+script string defined by
+.Ar pipecommand
+for the output device of each volume.
+This child pipeline's
+.Dv stdin
+.Pq Pa /dev/fd/0
+is redirected from the
+.Nm
+output stream, and the environment variable
+.Ev DUMP_VOLUME
+is set to the current volume number being written.
+After every volume, the writer side of the pipe is closed and
+.Ar pipecommand
+is executed again.
+Subject to the media size specified by
+.Fl B ,
+each volume is written in this manner as if the output were a tape drive.
+.It Fl h Ar level
+Honor the user
+.Dq nodump
+flag
+.Pq Dv UF_NODUMP
+only for dumps at or above the given
+.Ar level .
+The default honor level is 1,
+so that incremental backups omit such files
+but full backups retain them.
+.It Fl L
+This option is to notify
+.Nm
+that it is dumping a live file system.
+To obtain a consistent dump image,
+.Nm
+takes a snapshot of the file system in the
+.Pa .snap
+directory in the root of the file system being dumped and
+then does a dump of the snapshot.
+The snapshot is removed when the dump is complete.
+This option is ignored for unmounted or read-only file systems.
+If the
+.Pa .snap
+directory does not exist in the root of the file system being dumped,
+a warning will be issued and the
+.Nm
+will revert to the standard behavior.
+This problem can be corrected by creating a
+.Pa .snap
+directory in the root of the file system to be dumped;
+its owner should be
+.Dq Li root ,
+its group should be
+.Dq Li operator ,
+and its mode should be
+.Dq Li 0770 .
+.It Fl n
+Whenever
+.Nm
+requires operator attention,
+notify all operators in the group
+.Dq operator
+by means similar to a
+.Xr wall 1 .
+.It Fl S
+Display an estimate of the backup size and the number of
+tapes required, and exit without actually performing the dump.
+.It Fl s Ar feet
+Attempt to calculate the amount of tape needed
+at a particular density.
+If this amount is exceeded,
+.Nm
+prompts for a new tape.
+It is recommended to be a bit conservative on this option.
+The default tape length is 2300 feet.
+.It Fl T Ar date
+Use the specified date as the starting time for the dump
+instead of the time determined from looking in
+a warning will be issued and the
+.Pa dumpdates
+file.
+The format of date is the same as that of
+.Xr ctime 3 .
+This option is useful for automated dump scripts that wish to
+dump over a specific period of time.
+The
+.Fl T
+option is mutually exclusive from the
+.Fl u
+option.
+.It Fl u
+Update the
+.Pa dumpdates
+file
+after a successful dump.
+The format of
+the
+.Pa dumpdates
+file
+is readable by people, consisting of one
+free format record per line:
+file system name,
+increment level
+and
+.Xr ctime 3
+format dump date.
+There may be only one entry per file system at each level.
+The
+.Pa dumpdates
+file
+may be edited to change any of the fields,
+if necessary.
+The default path for the
+.Pa dumpdates
+file is
+.Pa /etc/dumpdates ,
+but the
+.Fl D
+option may be used to change it.
+.It Fl W
+Tell the operator what file systems need to be dumped.
+This information is gleaned from the files
+.Pa dumpdates
+and
+.Pa /etc/fstab .
+The
+.Fl W
+option causes
+.Nm
+to print out, for each file system in
+the
+.Pa dumpdates
+file
+the most recent dump date and level,
+and highlights those file systems that should be dumped.
+If the
+.Fl W
+option is set, all other options are ignored, and
+.Nm
+exits immediately.
+.It Fl w
+Is like
+.Fl W ,
+but prints only those file systems which need to be dumped.
+.El
+.Pp
+Directories and regular files which have their
+.Dq nodump
+flag
+.Pq Dv UF_NODUMP
+set will be omitted along with everything under such directories,
+subject to the
+.Fl h
+option.
+.Pp
+The
+.Nm
+utility requires operator intervention on these conditions:
+end of tape,
+end of dump,
+tape write error,
+tape open error or
+disk read error (if there are more than a threshold of 32).
+In addition to alerting all operators implied by the
+.Fl n
+key,
+.Nm
+interacts with the operator on
+.Em dump's
+control terminal at times when
+.Nm
+can no longer proceed,
+or if something is grossly wrong.
+All questions
+.Nm
+poses
+.Em must
+be answered by typing
+.Dq yes
+or
+.Dq no ,
+appropriately.
+.Pp
+Since making a dump involves a lot of time and effort for full dumps,
+.Nm
+checkpoints itself at the start of each tape volume.
+If writing that volume fails for some reason,
+.Nm
+will,
+with operator permission,
+restart itself from the checkpoint
+after the old tape has been rewound and removed,
+and a new tape has been mounted.
+.Pp
+The
+.Nm
+utility tells the operator what is going on at periodic intervals
+(every 5 minutes, or promptly after receiving
+.Dv SIGINFO ) ,
+including usually low estimates of the number of blocks to write,
+the number of tapes it will take, the time to completion, and
+the time to the tape change.
+The output is verbose,
+so that others know that the terminal
+controlling
+.Nm
+is busy,
+and will be for some time.
+.Pp
+In the event of a catastrophic disk event, the time required
+to restore all the necessary backup tapes or files to disk
+can be kept to a minimum by staggering the incremental dumps.
+An efficient method of staggering incremental dumps
+to minimize the number of tapes follows:
+.Bl -bullet -offset indent
+.It
+Always start with a level 0 backup, for example:
+.Bd -literal -offset indent
+/sbin/dump -0u -f /dev/nsa0 /usr/src
+.Ed
+.Pp
+This should be done at set intervals, say once a month or once every two months,
+and on a set of fresh tapes that is saved forever.
+.It
+After a level 0, dumps of active file systems (file systems with files
+that change, depending on your partition layout some file systems may
+contain only data that does not change) are taken on a daily basis,
+using a modified Tower of Hanoi algorithm,
+with this sequence of dump levels:
+.Bd -literal -offset indent
+3 2 5 4 7 6 9 8 9 9 ...
+.Ed
+.Pp
+For the daily dumps, it should be possible to use a fixed number of tapes
+for each day, used on a weekly basis.
+Each week, a level 1 dump is taken, and
+the daily Hanoi sequence repeats beginning with 3.
+For weekly dumps, another fixed set of tapes per dumped file system is
+used, also on a cyclical basis.
+.El
+.Pp
+After several months or so, the daily and weekly tapes should get
+rotated out of the dump cycle and fresh tapes brought in.
+.Sh ENVIRONMENT
+.Bl -tag -width ".Ev TAPE"
+.It Ev TAPE
+The
+.Ar file
+or device to dump to if the
+.Fl f
+option is not used.
+.It Ev RMT
+Pathname of the remote
+.Xr rmt 8
+program.
+.It Ev RSH
+Pathname of a remote shell program, if not
+.Xr rsh 1 .
+.El
+.Sh EXAMPLES
+Dumps the "/u" file system to dvds using growisofs.
+Uses a 16MB cache and creates a snapshot of the dump and records the
+dumpdates file.
+.Bd -literal
+/sbin/dump -0u -L -C16 -B4589840 -P 'growisofs -Z /dev/cd0=/dev/fd/0' /u
+.Ed
+.Sh FILES
+.Bl -tag -width /etc/dumpdates -compact
+.It Pa /dev/sa0
+default tape unit to dump to
+.It Pa /etc/dumpdates
+dump date records
+(this can be changed;
+see the
+.Fl D
+option)
+.It Pa /etc/fstab
+dump table: file systems and frequency
+.It Pa /etc/group
+to find group
+.Em operator
+.El
+.Sh EXIT STATUS
+Dump exits with zero status on success.
+Startup errors are indicated with an exit code of 1;
+abnormal termination is indicated with an exit code of 3.
+.Sh DIAGNOSTICS
+Many, and verbose.
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr fstab 5 ,
+.Xr restore 8 ,
+.Xr rmt 8
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.At v6 .
+.Sh BUGS
+Fewer than 32 read errors on the file system are ignored, though all
+errors will generate a warning message.
+This is a bit of a compromise.
+In practice, it is possible to generate read errors when doing dumps
+on mounted partitions if the file system is being modified while the
+.Nm
+is running.
+Since dumps are often done in an unattended fashion using
+.Xr cron 8
+jobs asking for Operator intervention would result in the
+.Nm
+dying.
+However, there is nothing wrong with a dump tape written when this sort
+of read error occurs, and there is no reason to terminate the
+.Nm .
+.Pp
+Each reel requires a new process, so parent processes for
+reels already written just hang around until the entire tape
+is written.
+.Pp
+The
+.Nm
+utility with the
+.Fl W
+or
+.Fl w
+options does not report file systems that have never been recorded
+in the
+.Pa dumpdates
+file,
+even if listed in
+.Pa /etc/fstab .
+.Pp
+It would be nice if
+.Nm
+knew about the dump sequence,
+kept track of the tapes scribbled on,
+told the operator which tape to mount when,
+and provided more assistance
+for the operator running
+.Xr restore 8 .
+.Pp
+The
+.Nm
+utility cannot do remote backups without being run as root, due to its
+security history.
+This will be fixed in a later version of
+.Fx .
+Presently, it works if you set it setuid (like it used to be), but this
+might constitute a security risk.
diff --git a/sbin/dump/dump.h b/sbin/dump/dump.h
new file mode 100644
index 0000000..2224782
--- /dev/null
+++ b/sbin/dump/dump.h
@@ -0,0 +1,179 @@
+/*-
+ * 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.
+ * 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 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.
+ *
+ * @(#)dump.h 8.2 (Berkeley) 4/28/95
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Dump maps used to describe what is to be dumped.
+ */
+int mapsize; /* size of the state maps */
+char *usedinomap; /* map of allocated inodes */
+char *dumpdirmap; /* map of directories to be dumped */
+char *dumpinomap; /* map of files to be dumped */
+/*
+ * Map manipulation macros.
+ */
+#define SETINO(ino, map) \
+ map[(u_int)((ino) - 1) / CHAR_BIT] |= \
+ 1 << ((u_int)((ino) - 1) % CHAR_BIT)
+#define CLRINO(ino, map) \
+ map[(u_int)((ino) - 1) / CHAR_BIT] &= \
+ ~(1 << ((u_int)((ino) - 1) % CHAR_BIT))
+#define TSTINO(ino, map) \
+ (map[(u_int)((ino) - 1) / CHAR_BIT] & \
+ (1 << ((u_int)((ino) - 1) % CHAR_BIT)))
+
+/*
+ * All calculations done in 0.1" units!
+ */
+char *disk; /* name of the disk file */
+char *tape; /* name of the tape file */
+char *popenout; /* popen(3) per-"tape" command */
+char *dumpdates; /* name of the file containing dump date information*/
+char *temp; /* name of the file for doing rewrite of dumpdates */
+char lastlevel; /* dump level of previous dump */
+char level; /* dump level of this dump */
+int uflag; /* update flag */
+int diskfd; /* disk file descriptor */
+int tapefd; /* tape file descriptor */
+int pipeout; /* true => output to standard output */
+ino_t curino; /* current inumber; used globally */
+int newtape; /* new tape flag */
+int density; /* density in 0.1" units */
+long tapesize; /* estimated tape size, blocks */
+long tsize; /* tape size in 0.1" units */
+long asize; /* number of 0.1" units written on current tape */
+int etapes; /* estimated number of tapes */
+int nonodump; /* if set, do not honor UF_NODUMP user flags */
+int unlimited; /* if set, write to end of medium */
+int cachesize; /* size of block cache in bytes */
+
+int notify; /* notify operator flag */
+int blockswritten; /* number of blocks written on current tape */
+int tapeno; /* current tape number */
+time_t tstart_writing; /* when started writing the first tape block */
+time_t tend_writing; /* after writing the last tape block */
+int passno; /* current dump pass number */
+struct fs *sblock; /* the file system super block */
+char sblock_buf[MAXBSIZE];
+long dev_bsize; /* block size of underlying disk device */
+int dev_bshift; /* log2(dev_bsize) */
+int tp_bshift; /* log2(TP_BSIZE) */
+
+/* operator interface functions */
+void broadcast(const char *message);
+void infosch(int);
+void lastdump(int arg); /* int should be char */
+void msg(const char *fmt, ...) __printflike(1, 2);
+void msgtail(const char *fmt, ...) __printflike(1, 2);
+int query(const char *question);
+void quit(const char *fmt, ...) __printflike(1, 2);
+void timeest(void);
+time_t unctime(char *str);
+
+/* mapping rouintes */
+union dinode;
+int mapfiles(ino_t maxino, long *tapesize);
+int mapdirs(ino_t maxino, long *tapesize);
+
+/* file dumping routines */
+void ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino);
+void ufs2_blksout(ufs2_daddr_t *blkp, int frags, ino_t ino);
+void bread(ufs2_daddr_t blkno, char *buf, int size);
+ssize_t cread(int fd, void *buf, size_t nbytes, off_t offset);
+void dumpino(union dinode *dp, ino_t ino);
+void dumpmap(char *map, int type, ino_t ino);
+void writeheader(ino_t ino);
+
+/* tape writing routines */
+int alloctape(void);
+void close_rewind(void);
+void dumpblock(ufs2_daddr_t blkno, int size);
+void startnewtape(int top);
+void trewind(void);
+void writerec(char *dp, int isspcl);
+
+void Exit(int status) __dead2;
+void dumpabort(int signo);
+void dump_getfstab(void);
+
+char *rawname(char *cp);
+union dinode *getino(ino_t inum, int *mode);
+
+/* rdump routines */
+#ifdef RDUMP
+void rmtclose(void);
+int rmthost(const char *host);
+int rmtopen(const char *tape, int mode);
+int rmtwrite(const char *buf, int count);
+#endif /* RDUMP */
+
+void interrupt(int signo); /* in case operator bangs on console */
+
+/*
+ * Exit status codes
+ */
+#define X_FINOK 0 /* normal exit */
+#define X_STARTUP 1 /* startup error */
+#define X_REWRITE 2 /* restart writing from the check point */
+#define X_ABORT 3 /* abort dump; don't attempt checkpointing */
+
+#define OPGRENT "operator" /* group entry to notify */
+
+struct fstab *fstabsearch(const char *key); /* search fs_file and fs_spec */
+
+#ifndef NAME_MAX
+#define NAME_MAX 255
+#endif
+
+/*
+ * The contents of the file _PATH_DUMPDATES is maintained both on
+ * a linked list, and then (eventually) arrayified.
+ */
+struct dumpdates {
+ char dd_name[NAME_MAX+3];
+ char dd_level;
+ time_t dd_ddate;
+};
+int nddates; /* number of records (might be zero) */
+struct dumpdates **ddatev; /* the arrayfied version */
+void initdumptimes(void);
+void getdumptime(void);
+void putdumptime(void);
+#define ITITERATE(i, ddp) \
+ if (ddatev != NULL) \
+ for (ddp = ddatev[i = 0]; i < nddates; ddp = ddatev[++i])
+
+void sig(int signo);
+
+#ifndef _PATH_FSTAB
+#define _PATH_FSTAB "/etc/fstab"
+#endif
diff --git a/sbin/dump/dumprmt.c b/sbin/dump/dumprmt.c
new file mode 100644
index 0000000..dd02642
--- /dev/null
+++ b/sbin/dump/dumprmt.c
@@ -0,0 +1,375 @@
+/*-
+ * 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.
+ * 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 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 lint
+#if 0
+static char sccsid[] = "@(#)dumprmt.c 8.3 (Berkeley) 4/28/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mtio.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <ufs/ufs/dinode.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include <protocols/dumprestore.h>
+
+#include <ctype.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+#include "dump.h"
+
+#define TS_CLOSED 0
+#define TS_OPEN 1
+
+static int rmtstate = TS_CLOSED;
+static int rmtape;
+static char *rmtpeer;
+
+static int okname(const char *);
+static int rmtcall(const char *, const char *);
+static void rmtconnaborted(int);
+static int rmtgetb(void);
+static void rmtgetconn(void);
+static void rmtgets(char *, int);
+static int rmtreply(const char *);
+
+static int errfd = -1;
+extern int ntrec; /* blocking factor on tape */
+
+int
+rmthost(const char *host)
+{
+
+ rmtpeer = strdup(host);
+ if (rmtpeer == NULL)
+ return (0);
+ signal(SIGPIPE, rmtconnaborted);
+ rmtgetconn();
+ if (rmtape < 0)
+ return (0);
+ return (1);
+}
+
+static void
+rmtconnaborted(int sig __unused)
+{
+ msg("Lost connection to remote host.\n");
+ if (errfd != -1) {
+ fd_set r;
+ struct timeval t;
+
+ FD_ZERO(&r);
+ FD_SET(errfd, &r);
+ t.tv_sec = 0;
+ t.tv_usec = 0;
+ if (select(errfd + 1, &r, NULL, NULL, &t)) {
+ int i;
+ char buf[2048];
+
+ if ((i = read(errfd, buf, sizeof(buf) - 1)) > 0) {
+ buf[i] = '\0';
+ msg("on %s: %s%s", rmtpeer, buf,
+ buf[i - 1] == '\n' ? "" : "\n");
+ }
+ }
+ }
+
+ exit(X_ABORT);
+}
+
+void
+rmtgetconn(void)
+{
+ char *cp;
+ const char *rmt;
+ static struct servent *sp = NULL;
+ static struct passwd *pwd = NULL;
+ char *tuser;
+ int size;
+ int throughput;
+ int on;
+
+ if (sp == NULL) {
+ sp = getservbyname("shell", "tcp");
+ if (sp == NULL) {
+ msg("shell/tcp: unknown service\n");
+ exit(X_STARTUP);
+ }
+ pwd = getpwuid(getuid());
+ if (pwd == NULL) {
+ msg("who are you?\n");
+ exit(X_STARTUP);
+ }
+ }
+ if ((cp = strchr(rmtpeer, '@')) != NULL) {
+ tuser = rmtpeer;
+ *cp = '\0';
+ if (!okname(tuser))
+ exit(X_STARTUP);
+ rmtpeer = ++cp;
+ } else
+ tuser = pwd->pw_name;
+ if ((rmt = getenv("RMT")) == NULL)
+ rmt = _PATH_RMT;
+ msg("%s", "");
+ rmtape = rcmd(&rmtpeer, (u_short)sp->s_port, pwd->pw_name,
+ tuser, rmt, &errfd);
+ if (rmtape < 0) {
+ msg("login to %s as %s failed.\n", rmtpeer, tuser);
+ return;
+ }
+ (void)fprintf(stderr, "Connection to %s established.\n", rmtpeer);
+ size = ntrec * TP_BSIZE;
+ if (size > 60 * 1024) /* XXX */
+ size = 60 * 1024;
+ /* Leave some space for rmt request/response protocol */
+ size += 2 * 1024;
+ while (size > TP_BSIZE &&
+ setsockopt(rmtape, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) < 0)
+ size -= TP_BSIZE;
+ (void)setsockopt(rmtape, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size));
+ throughput = IPTOS_THROUGHPUT;
+ if (setsockopt(rmtape, IPPROTO_IP, IP_TOS,
+ &throughput, sizeof(throughput)) < 0)
+ perror("IP_TOS:IPTOS_THROUGHPUT setsockopt");
+ on = 1;
+ if (setsockopt(rmtape, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0)
+ perror("TCP_NODELAY setsockopt");
+}
+
+static int
+okname(const char *cp0)
+{
+ const char *cp;
+ int c;
+
+ for (cp = cp0; *cp; cp++) {
+ c = *cp;
+ if (!isascii(c) || !(isalnum(c) || c == '_' || c == '-')) {
+ msg("invalid user name %s\n", cp0);
+ return (0);
+ }
+ }
+ return (1);
+}
+
+int
+rmtopen(const char *tape, int mode)
+{
+ char buf[256];
+
+ (void)snprintf(buf, sizeof (buf), "O%.226s\n%d\n", tape, mode);
+ rmtstate = TS_OPEN;
+ return (rmtcall(tape, buf));
+}
+
+void
+rmtclose(void)
+{
+
+ if (rmtstate != TS_OPEN)
+ return;
+ rmtcall("close", "C\n");
+ rmtstate = TS_CLOSED;
+}
+
+int
+rmtread(char *buf, int count)
+{
+ char line[30];
+ int n, i, cc;
+
+ (void)snprintf(line, sizeof (line), "R%d\n", count);
+ n = rmtcall("read", line);
+ if (n < 0)
+ /* rmtcall() properly sets errno for us on errors. */
+ return (n);
+ for (i = 0; i < n; i += cc) {
+ cc = read(rmtape, buf+i, n - i);
+ if (cc <= 0)
+ rmtconnaborted(0);
+ }
+ return (n);
+}
+
+int
+rmtwrite(const char *buf, int count)
+{
+ char line[30];
+
+ (void)snprintf(line, sizeof (line), "W%d\n", count);
+ write(rmtape, line, strlen(line));
+ write(rmtape, buf, count);
+ return (rmtreply("write"));
+}
+
+void
+rmtwrite0(int count)
+{
+ char line[30];
+
+ (void)snprintf(line, sizeof (line), "W%d\n", count);
+ write(rmtape, line, strlen(line));
+}
+
+void
+rmtwrite1(const char *buf, int count)
+{
+
+ write(rmtape, buf, count);
+}
+
+int
+rmtwrite2(void)
+{
+
+ return (rmtreply("write"));
+}
+
+int
+rmtseek(int offset, int pos) /* XXX off_t ? */
+{
+ char line[80];
+
+ (void)snprintf(line, sizeof (line), "L%d\n%d\n", offset, pos);
+ return (rmtcall("seek", line));
+}
+
+struct mtget mts;
+
+struct mtget *
+rmtstatus(void)
+{
+ int i;
+ char *cp;
+
+ if (rmtstate != TS_OPEN)
+ return (NULL);
+ rmtcall("status", "S\n");
+ for (i = 0, cp = (char *)&mts; i < sizeof(mts); i++)
+ *cp++ = rmtgetb();
+ return (&mts);
+}
+
+int
+rmtioctl(int cmd, int count)
+{
+ char buf[256];
+
+ if (count < 0)
+ return (-1);
+ (void)snprintf(buf, sizeof (buf), "I%d\n%d\n", cmd, count);
+ return (rmtcall("ioctl", buf));
+}
+
+static int
+rmtcall(const char *cmd, const char *buf)
+{
+
+ if (write(rmtape, buf, strlen(buf)) != strlen(buf))
+ rmtconnaborted(0);
+ return (rmtreply(cmd));
+}
+
+static int
+rmtreply(const char *cmd)
+{
+ char *cp;
+ char code[30], emsg[BUFSIZ];
+
+ rmtgets(code, sizeof (code));
+ if (*code == 'E' || *code == 'F') {
+ rmtgets(emsg, sizeof (emsg));
+ msg("%s: %s", cmd, emsg);
+ errno = atoi(code + 1);
+ if (*code == 'F')
+ rmtstate = TS_CLOSED;
+ return (-1);
+ }
+ if (*code != 'A') {
+ /* Kill trailing newline */
+ cp = code + strlen(code);
+ if (cp > code && *--cp == '\n')
+ *cp = '\0';
+
+ msg("Protocol to remote tape server botched (code \"%s\").\n",
+ code);
+ rmtconnaborted(0);
+ }
+ return (atoi(code + 1));
+}
+
+int
+rmtgetb(void)
+{
+ char c;
+
+ if (read(rmtape, &c, 1) != 1)
+ rmtconnaborted(0);
+ return (c);
+}
+
+/* Get a line (guaranteed to have a trailing newline). */
+void
+rmtgets(char *line, int len)
+{
+ char *cp = line;
+
+ while (len > 1) {
+ *cp = rmtgetb();
+ if (*cp == '\n') {
+ cp[1] = '\0';
+ return;
+ }
+ cp++;
+ len--;
+ }
+ *cp = '\0';
+ msg("Protocol to remote tape server botched.\n");
+ msg("(rmtgets got \"%s\").\n", line);
+ rmtconnaborted(0);
+}
diff --git a/sbin/dump/itime.c b/sbin/dump/itime.c
new file mode 100644
index 0000000..de880bb
--- /dev/null
+++ b/sbin/dump/itime.c
@@ -0,0 +1,260 @@
+/*-
+ * 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.
+ * 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 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 lint
+#if 0
+static char sccsid[] = "@(#)itime.c 8.1 (Berkeley) 6/5/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <ufs/ufs/dinode.h>
+
+#include <protocols/dumprestore.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <timeconv.h>
+
+#include "dump.h"
+
+struct dumptime {
+ struct dumpdates dt_value;
+ SLIST_ENTRY(dumptime) dt_list;
+};
+SLIST_HEAD(dthead, dumptime) dthead = SLIST_HEAD_INITIALIZER(dthead);
+struct dumpdates **ddatev = 0;
+int nddates = 0;
+
+static void dumprecout(FILE *, const struct dumpdates *);
+static int getrecord(FILE *, struct dumpdates *);
+static int makedumpdate(struct dumpdates *, const char *);
+static void readdumptimes(FILE *);
+
+void
+initdumptimes(void)
+{
+ FILE *df;
+
+ if ((df = fopen(dumpdates, "r")) == NULL) {
+ if (errno != ENOENT) {
+ msg("WARNING: cannot read %s: %s\n", dumpdates,
+ strerror(errno));
+ return;
+ }
+ /*
+ * Dumpdates does not exist, make an empty one.
+ */
+ msg("WARNING: no file `%s', making an empty one\n", dumpdates);
+ if ((df = fopen(dumpdates, "w")) == NULL) {
+ msg("WARNING: cannot create %s: %s\n", dumpdates,
+ strerror(errno));
+ return;
+ }
+ (void) fclose(df);
+ if ((df = fopen(dumpdates, "r")) == NULL) {
+ quit("cannot read %s even after creating it: %s\n",
+ dumpdates, strerror(errno));
+ /* NOTREACHED */
+ }
+ }
+ (void) flock(fileno(df), LOCK_SH);
+ readdumptimes(df);
+ (void) fclose(df);
+}
+
+static void
+readdumptimes(FILE *df)
+{
+ int i;
+ struct dumptime *dtwalk;
+
+ for (;;) {
+ dtwalk = (struct dumptime *)calloc(1, sizeof (struct dumptime));
+ if (getrecord(df, &(dtwalk->dt_value)) < 0)
+ break;
+ nddates++;
+ SLIST_INSERT_HEAD(&dthead, dtwalk, dt_list);
+ }
+
+ /*
+ * arrayify the list, leaving enough room for the additional
+ * record that we may have to add to the ddate structure
+ */
+ ddatev = (struct dumpdates **)
+ calloc((unsigned) (nddates + 1), sizeof (struct dumpdates *));
+ dtwalk = SLIST_FIRST(&dthead);
+ for (i = nddates - 1; i >= 0; i--, dtwalk = SLIST_NEXT(dtwalk, dt_list))
+ ddatev[i] = &dtwalk->dt_value;
+}
+
+void
+getdumptime(void)
+{
+ struct dumpdates *ddp;
+ int i;
+ char *fname;
+
+ fname = disk;
+#ifdef FDEBUG
+ msg("Looking for name %s in dumpdates = %s for level = %c\n",
+ fname, dumpdates, level);
+#endif
+ spcl.c_ddate = 0;
+ lastlevel = '0';
+
+ initdumptimes();
+ /*
+ * Go find the entry with the same name for a lower increment
+ * and older date
+ */
+ ITITERATE(i, ddp) {
+ if (strncmp(fname, ddp->dd_name, sizeof (ddp->dd_name)) != 0)
+ continue;
+ if (ddp->dd_level >= level)
+ continue;
+ if (ddp->dd_ddate <= _time64_to_time(spcl.c_ddate))
+ continue;
+ spcl.c_ddate = _time_to_time64(ddp->dd_ddate);
+ lastlevel = ddp->dd_level;
+ }
+}
+
+void
+putdumptime(void)
+{
+ FILE *df;
+ struct dumpdates *dtwalk;
+ int i;
+ int fd;
+ char *fname;
+ char *tmsg;
+
+ if(uflag == 0)
+ return;
+ if ((df = fopen(dumpdates, "r+")) == NULL)
+ quit("cannot rewrite %s: %s\n", dumpdates, strerror(errno));
+ fd = fileno(df);
+ (void) flock(fd, LOCK_EX);
+ fname = disk;
+ free((char *)ddatev);
+ ddatev = 0;
+ nddates = 0;
+ readdumptimes(df);
+ if (fseek(df, 0L, 0) < 0)
+ quit("fseek: %s\n", strerror(errno));
+ spcl.c_ddate = 0;
+ ITITERATE(i, dtwalk) {
+ if (strncmp(fname, dtwalk->dd_name,
+ sizeof (dtwalk->dd_name)) != 0)
+ continue;
+ if (dtwalk->dd_level != level)
+ continue;
+ goto found;
+ }
+ /*
+ * construct the new upper bound;
+ * Enough room has been allocated.
+ */
+ dtwalk = ddatev[nddates] =
+ (struct dumpdates *)calloc(1, sizeof (struct dumpdates));
+ nddates += 1;
+ found:
+ (void) strncpy(dtwalk->dd_name, fname, sizeof (dtwalk->dd_name));
+ dtwalk->dd_level = level;
+ dtwalk->dd_ddate = _time64_to_time(spcl.c_date);
+
+ ITITERATE(i, dtwalk) {
+ dumprecout(df, dtwalk);
+ }
+ if (fflush(df))
+ quit("%s: %s\n", dumpdates, strerror(errno));
+ if (ftruncate(fd, ftell(df)))
+ quit("ftruncate (%s): %s\n", dumpdates, strerror(errno));
+ (void) fclose(df);
+ if (spcl.c_date == 0) {
+ tmsg = "the epoch\n";
+ } else {
+ time_t t = _time64_to_time(spcl.c_date);
+ tmsg = ctime(&t);
+ }
+ msg("level %c dump on %s", level, tmsg);
+}
+
+static void
+dumprecout(FILE *file, const struct dumpdates *what)
+{
+
+ if (fprintf(file, DUMPOUTFMT, what->dd_name,
+ what->dd_level, ctime(&what->dd_ddate)) < 0)
+ quit("%s: %s\n", dumpdates, strerror(errno));
+}
+
+int recno;
+
+static int
+getrecord(FILE *df, struct dumpdates *ddatep)
+{
+ char tbuf[BUFSIZ];
+
+ recno = 0;
+ if ( (fgets(tbuf, sizeof (tbuf), df)) != tbuf)
+ return(-1);
+ recno++;
+ if (makedumpdate(ddatep, tbuf) < 0)
+ msg("Unknown intermediate format in %s, line %d\n",
+ dumpdates, recno);
+
+#ifdef FDEBUG
+ msg("getrecord: %s %c %s", ddatep->dd_name, ddatep->dd_level,
+ ddatep->dd_ddate == 0 ? "the epoch\n" : ctime(&ddatep->dd_ddate));
+#endif
+ return(0);
+}
+
+static int
+makedumpdate(struct dumpdates *ddp, const char *tbuf)
+{
+ char un_buf[128];
+
+ (void) sscanf(tbuf, DUMPINFMT, ddp->dd_name, &ddp->dd_level, un_buf);
+ ddp->dd_ddate = unctime(un_buf);
+ if (ddp->dd_ddate < 0)
+ return(-1);
+ return(0);
+}
diff --git a/sbin/dump/main.c b/sbin/dump/main.c
new file mode 100644
index 0000000..6af0995
--- /dev/null
+++ b/sbin/dump/main.c
@@ -0,0 +1,749 @@
+/*-
+ * Copyright (c) 1980, 1991, 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/1/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mount.h>
+#include <sys/disklabel.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ffs/fs.h>
+
+#include <protocols/dumprestore.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <timeconv.h>
+#include <unistd.h>
+
+#include "dump.h"
+#include "pathnames.h"
+
+int notify = 0; /* notify operator flag */
+int snapdump = 0; /* dumping live filesystem, so use snapshot */
+int blockswritten = 0; /* number of blocks written on current tape */
+int tapeno = 0; /* current tape number */
+int density = 0; /* density in bytes/0.1" " <- this is for hilit19 */
+int ntrec = NTREC; /* # tape blocks in each tape record */
+int cartridge = 0; /* Assume non-cartridge tape */
+int cachesize = 0; /* block cache size (in bytes), defaults to 0 */
+long dev_bsize = 1; /* recalculated below */
+long blocksperfile; /* output blocks per file */
+char *host = NULL; /* remote host (if any) */
+
+/*
+ * Possible superblock locations ordered from most to least likely.
+ */
+static int sblock_try[] = SBLOCKSEARCH;
+
+static char *getmntpt(char *, int *);
+static long numarg(const char *, long, long);
+static void obsolete(int *, char **[]);
+static void usage(void) __dead2;
+
+int
+main(int argc, char *argv[])
+{
+ struct stat sb;
+ ino_t ino;
+ int dirty;
+ union dinode *dp;
+ struct fstab *dt;
+ char *map, *mntpt;
+ int ch, mode, mntflags;
+ int i, anydirskipped, bflag = 0, Tflag = 0, honorlevel = 1;
+ int just_estimate = 0;
+ ino_t maxino;
+ char *tmsg;
+
+ spcl.c_date = _time_to_time64(time(NULL));
+
+ tsize = 0; /* Default later, based on 'c' option for cart tapes */
+ dumpdates = _PATH_DUMPDATES;
+ popenout = NULL;
+ tape = NULL;
+ temp = _PATH_DTMP;
+ if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0)
+ quit("TP_BSIZE must be a multiple of DEV_BSIZE\n");
+ level = '0';
+
+ if (argc < 2)
+ usage();
+
+ obsolete(&argc, &argv);
+ while ((ch = getopt(argc, argv,
+ "0123456789aB:b:C:cD:d:f:h:LnP:Ss:T:uWw")) != -1)
+ switch (ch) {
+ /* dump level */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ level = ch;
+ break;
+
+ case 'a': /* `auto-size', Write to EOM. */
+ unlimited = 1;
+ break;
+
+ case 'B': /* blocks per output file */
+ blocksperfile = numarg("number of blocks per file",
+ 1L, 0L);
+ break;
+
+ case 'b': /* blocks per tape write */
+ ntrec = numarg("number of blocks per write",
+ 1L, 1000L);
+ break;
+
+ case 'C':
+ cachesize = numarg("cachesize", 0, 0) * 1024 * 1024;
+ break;
+
+ case 'c': /* Tape is cart. not 9-track */
+ cartridge = 1;
+ break;
+
+ case 'D':
+ dumpdates = optarg;
+ break;
+
+ case 'd': /* density, in bits per inch */
+ density = numarg("density", 10L, 327670L) / 10;
+ if (density >= 625 && !bflag)
+ ntrec = HIGHDENSITYTREC;
+ break;
+
+ case 'f': /* output file */
+ if (popenout != NULL)
+ errx(X_STARTUP, "You cannot use the P and f "
+ "flags together.\n");
+ tape = optarg;
+ break;
+
+ case 'h':
+ honorlevel = numarg("honor level", 0L, 10L);
+ break;
+
+ case 'L':
+ snapdump = 1;
+ break;
+
+ case 'n': /* notify operators */
+ notify = 1;
+ break;
+
+ case 'P':
+ if (tape != NULL)
+ errx(X_STARTUP, "You cannot use the P and f "
+ "flags together.\n");
+ popenout = optarg;
+ break;
+
+ case 'S': /* exit after estimating # of tapes */
+ just_estimate = 1;
+ break;
+
+ case 's': /* tape size, feet */
+ tsize = numarg("tape size", 1L, 0L) * 12 * 10;
+ break;
+
+ case 'T': /* time of last dump */
+ spcl.c_ddate = unctime(optarg);
+ if (spcl.c_ddate < 0) {
+ (void)fprintf(stderr, "bad time \"%s\"\n",
+ optarg);
+ exit(X_STARTUP);
+ }
+ Tflag = 1;
+ lastlevel = '?';
+ break;
+
+ case 'u': /* update /etc/dumpdates */
+ uflag = 1;
+ break;
+
+ case 'W': /* what to do */
+ case 'w':
+ lastdump(ch);
+ exit(X_FINOK); /* do nothing else */
+
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ (void)fprintf(stderr, "Must specify disk or file system\n");
+ exit(X_STARTUP);
+ }
+ disk = *argv++;
+ argc--;
+ if (argc >= 1) {
+ (void)fprintf(stderr, "Unknown arguments to dump:");
+ while (argc--)
+ (void)fprintf(stderr, " %s", *argv++);
+ (void)fprintf(stderr, "\n");
+ exit(X_STARTUP);
+ }
+ if (Tflag && uflag) {
+ (void)fprintf(stderr,
+ "You cannot use the T and u flags together.\n");
+ exit(X_STARTUP);
+ }
+ if (popenout) {
+ tape = "child pipeline process";
+ } else if (tape == NULL && (tape = getenv("TAPE")) == NULL)
+ tape = _PATH_DEFTAPE;
+ if (strcmp(tape, "-") == 0) {
+ pipeout++;
+ tape = "standard output";
+ }
+
+ if (blocksperfile)
+ blocksperfile = blocksperfile / ntrec * ntrec; /* round down */
+ else if (!unlimited) {
+ /*
+ * Determine how to default tape size and density
+ *
+ * density tape size
+ * 9-track 1600 bpi (160 bytes/.1") 2300 ft.
+ * 9-track 6250 bpi (625 bytes/.1") 2300 ft.
+ * cartridge 8000 bpi (100 bytes/.1") 1700 ft.
+ * (450*4 - slop)
+ * hilit19 hits again: "
+ */
+ if (density == 0)
+ density = cartridge ? 100 : 160;
+ if (tsize == 0)
+ tsize = cartridge ? 1700L*120L : 2300L*120L;
+ }
+
+ if (strchr(tape, ':')) {
+ host = tape;
+ tape = strchr(host, ':');
+ *tape++ = '\0';
+#ifdef RDUMP
+ if (index(tape, '\n')) {
+ (void)fprintf(stderr, "invalid characters in tape\n");
+ exit(X_STARTUP);
+ }
+ if (rmthost(host) == 0)
+ exit(X_STARTUP);
+#else
+ (void)fprintf(stderr, "remote dump not enabled\n");
+ exit(X_STARTUP);
+#endif
+ }
+ (void)setuid(getuid()); /* rmthost() is the only reason to be setuid */
+
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
+ signal(SIGHUP, sig);
+ if (signal(SIGTRAP, SIG_IGN) != SIG_IGN)
+ signal(SIGTRAP, sig);
+ if (signal(SIGFPE, SIG_IGN) != SIG_IGN)
+ signal(SIGFPE, sig);
+ if (signal(SIGBUS, SIG_IGN) != SIG_IGN)
+ signal(SIGBUS, sig);
+ if (signal(SIGSEGV, SIG_IGN) != SIG_IGN)
+ signal(SIGSEGV, sig);
+ if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, sig);
+ if (signal(SIGINT, interrupt) == SIG_IGN)
+ signal(SIGINT, SIG_IGN);
+
+ dump_getfstab(); /* /etc/fstab snarfed */
+ /*
+ * disk can be either the full special file name,
+ * the suffix of the special file name,
+ * the special name missing the leading '/',
+ * the file system name with or without the leading '/'.
+ */
+ dt = fstabsearch(disk);
+ if (dt != NULL) {
+ disk = rawname(dt->fs_spec);
+ (void)strncpy(spcl.c_dev, dt->fs_spec, NAMELEN);
+ (void)strncpy(spcl.c_filesys, dt->fs_file, NAMELEN);
+ } else {
+ (void)strncpy(spcl.c_dev, disk, NAMELEN);
+ (void)strncpy(spcl.c_filesys, "an unlisted file system",
+ NAMELEN);
+ }
+ spcl.c_dev[NAMELEN-1]='\0';
+ spcl.c_filesys[NAMELEN-1]='\0';
+
+ if ((mntpt = getmntpt(disk, &mntflags)) != 0) {
+ if (mntflags & MNT_RDONLY) {
+ if (snapdump != 0) {
+ msg("WARNING: %s\n",
+ "-L ignored for read-only filesystem.");
+ snapdump = 0;
+ }
+ } else if (snapdump == 0) {
+ msg("WARNING: %s\n",
+ "should use -L when dumping live read-write "
+ "filesystems!");
+ } else {
+ char snapname[BUFSIZ], snapcmd[BUFSIZ];
+
+ snprintf(snapname, sizeof snapname, "%s/.snap", mntpt);
+ if ((stat(snapname, &sb) < 0) || !S_ISDIR(sb.st_mode)) {
+ msg("WARNING: %s %s\n",
+ "-L requested but snapshot location",
+ snapname);
+ msg(" %s: %s\n",
+ "is not a directory",
+ "dump downgraded, -L ignored");
+ snapdump = 0;
+ } else {
+ snprintf(snapname, sizeof snapname,
+ "%s/.snap/dump_snapshot", mntpt);
+ snprintf(snapcmd, sizeof snapcmd, "%s %s %s",
+ _PATH_MKSNAP_FFS, mntpt, snapname);
+ unlink(snapname);
+ if (system(snapcmd) != 0)
+ errx(X_STARTUP, "Cannot create %s: %s\n",
+ snapname, strerror(errno));
+ if ((diskfd = open(snapname, O_RDONLY)) < 0) {
+ unlink(snapname);
+ errx(X_STARTUP, "Cannot open %s: %s\n",
+ snapname, strerror(errno));
+ }
+ unlink(snapname);
+ if (fstat(diskfd, &sb) != 0)
+ err(X_STARTUP, "%s: stat", snapname);
+ spcl.c_date = _time_to_time64(sb.st_mtime);
+ }
+ }
+ } else if (snapdump != 0) {
+ msg("WARNING: Cannot use -L on an unmounted filesystem.\n");
+ snapdump = 0;
+ }
+ if (snapdump == 0) {
+ if ((diskfd = open(disk, O_RDONLY)) < 0)
+ err(X_STARTUP, "Cannot open %s", disk);
+ if (fstat(diskfd, &sb) != 0)
+ err(X_STARTUP, "%s: stat", disk);
+ if (S_ISDIR(sb.st_mode))
+ errx(X_STARTUP, "%s: unknown file system", disk);
+ }
+
+ (void)strcpy(spcl.c_label, "none");
+ (void)gethostname(spcl.c_host, NAMELEN);
+ spcl.c_level = level - '0';
+ spcl.c_type = TS_TAPE;
+
+ if (spcl.c_date == 0) {
+ tmsg = "the epoch\n";
+ } else {
+ time_t t = _time64_to_time(spcl.c_date);
+ tmsg = ctime(&t);
+ }
+ msg("Date of this level %c dump: %s", level, tmsg);
+
+ if (!Tflag)
+ getdumptime(); /* /etc/dumpdates snarfed */
+ if (spcl.c_ddate == 0) {
+ tmsg = "the epoch\n";
+ } else {
+ time_t t = _time64_to_time(spcl.c_ddate);
+ tmsg = ctime(&t);
+ }
+ msg("Date of last level %c dump: %s", lastlevel, tmsg);
+
+ msg("Dumping %s%s ", snapdump ? "snapshot of ": "", disk);
+ if (dt != NULL)
+ msgtail("(%s) ", dt->fs_file);
+ if (host)
+ msgtail("to %s on host %s\n", tape, host);
+ else
+ msgtail("to %s\n", tape);
+
+ sync();
+ sblock = (struct fs *)sblock_buf;
+ for (i = 0; sblock_try[i] != -1; i++) {
+ sblock->fs_fsize = SBLOCKSIZE; /* needed in bread */
+ bread(sblock_try[i] >> dev_bshift, (char *) sblock, SBLOCKSIZE);
+ if ((sblock->fs_magic == FS_UFS1_MAGIC ||
+ (sblock->fs_magic == FS_UFS2_MAGIC &&
+ sblock->fs_sblockloc == sblock_try[i])) &&
+ sblock->fs_bsize <= MAXBSIZE &&
+ sblock->fs_bsize >= sizeof(struct fs))
+ break;
+ }
+ if (sblock_try[i] == -1)
+ quit("Cannot find file system superblock\n");
+ dev_bsize = sblock->fs_fsize / fsbtodb(sblock, 1);
+ dev_bshift = ffs(dev_bsize) - 1;
+ if (dev_bsize != (1 << dev_bshift))
+ quit("dev_bsize (%ld) is not a power of 2", dev_bsize);
+ tp_bshift = ffs(TP_BSIZE) - 1;
+ if (TP_BSIZE != (1 << tp_bshift))
+ quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE);
+ maxino = sblock->fs_ipg * sblock->fs_ncg;
+ mapsize = roundup(howmany(maxino, CHAR_BIT), TP_BSIZE);
+ usedinomap = (char *)calloc((unsigned) mapsize, sizeof(char));
+ dumpdirmap = (char *)calloc((unsigned) mapsize, sizeof(char));
+ dumpinomap = (char *)calloc((unsigned) mapsize, sizeof(char));
+ tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
+
+ nonodump = spcl.c_level < honorlevel;
+
+ passno = 1;
+ setproctitle("%s: pass 1: regular files", disk);
+ msg("mapping (Pass I) [regular files]\n");
+ anydirskipped = mapfiles(maxino, &tapesize);
+
+ passno = 2;
+ setproctitle("%s: pass 2: directories", disk);
+ msg("mapping (Pass II) [directories]\n");
+ while (anydirskipped) {
+ anydirskipped = mapdirs(maxino, &tapesize);
+ }
+
+ if (pipeout || unlimited) {
+ tapesize += 10; /* 10 trailer blocks */
+ msg("estimated %ld tape blocks.\n", tapesize);
+ } else {
+ double fetapes;
+
+ if (blocksperfile)
+ fetapes = (double) tapesize / blocksperfile;
+ else if (cartridge) {
+ /* Estimate number of tapes, assuming streaming stops at
+ the end of each block written, and not in mid-block.
+ Assume no erroneous blocks; this can be compensated
+ for with an artificially low tape size. */
+ fetapes =
+ ( (double) tapesize /* blocks */
+ * TP_BSIZE /* bytes/block */
+ * (1.0/density) /* 0.1" / byte " */
+ +
+ (double) tapesize /* blocks */
+ * (1.0/ntrec) /* streaming-stops per block */
+ * 15.48 /* 0.1" / streaming-stop " */
+ ) * (1.0 / tsize ); /* tape / 0.1" " */
+ } else {
+ /* Estimate number of tapes, for old fashioned 9-track
+ tape */
+ int tenthsperirg = (density == 625) ? 3 : 7;
+ fetapes =
+ ( (double) tapesize /* blocks */
+ * TP_BSIZE /* bytes / block */
+ * (1.0/density) /* 0.1" / byte " */
+ +
+ (double) tapesize /* blocks */
+ * (1.0/ntrec) /* IRG's / block */
+ * tenthsperirg /* 0.1" / IRG " */
+ ) * (1.0 / tsize ); /* tape / 0.1" " */
+ }
+ etapes = fetapes; /* truncating assignment */
+ etapes++;
+ /* count the dumped inodes map on each additional tape */
+ tapesize += (etapes - 1) *
+ (howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
+ tapesize += etapes + 10; /* headers + 10 trailer blks */
+ msg("estimated %ld tape blocks on %3.2f tape(s).\n",
+ tapesize, fetapes);
+ }
+
+ /*
+ * If the user only wants an estimate of the number of
+ * tapes, exit now.
+ */
+ if (just_estimate)
+ exit(0);
+
+ /*
+ * Allocate tape buffer.
+ */
+ if (!alloctape())
+ quit(
+ "can't allocate tape buffers - try a smaller blocking factor.\n");
+
+ startnewtape(1);
+ (void)time((time_t *)&(tstart_writing));
+ dumpmap(usedinomap, TS_CLRI, maxino - 1);
+
+ passno = 3;
+ setproctitle("%s: pass 3: directories", disk);
+ msg("dumping (Pass III) [directories]\n");
+ dirty = 0; /* XXX just to get gcc to shut up */
+ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
+ if (((ino - 1) % CHAR_BIT) == 0) /* map is offset by 1 */
+ dirty = *map++;
+ else
+ dirty >>= 1;
+ if ((dirty & 1) == 0)
+ continue;
+ /*
+ * Skip directory inodes deleted and maybe reallocated
+ */
+ dp = getino(ino, &mode);
+ if (mode != IFDIR)
+ continue;
+ (void)dumpino(dp, ino);
+ }
+
+ passno = 4;
+ setproctitle("%s: pass 4: regular files", disk);
+ msg("dumping (Pass IV) [regular files]\n");
+ for (map = dumpinomap, ino = 1; ino < maxino; ino++) {
+ if (((ino - 1) % CHAR_BIT) == 0) /* map is offset by 1 */
+ dirty = *map++;
+ else
+ dirty >>= 1;
+ if ((dirty & 1) == 0)
+ continue;
+ /*
+ * Skip inodes deleted and reallocated as directories.
+ */
+ dp = getino(ino, &mode);
+ if (mode == IFDIR)
+ continue;
+ (void)dumpino(dp, ino);
+ }
+
+ (void)time((time_t *)&(tend_writing));
+ spcl.c_type = TS_END;
+ for (i = 0; i < ntrec; i++)
+ writeheader(maxino - 1);
+ if (pipeout)
+ msg("DUMP: %jd tape blocks\n", (intmax_t)spcl.c_tapea);
+ else
+ msg("DUMP: %jd tape blocks on %d volume%s\n",
+ (intmax_t)spcl.c_tapea, spcl.c_volume,
+ (spcl.c_volume == 1) ? "" : "s");
+
+ /* report dump performance, avoid division through zero */
+ if (tend_writing - tstart_writing == 0)
+ msg("finished in less than a second\n");
+ else
+ msg("finished in %jd seconds, throughput %jd KBytes/sec\n",
+ (intmax_t)tend_writing - tstart_writing,
+ (intmax_t)(spcl.c_tapea /
+ (tend_writing - tstart_writing)));
+
+ putdumptime();
+ trewind();
+ broadcast("DUMP IS DONE!\a\a\n");
+ msg("DUMP IS DONE\n");
+ Exit(X_FINOK);
+ /* NOTREACHED */
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: dump [-0123456789acLnSu] [-B records] [-b blocksize] [-C cachesize]\n"
+ " [-D dumpdates] [-d density] [-f file | -P pipecommand] [-h level]\n"
+ " [-s feet] [-T date] filesystem\n"
+ " dump -W | -w\n");
+ exit(X_STARTUP);
+}
+
+/*
+ * Check to see if a disk is currently mounted.
+ */
+static char *
+getmntpt(char *name, int *mntflagsp)
+{
+ long mntsize, i;
+ struct statfs *mntbuf;
+
+ mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
+ for (i = 0; i < mntsize; i++) {
+ if (!strcmp(mntbuf[i].f_mntfromname, name)) {
+ *mntflagsp = mntbuf[i].f_flags;
+ return (mntbuf[i].f_mntonname);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Pick up a numeric argument. It must be nonnegative and in the given
+ * range (except that a vmax of 0 means unlimited).
+ */
+static long
+numarg(const char *meaning, long vmin, long vmax)
+{
+ char *p;
+ long val;
+
+ val = strtol(optarg, &p, 10);
+ if (*p)
+ errx(1, "illegal %s -- %s", meaning, optarg);
+ if (val < vmin || (vmax && val > vmax))
+ errx(1, "%s must be between %ld and %ld", meaning, vmin, vmax);
+ return (val);
+}
+
+void
+sig(int signo)
+{
+ switch(signo) {
+ case SIGALRM:
+ case SIGBUS:
+ case SIGFPE:
+ case SIGHUP:
+ case SIGTERM:
+ case SIGTRAP:
+ if (pipeout)
+ quit("Signal on pipe: cannot recover\n");
+ msg("Rewriting attempted as response to unknown signal.\n");
+ (void)fflush(stderr);
+ (void)fflush(stdout);
+ close_rewind();
+ exit(X_REWRITE);
+ /* NOTREACHED */
+ case SIGSEGV:
+ msg("SIGSEGV: ABORTING!\n");
+ (void)signal(SIGSEGV, SIG_DFL);
+ (void)kill(0, SIGSEGV);
+ /* NOTREACHED */
+ }
+}
+
+char *
+rawname(char *cp)
+{
+ struct stat sb;
+
+ /*
+ * Ensure that the device passed in is a raw device.
+ */
+ if (stat(cp, &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFCHR)
+ return (cp);
+
+ /*
+ * Since there's only one device type now, we can't construct any
+ * better name, so we have to return NULL.
+ */
+ return (NULL);
+}
+
+/*
+ * obsolete --
+ * Change set of key letters and ordered arguments into something
+ * getopt(3) will like.
+ */
+static void
+obsolete(int *argcp, char **argvp[])
+{
+ int argc, flags;
+ char *ap, **argv, *flagsp, **nargv, *p;
+
+ /* Setup. */
+ argv = *argvp;
+ argc = *argcp;
+
+ /* Return if no arguments or first argument has leading dash. */
+ ap = argv[1];
+ if (argc == 1 || *ap == '-')
+ return;
+
+ /* Allocate space for new arguments. */
+ if ((*argvp = nargv = malloc((argc + 1) * sizeof(char *))) == NULL ||
+ (p = flagsp = malloc(strlen(ap) + 2)) == NULL)
+ err(1, NULL);
+
+ *nargv++ = *argv;
+ argv += 2;
+
+ for (flags = 0; *ap; ++ap) {
+ switch (*ap) {
+ case 'B':
+ case 'b':
+ case 'd':
+ case 'f':
+ case 'D':
+ case 'C':
+ case 'h':
+ case 's':
+ case 'T':
+ if (*argv == NULL) {
+ warnx("option requires an argument -- %c", *ap);
+ usage();
+ }
+ if ((nargv[0] = malloc(strlen(*argv) + 2 + 1)) == NULL)
+ err(1, NULL);
+ nargv[0][0] = '-';
+ nargv[0][1] = *ap;
+ (void)strcpy(&nargv[0][2], *argv);
+ ++argv;
+ ++nargv;
+ break;
+ default:
+ if (!flags) {
+ *p++ = '-';
+ flags = 1;
+ }
+ *p++ = *ap;
+ break;
+ }
+ }
+
+ /* Terminate flags. */
+ if (flags) {
+ *p = '\0';
+ *nargv++ = flagsp;
+ }
+
+ /* Copy remaining arguments. */
+ while ((*nargv++ = *argv++));
+
+ /* Update argument count. */
+ *argcp = nargv - *argvp - 1;
+}
diff --git a/sbin/dump/optr.c b/sbin/dump/optr.c
new file mode 100644
index 0000000..3dc17b6
--- /dev/null
+++ b/sbin/dump/optr.c
@@ -0,0 +1,425 @@
+/*-
+ * Copyright (c) 1980, 1988, 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.
+ * 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 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 lint
+#if 0
+static char sccsid[] = "@(#)optr.c 8.2 (Berkeley) 1/6/94";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+
+#include <ufs/ufs/dinode.h>
+
+#include <errno.h>
+#include <fstab.h>
+#include <grp.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "dump.h"
+#include "pathnames.h"
+
+void alarmcatch(int);
+int datesort(const void *, const void *);
+
+/*
+ * Query the operator; This previously-fascist piece of code
+ * no longer requires an exact response.
+ * It is intended to protect dump aborting by inquisitive
+ * people banging on the console terminal to see what is
+ * happening which might cause dump to croak, destroying
+ * a large number of hours of work.
+ *
+ * Every 2 minutes we reprint the message, alerting others
+ * that dump needs attention.
+ */
+static int timeout;
+static const char *attnmessage; /* attention message */
+
+int
+query(const char *question)
+{
+ char replybuffer[64];
+ int back, errcount;
+ FILE *mytty;
+
+ if ((mytty = fopen(_PATH_TTY, "r")) == NULL)
+ quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno));
+ attnmessage = question;
+ timeout = 0;
+ alarmcatch(0);
+ back = -1;
+ errcount = 0;
+ do {
+ if (fgets(replybuffer, 63, mytty) == NULL) {
+ clearerr(mytty);
+ if (++errcount > 30) /* XXX ugly */
+ quit("excessive operator query failures\n");
+ } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') {
+ back = 1;
+ } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') {
+ back = 0;
+ } else {
+ (void) fprintf(stderr,
+ " DUMP: \"Yes\" or \"No\"?\n");
+ (void) fprintf(stderr,
+ " DUMP: %s: (\"yes\" or \"no\") ", question);
+ }
+ } while (back < 0);
+
+ /*
+ * Turn off the alarm, and reset the signal to trap out..
+ */
+ (void) alarm(0);
+ if (signal(SIGALRM, sig) == SIG_IGN)
+ signal(SIGALRM, SIG_IGN);
+ (void) fclose(mytty);
+ return(back);
+}
+
+char lastmsg[BUFSIZ];
+
+/*
+ * Alert the console operator, and enable the alarm clock to
+ * sleep for 2 minutes in case nobody comes to satisfy dump
+ */
+void
+alarmcatch(int sig __unused)
+{
+ if (notify == 0) {
+ if (timeout == 0)
+ (void) fprintf(stderr,
+ " DUMP: %s: (\"yes\" or \"no\") ",
+ attnmessage);
+ else
+ msgtail("\a\a");
+ } else {
+ if (timeout) {
+ msgtail("\n");
+ broadcast(""); /* just print last msg */
+ }
+ (void) fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ",
+ attnmessage);
+ }
+ signal(SIGALRM, alarmcatch);
+ (void) alarm(120);
+ timeout = 1;
+}
+
+/*
+ * Here if an inquisitive operator interrupts the dump program
+ */
+void
+interrupt(int signo __unused)
+{
+ msg("Interrupt received.\n");
+ if (query("Do you want to abort dump?"))
+ dumpabort(0);
+}
+
+/*
+ * We now use wall(1) to do the actual broadcasting.
+ */
+void
+broadcast(const char *message)
+{
+ FILE *fp;
+ char buf[sizeof(_PATH_WALL) + sizeof(OPGRENT) + 3];
+
+ if (!notify)
+ return;
+
+ snprintf(buf, sizeof(buf), "%s -g %s", _PATH_WALL, OPGRENT);
+ if ((fp = popen(buf, "w")) == NULL)
+ return;
+
+ (void) fputs("\a\a\aMessage from the dump program to all operators\n\nDUMP: NEEDS ATTENTION: ", fp);
+ if (lastmsg[0])
+ (void) fputs(lastmsg, fp);
+ if (message[0])
+ (void) fputs(message, fp);
+
+ (void) pclose(fp);
+}
+
+/*
+ * Print out an estimate of the amount of time left to do the dump
+ */
+
+time_t tschedule = 0;
+
+void
+timeest(void)
+{
+ double percent;
+ time_t tnow, tdone;
+ int deltat, hours, mins;
+
+ (void)time(&tnow);
+ if (blockswritten > tapesize) {
+ setproctitle("%s: 99.99%% done, finished soon", disk);
+ if (tnow >= tschedule) {
+ tschedule = tnow + 300;
+ msg("99.99%% done, finished soon\n");
+ }
+ } else {
+ deltat = (blockswritten == 0) ? 0 : tstart_writing - tnow +
+ (double)(tnow - tstart_writing) / blockswritten * tapesize;
+ tdone = tnow + deltat;
+ percent = (blockswritten * 100.0) / tapesize;
+ hours = deltat / 3600;
+ mins = (deltat % 3600) / 60;
+
+ setproctitle(
+ "%s: pass %d: %3.2f%% done, finished in %d:%02d at %s",
+ disk, passno, percent, hours, mins, ctime(&tdone));
+ if (tnow >= tschedule) {
+ tschedule = tnow + 300;
+ if (blockswritten < 500)
+ return;
+ msg("%3.2f%% done, finished in %d:%02d at %s", percent,
+ hours, mins, ctime(&tdone));
+ }
+ }
+}
+
+/*
+ * Schedule a printout of the estimate in the next call to timeest().
+ */
+void
+infosch(int signal __unused)
+{
+ tschedule = 0;
+}
+
+void
+msg(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void) fprintf(stderr," DUMP: ");
+#ifdef TDEBUG
+ (void) fprintf(stderr, "pid=%d ", getpid());
+#endif
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void) fflush(stdout);
+ (void) fflush(stderr);
+ va_start(ap, fmt);
+ (void) vsnprintf(lastmsg, sizeof(lastmsg), fmt, ap);
+ va_end(ap);
+}
+
+void
+msgtail(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+void
+quit(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void) fprintf(stderr," DUMP: ");
+#ifdef TDEBUG
+ (void) fprintf(stderr, "pid=%d ", getpid());
+#endif
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void) fflush(stdout);
+ (void) fflush(stderr);
+ dumpabort(0);
+}
+
+/*
+ * Tell the operator what has to be done;
+ * we don't actually do it
+ */
+
+struct fstab *
+allocfsent(const struct fstab *fs)
+{
+ struct fstab *new;
+
+ new = (struct fstab *)malloc(sizeof (*fs));
+ if (new == NULL ||
+ (new->fs_file = strdup(fs->fs_file)) == NULL ||
+ (new->fs_type = strdup(fs->fs_type)) == NULL ||
+ (new->fs_spec = strdup(fs->fs_spec)) == NULL)
+ quit("%s\n", strerror(errno));
+ new->fs_passno = fs->fs_passno;
+ new->fs_freq = fs->fs_freq;
+ return (new);
+}
+
+struct pfstab {
+ SLIST_ENTRY(pfstab) pf_list;
+ struct fstab *pf_fstab;
+};
+
+static SLIST_HEAD(, pfstab) table;
+
+void
+dump_getfstab(void)
+{
+ struct fstab *fs;
+ struct pfstab *pf;
+
+ if (setfsent() == 0) {
+ msg("Can't open %s for dump table information: %s\n",
+ _PATH_FSTAB, strerror(errno));
+ return;
+ }
+ while ((fs = getfsent()) != NULL) {
+ if (strcmp(fs->fs_type, FSTAB_RW) &&
+ strcmp(fs->fs_type, FSTAB_RO) &&
+ strcmp(fs->fs_type, FSTAB_RQ))
+ continue;
+ fs = allocfsent(fs);
+ if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL)
+ quit("%s\n", strerror(errno));
+ pf->pf_fstab = fs;
+ SLIST_INSERT_HEAD(&table, pf, pf_list);
+ }
+ (void) endfsent();
+}
+
+/*
+ * Search in the fstab for a file name.
+ * This file name can be either the special or the path file name.
+ *
+ * The file name can omit the leading '/'.
+ */
+struct fstab *
+fstabsearch(const char *key)
+{
+ struct pfstab *pf;
+ struct fstab *fs;
+ char *rn;
+
+ SLIST_FOREACH(pf, &table, pf_list) {
+ fs = pf->pf_fstab;
+ if (strcmp(fs->fs_file, key) == 0 ||
+ strcmp(fs->fs_spec, key) == 0)
+ return (fs);
+ rn = rawname(fs->fs_spec);
+ if (rn != NULL && strcmp(rn, key) == 0)
+ return (fs);
+ if (key[0] != '/') {
+ if (*fs->fs_spec == '/' &&
+ strcmp(fs->fs_spec + 1, key) == 0)
+ return (fs);
+ if (*fs->fs_file == '/' &&
+ strcmp(fs->fs_file + 1, key) == 0)
+ return (fs);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Tell the operator what to do
+ */
+void
+lastdump(int arg) /* w ==> just what to do; W ==> most recent dumps */
+{
+ int i;
+ struct fstab *dt;
+ struct dumpdates *dtwalk;
+ char *lastname, *date;
+ int dumpme;
+ time_t tnow;
+ struct tm *tlast;
+
+ (void) time(&tnow);
+ dump_getfstab(); /* /etc/fstab input */
+ initdumptimes(); /* /etc/dumpdates input */
+ qsort((char *) ddatev, nddates, sizeof(struct dumpdates *), datesort);
+
+ if (arg == 'w')
+ (void) printf("Dump these file systems:\n");
+ else
+ (void) printf("Last dump(s) done (Dump '>' file systems):\n");
+ lastname = "??";
+ ITITERATE(i, dtwalk) {
+ if (strncmp(lastname, dtwalk->dd_name,
+ sizeof(dtwalk->dd_name)) == 0)
+ continue;
+ date = (char *)ctime(&dtwalk->dd_ddate);
+ date[16] = '\0'; /* blast away seconds and year */
+ lastname = dtwalk->dd_name;
+ dt = fstabsearch(dtwalk->dd_name);
+ dumpme = (dt != NULL && dt->fs_freq != 0);
+ if (dumpme) {
+ tlast = localtime(&dtwalk->dd_ddate);
+ dumpme = tnow > (dtwalk->dd_ddate - (tlast->tm_hour * 3600)
+ - (tlast->tm_min * 60) - tlast->tm_sec
+ + (dt->fs_freq * 86400));
+ };
+ if (arg != 'w' || dumpme)
+ (void) printf(
+ "%c %8s\t(%6s) Last dump: Level %c, Date %s\n",
+ dumpme && (arg != 'w') ? '>' : ' ',
+ dtwalk->dd_name,
+ dt ? dt->fs_file : "",
+ dtwalk->dd_level,
+ date);
+ }
+}
+
+int
+datesort(const void *a1, const void *a2)
+{
+ struct dumpdates *d1 = *(struct dumpdates **)a1;
+ struct dumpdates *d2 = *(struct dumpdates **)a2;
+ int diff;
+
+ diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name));
+ if (diff == 0)
+ return (d2->dd_ddate - d1->dd_ddate);
+ return (diff);
+}
diff --git a/sbin/dump/pathnames.h b/sbin/dump/pathnames.h
new file mode 100644
index 0000000..eb3ce39
--- /dev/null
+++ b/sbin/dump/pathnames.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ * 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 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.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/5/93
+ * $FreeBSD$
+ */
+
+#include <paths.h>
+
+#define _PATH_DEFTAPE "/dev/sa0"
+#define _PATH_DTMP "/etc/dtmp"
+#define _PATH_DUMPDATES "/etc/dumpdates"
+#define _PATH_LOCK "/tmp/dumplockXXXXXX"
+#define _PATH_RMT "/etc/rmt" /* path on remote host */
diff --git a/sbin/dump/tape.c b/sbin/dump/tape.c
new file mode 100644
index 0000000..913409f
--- /dev/null
+++ b/sbin/dump/tape.c
@@ -0,0 +1,880 @@
+/*-
+ * 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.
+ * 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 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 lint
+#if 0
+static char sccsid[] = "@(#)tape.c 8.4 (Berkeley) 5/1/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <protocols/dumprestore.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dump.h"
+
+int writesize; /* size of malloc()ed buffer for tape */
+int64_t lastspclrec = -1; /* tape block number of last written header */
+int trecno = 0; /* next record to write in current block */
+extern long blocksperfile; /* number of blocks per output file */
+long blocksthisvol; /* number of blocks on current output file */
+extern int ntrec; /* blocking factor on tape */
+extern int cartridge;
+extern char *host;
+char *nexttape;
+FILE *popenfp = NULL;
+
+static int atomic(ssize_t (*)(), int, char *, int);
+static void doslave(int, int);
+static void enslave(void);
+static void flushtape(void);
+static void killall(void);
+static void rollforward(void);
+
+/*
+ * Concurrent dump mods (Caltech) - disk block reading and tape writing
+ * are exported to several slave processes. While one slave writes the
+ * tape, the others read disk blocks; they pass control of the tape in
+ * a ring via signals. The parent process traverses the file system and
+ * sends writeheader()'s and lists of daddr's to the slaves via pipes.
+ * The following structure defines the instruction packets sent to slaves.
+ */
+struct req {
+ ufs2_daddr_t dblk;
+ int count;
+};
+int reqsiz;
+
+#define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */
+struct slave {
+ int64_t tapea; /* header number at start of this chunk */
+ int64_t firstrec; /* record number of this block */
+ int count; /* count to next header (used for TS_TAPE */
+ /* after EOT) */
+ int inode; /* inode that we are currently dealing with */
+ int fd; /* FD for this slave */
+ int pid; /* PID for this slave */
+ int sent; /* 1 == we've sent this slave requests */
+ char (*tblock)[TP_BSIZE]; /* buffer for data blocks */
+ struct req *req; /* buffer for requests */
+} slaves[SLAVES+1];
+struct slave *slp;
+
+char (*nextblock)[TP_BSIZE];
+
+int master; /* pid of master, for sending error signals */
+int tenths; /* length of tape used per block written */
+static volatile sig_atomic_t caught; /* have we caught the signal to proceed? */
+static volatile sig_atomic_t ready; /* reached the lock point without having */
+ /* received the SIGUSR2 signal from the prev slave? */
+static jmp_buf jmpbuf; /* where to jump to if we are ready when the */
+ /* SIGUSR2 arrives from the previous slave */
+
+int
+alloctape(void)
+{
+ int pgoff = getpagesize() - 1;
+ char *buf;
+ int i;
+
+ writesize = ntrec * TP_BSIZE;
+ reqsiz = (ntrec + 1) * sizeof(struct req);
+ /*
+ * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode
+ * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require
+ * repositioning after stopping, i.e, streaming mode, where the gap is
+ * variable, 0.30" to 0.45". The gap is maximal when the tape stops.
+ */
+ if (blocksperfile == 0 && !unlimited)
+ tenths = writesize / density +
+ (cartridge ? 16 : density == 625 ? 5 : 8);
+ /*
+ * Allocate tape buffer contiguous with the array of instruction
+ * packets, so flushtape() can write them together with one write().
+ * Align tape buffer on page boundary to speed up tape write().
+ */
+ for (i = 0; i <= SLAVES; i++) {
+ buf = (char *)
+ malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE));
+ if (buf == NULL)
+ return(0);
+ slaves[i].tblock = (char (*)[TP_BSIZE])
+ (((long)&buf[ntrec + 1] + pgoff) &~ pgoff);
+ slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1;
+ }
+ slp = &slaves[0];
+ slp->count = 1;
+ slp->tapea = 0;
+ slp->firstrec = 0;
+ nextblock = slp->tblock;
+ return(1);
+}
+
+void
+writerec(char *dp, int isspcl)
+{
+
+ slp->req[trecno].dblk = (ufs2_daddr_t)0;
+ slp->req[trecno].count = 1;
+ /* Can't do a structure assignment due to alignment problems */
+ bcopy(dp, *(nextblock)++, sizeof (union u_spcl));
+ if (isspcl)
+ lastspclrec = spcl.c_tapea;
+ trecno++;
+ spcl.c_tapea++;
+ if (trecno >= ntrec)
+ flushtape();
+}
+
+void
+dumpblock(ufs2_daddr_t blkno, int size)
+{
+ int avail, tpblks;
+ ufs2_daddr_t dblkno;
+
+ dblkno = fsbtodb(sblock, blkno);
+ tpblks = size >> tp_bshift;
+ while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
+ slp->req[trecno].dblk = dblkno;
+ slp->req[trecno].count = avail;
+ trecno += avail;
+ spcl.c_tapea += avail;
+ if (trecno >= ntrec)
+ flushtape();
+ dblkno += avail << (tp_bshift - dev_bshift);
+ tpblks -= avail;
+ }
+}
+
+int nogripe = 0;
+
+void
+tperror(int signo __unused)
+{
+
+ if (pipeout) {
+ msg("write error on %s\n", tape);
+ quit("Cannot recover\n");
+ /* NOTREACHED */
+ }
+ msg("write error %ld blocks into volume %d\n", blocksthisvol, tapeno);
+ broadcast("DUMP WRITE ERROR!\n");
+ if (!query("Do you want to restart?"))
+ dumpabort(0);
+ msg("Closing this volume. Prepare to restart with new media;\n");
+ msg("this dump volume will be rewritten.\n");
+ killall();
+ nogripe = 1;
+ close_rewind();
+ Exit(X_REWRITE);
+}
+
+void
+sigpipe(int signo __unused)
+{
+
+ quit("Broken pipe\n");
+}
+
+static void
+flushtape(void)
+{
+ int i, blks, got;
+ int64_t lastfirstrec;
+
+ int siz = (char *)nextblock - (char *)slp->req;
+
+ slp->req[trecno].count = 0; /* Sentinel */
+
+ if (atomic(write, slp->fd, (char *)slp->req, siz) != siz)
+ quit("error writing command pipe: %s\n", strerror(errno));
+ slp->sent = 1; /* we sent a request, read the response later */
+
+ lastfirstrec = slp->firstrec;
+
+ if (++slp >= &slaves[SLAVES])
+ slp = &slaves[0];
+
+ /* Read results back from next slave */
+ if (slp->sent) {
+ if (atomic(read, slp->fd, (char *)&got, sizeof got)
+ != sizeof got) {
+ perror(" DUMP: error reading command pipe in master");
+ dumpabort(0);
+ }
+ slp->sent = 0;
+
+ /* Check for end of tape */
+ if (got < writesize) {
+ msg("End of tape detected\n");
+
+ /*
+ * Drain the results, don't care what the values were.
+ * If we read them here then trewind won't...
+ */
+ for (i = 0; i < SLAVES; i++) {
+ if (slaves[i].sent) {
+ if (atomic(read, slaves[i].fd,
+ (char *)&got, sizeof got)
+ != sizeof got) {
+ perror(" DUMP: error reading command pipe in master");
+ dumpabort(0);
+ }
+ slaves[i].sent = 0;
+ }
+ }
+
+ close_rewind();
+ rollforward();
+ return;
+ }
+ }
+
+ blks = 0;
+ if (spcl.c_type != TS_END) {
+ for (i = 0; i < spcl.c_count; i++)
+ if (spcl.c_addr[i] != 0)
+ blks++;
+ }
+ slp->count = lastspclrec + blks + 1 - spcl.c_tapea;
+ slp->tapea = spcl.c_tapea;
+ slp->firstrec = lastfirstrec + ntrec;
+ slp->inode = curino;
+ nextblock = slp->tblock;
+ trecno = 0;
+ asize += tenths;
+ blockswritten += ntrec;
+ blocksthisvol += ntrec;
+ if (!pipeout && !unlimited && (blocksperfile ?
+ (blocksthisvol >= blocksperfile) : (asize > tsize))) {
+ close_rewind();
+ startnewtape(0);
+ }
+ timeest();
+}
+
+void
+trewind(void)
+{
+ struct stat sb;
+ int f;
+ int got;
+
+ for (f = 0; f < SLAVES; f++) {
+ /*
+ * Drain the results, but unlike EOT we DO (or should) care
+ * what the return values were, since if we detect EOT after
+ * we think we've written the last blocks to the tape anyway,
+ * we have to replay those blocks with rollforward.
+ *
+ * fixme: punt for now.
+ */
+ if (slaves[f].sent) {
+ if (atomic(read, slaves[f].fd, (char *)&got, sizeof got)
+ != sizeof got) {
+ perror(" DUMP: error reading command pipe in master");
+ dumpabort(0);
+ }
+ slaves[f].sent = 0;
+ if (got != writesize) {
+ msg("EOT detected in last 2 tape records!\n");
+ msg("Use a longer tape, decrease the size estimate\n");
+ quit("or use no size estimate at all.\n");
+ }
+ }
+ (void) close(slaves[f].fd);
+ }
+ while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */
+ /* void */;
+
+ if (pipeout)
+ return;
+
+ msg("Closing %s\n", tape);
+
+ if (popenout) {
+ tapefd = -1;
+ (void)pclose(popenfp);
+ popenfp = NULL;
+ return;
+ }
+#ifdef RDUMP
+ if (host) {
+ rmtclose();
+ while (rmtopen(tape, 0) < 0)
+ sleep(10);
+ rmtclose();
+ return;
+ }
+#endif
+ if (fstat(tapefd, &sb) == 0 && S_ISFIFO(sb.st_mode)) {
+ (void)close(tapefd);
+ return;
+ }
+ (void) close(tapefd);
+ while ((f = open(tape, 0)) < 0)
+ sleep (10);
+ (void) close(f);
+}
+
+void
+close_rewind()
+{
+ time_t tstart_changevol, tend_changevol;
+
+ trewind();
+ if (nexttape)
+ return;
+ (void)time((time_t *)&(tstart_changevol));
+ if (!nogripe) {
+ msg("Change Volumes: Mount volume #%d\n", tapeno+1);
+ broadcast("CHANGE DUMP VOLUMES!\a\a\n");
+ }
+ while (!query("Is the new volume mounted and ready to go?"))
+ if (query("Do you want to abort?")) {
+ dumpabort(0);
+ /*NOTREACHED*/
+ }
+ (void)time((time_t *)&(tend_changevol));
+ if ((tstart_changevol != (time_t)-1) && (tend_changevol != (time_t)-1))
+ tstart_writing += (tend_changevol - tstart_changevol);
+}
+
+void
+rollforward(void)
+{
+ struct req *p, *q, *prev;
+ struct slave *tslp;
+ int i, size, got;
+ int64_t savedtapea;
+ union u_spcl *ntb, *otb;
+ tslp = &slaves[SLAVES];
+ ntb = (union u_spcl *)tslp->tblock[1];
+
+ /*
+ * Each of the N slaves should have requests that need to
+ * be replayed on the next tape. Use the extra slave buffers
+ * (slaves[SLAVES]) to construct request lists to be sent to
+ * each slave in turn.
+ */
+ for (i = 0; i < SLAVES; i++) {
+ q = &tslp->req[1];
+ otb = (union u_spcl *)slp->tblock;
+
+ /*
+ * For each request in the current slave, copy it to tslp.
+ */
+
+ prev = NULL;
+ for (p = slp->req; p->count > 0; p += p->count) {
+ *q = *p;
+ if (p->dblk == 0)
+ *ntb++ = *otb++; /* copy the datablock also */
+ prev = q;
+ q += q->count;
+ }
+ if (prev == NULL)
+ quit("rollforward: protocol botch");
+ if (prev->dblk != 0)
+ prev->count -= 1;
+ else
+ ntb--;
+ q -= 1;
+ q->count = 0;
+ q = &tslp->req[0];
+ if (i == 0) {
+ q->dblk = 0;
+ q->count = 1;
+ trecno = 0;
+ nextblock = tslp->tblock;
+ savedtapea = spcl.c_tapea;
+ spcl.c_tapea = slp->tapea;
+ startnewtape(0);
+ spcl.c_tapea = savedtapea;
+ lastspclrec = savedtapea - 1;
+ }
+ size = (char *)ntb - (char *)q;
+ if (atomic(write, slp->fd, (char *)q, size) != size) {
+ perror(" DUMP: error writing command pipe");
+ dumpabort(0);
+ }
+ slp->sent = 1;
+ if (++slp >= &slaves[SLAVES])
+ slp = &slaves[0];
+
+ q->count = 1;
+
+ if (prev->dblk != 0) {
+ /*
+ * If the last one was a disk block, make the
+ * first of this one be the last bit of that disk
+ * block...
+ */
+ q->dblk = prev->dblk +
+ prev->count * (TP_BSIZE / DEV_BSIZE);
+ ntb = (union u_spcl *)tslp->tblock;
+ } else {
+ /*
+ * It wasn't a disk block. Copy the data to its
+ * new location in the buffer.
+ */
+ q->dblk = 0;
+ *((union u_spcl *)tslp->tblock) = *ntb;
+ ntb = (union u_spcl *)tslp->tblock[1];
+ }
+ }
+ slp->req[0] = *q;
+ nextblock = slp->tblock;
+ if (q->dblk == 0)
+ nextblock++;
+ trecno = 1;
+
+ /*
+ * Clear the first slaves' response. One hopes that it
+ * worked ok, otherwise the tape is much too short!
+ */
+ if (slp->sent) {
+ if (atomic(read, slp->fd, (char *)&got, sizeof got)
+ != sizeof got) {
+ perror(" DUMP: error reading command pipe in master");
+ dumpabort(0);
+ }
+ slp->sent = 0;
+
+ if (got != writesize) {
+ quit("EOT detected at start of the tape!\n");
+ }
+ }
+}
+
+/*
+ * We implement taking and restoring checkpoints on the tape level.
+ * When each tape is opened, a new process is created by forking; this
+ * saves all of the necessary context in the parent. The child
+ * continues the dump; the parent waits around, saving the context.
+ * If the child returns X_REWRITE, then it had problems writing that tape;
+ * this causes the parent to fork again, duplicating the context, and
+ * everything continues as if nothing had happened.
+ */
+void
+startnewtape(int top)
+{
+ int parentpid;
+ int childpid;
+ int status;
+ char *p;
+ sig_t interrupt_save;
+
+ interrupt_save = signal(SIGINT, SIG_IGN);
+ parentpid = getpid();
+
+restore_check_point:
+ (void)signal(SIGINT, interrupt_save);
+ /*
+ * All signals are inherited...
+ */
+ setproctitle(NULL); /* Restore the proctitle. */
+ childpid = fork();
+ if (childpid < 0) {
+ msg("Context save fork fails in parent %d\n", parentpid);
+ Exit(X_ABORT);
+ }
+ if (childpid != 0) {
+ /*
+ * PARENT:
+ * save the context by waiting
+ * until the child doing all of the work returns.
+ * don't catch the interrupt
+ */
+ signal(SIGINT, SIG_IGN);
+#ifdef TDEBUG
+ msg("Tape: %d; parent process: %d child process %d\n",
+ tapeno+1, parentpid, childpid);
+#endif /* TDEBUG */
+ if (waitpid(childpid, &status, 0) == -1)
+ msg("Waiting for child %d: %s\n", childpid,
+ strerror(errno));
+ if (status & 0xFF) {
+ msg("Child %d returns LOB status %o\n",
+ childpid, status&0xFF);
+ }
+ status = (status >> 8) & 0xFF;
+#ifdef TDEBUG
+ switch(status) {
+ case X_FINOK:
+ msg("Child %d finishes X_FINOK\n", childpid);
+ break;
+ case X_ABORT:
+ msg("Child %d finishes X_ABORT\n", childpid);
+ break;
+ case X_REWRITE:
+ msg("Child %d finishes X_REWRITE\n", childpid);
+ break;
+ default:
+ msg("Child %d finishes unknown %d\n",
+ childpid, status);
+ break;
+ }
+#endif /* TDEBUG */
+ switch(status) {
+ case X_FINOK:
+ Exit(X_FINOK);
+ case X_ABORT:
+ Exit(X_ABORT);
+ case X_REWRITE:
+ goto restore_check_point;
+ default:
+ msg("Bad return code from dump: %d\n", status);
+ Exit(X_ABORT);
+ }
+ /*NOTREACHED*/
+ } else { /* we are the child; just continue */
+#ifdef TDEBUG
+ sleep(4); /* allow time for parent's message to get out */
+ msg("Child on Tape %d has parent %d, my pid = %d\n",
+ tapeno+1, parentpid, getpid());
+#endif /* TDEBUG */
+ /*
+ * If we have a name like "/dev/rmt0,/dev/rmt1",
+ * use the name before the comma first, and save
+ * the remaining names for subsequent volumes.
+ */
+ tapeno++; /* current tape sequence */
+ if (nexttape || strchr(tape, ',')) {
+ if (nexttape && *nexttape)
+ tape = nexttape;
+ if ((p = strchr(tape, ',')) != NULL) {
+ *p = '\0';
+ nexttape = p + 1;
+ } else
+ nexttape = NULL;
+ msg("Dumping volume %d on %s\n", tapeno, tape);
+ }
+ if (pipeout) {
+ tapefd = STDOUT_FILENO;
+ } else if (popenout) {
+ char volno[sizeof("2147483647")];
+
+ (void)sprintf(volno, "%d", spcl.c_volume + 1);
+ if (setenv("DUMP_VOLUME", volno, 1) == -1) {
+ msg("Cannot set $DUMP_VOLUME.\n");
+ dumpabort(0);
+ }
+ popenfp = popen(popenout, "w");
+ if (popenfp == NULL) {
+ msg("Cannot open output pipeline \"%s\".\n",
+ popenout);
+ dumpabort(0);
+ }
+ tapefd = fileno(popenfp);
+ } else {
+#ifdef RDUMP
+ while ((tapefd = (host ? rmtopen(tape, 2) :
+ open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
+#else
+ while ((tapefd =
+ open(tape, O_WRONLY|O_CREAT, 0666)) < 0)
+#endif
+ {
+ msg("Cannot open output \"%s\".\n", tape);
+ if (!query("Do you want to retry the open?"))
+ dumpabort(0);
+ }
+ }
+
+ enslave(); /* Share open tape file descriptor with slaves */
+ if (popenout)
+ close(tapefd); /* Give up our copy of it. */
+ signal(SIGINFO, infosch);
+
+ asize = 0;
+ blocksthisvol = 0;
+ if (top)
+ newtape++; /* new tape signal */
+ spcl.c_count = slp->count;
+ /*
+ * measure firstrec in TP_BSIZE units since restore doesn't
+ * know the correct ntrec value...
+ */
+ spcl.c_firstrec = slp->firstrec;
+ spcl.c_volume++;
+ spcl.c_type = TS_TAPE;
+ writeheader((ino_t)slp->inode);
+ if (tapeno > 1)
+ msg("Volume %d begins with blocks from inode %d\n",
+ tapeno, slp->inode);
+ }
+}
+
+void
+dumpabort(int signo __unused)
+{
+
+ if (master != 0 && master != getpid())
+ /* Signals master to call dumpabort */
+ (void) kill(master, SIGTERM);
+ else {
+ killall();
+ msg("The ENTIRE dump is aborted.\n");
+ }
+#ifdef RDUMP
+ rmtclose();
+#endif
+ Exit(X_ABORT);
+}
+
+void
+Exit(status)
+ int status;
+{
+
+#ifdef TDEBUG
+ msg("pid = %d exits with status %d\n", getpid(), status);
+#endif /* TDEBUG */
+ exit(status);
+}
+
+/*
+ * proceed - handler for SIGUSR2, used to synchronize IO between the slaves.
+ */
+void
+proceed(int signo __unused)
+{
+
+ if (ready)
+ longjmp(jmpbuf, 1);
+ caught++;
+}
+
+void
+enslave(void)
+{
+ int cmd[2];
+ int i, j;
+
+ master = getpid();
+
+ signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */
+ signal(SIGPIPE, sigpipe);
+ signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */
+ signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */
+
+ for (i = 0; i < SLAVES; i++) {
+ if (i == slp - &slaves[0]) {
+ caught = 1;
+ } else {
+ caught = 0;
+ }
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 ||
+ (slaves[i].pid = fork()) < 0)
+ quit("too many slaves, %d (recompile smaller): %s\n",
+ i, strerror(errno));
+
+ slaves[i].fd = cmd[1];
+ slaves[i].sent = 0;
+ if (slaves[i].pid == 0) { /* Slave starts up here */
+ for (j = 0; j <= i; j++)
+ (void) close(slaves[j].fd);
+ signal(SIGINT, SIG_IGN); /* Master handles this */
+ doslave(cmd[0], i);
+ Exit(X_FINOK);
+ }
+ }
+
+ for (i = 0; i < SLAVES; i++)
+ (void) atomic(write, slaves[i].fd,
+ (char *) &slaves[(i + 1) % SLAVES].pid,
+ sizeof slaves[0].pid);
+
+ master = 0;
+}
+
+void
+killall(void)
+{
+ int i;
+
+ for (i = 0; i < SLAVES; i++)
+ if (slaves[i].pid > 0) {
+ (void) kill(slaves[i].pid, SIGKILL);
+ slaves[i].sent = 0;
+ }
+}
+
+/*
+ * Synchronization - each process has a lockfile, and shares file
+ * descriptors to the following process's lockfile. When our write
+ * completes, we release our lock on the following process's lock-
+ * file, allowing the following process to lock it and proceed. We
+ * get the lock back for the next cycle by swapping descriptors.
+ */
+static void
+doslave(int cmd, int slave_number)
+{
+ int nread;
+ int nextslave, size, wrote, eot_count;
+
+ /*
+ * Need our own seek pointer.
+ */
+ (void) close(diskfd);
+ if ((diskfd = open(disk, O_RDONLY)) < 0)
+ quit("slave couldn't reopen disk: %s\n", strerror(errno));
+
+ /*
+ * Need the pid of the next slave in the loop...
+ */
+ if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof nextslave))
+ != sizeof nextslave) {
+ quit("master/slave protocol botched - didn't get pid of next slave.\n");
+ }
+
+ /*
+ * Get list of blocks to dump, read the blocks into tape buffer
+ */
+ while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) {
+ struct req *p = slp->req;
+
+ for (trecno = 0; trecno < ntrec;
+ trecno += p->count, p += p->count) {
+ if (p->dblk) {
+ bread(p->dblk, slp->tblock[trecno],
+ p->count * TP_BSIZE);
+ } else {
+ if (p->count != 1 || atomic(read, cmd,
+ (char *)slp->tblock[trecno],
+ TP_BSIZE) != TP_BSIZE)
+ quit("master/slave protocol botched.\n");
+ }
+ }
+ if (setjmp(jmpbuf) == 0) {
+ ready = 1;
+ if (!caught)
+ (void) pause();
+ }
+ ready = 0;
+ caught = 0;
+
+ /* Try to write the data... */
+ eot_count = 0;
+ size = 0;
+
+ wrote = 0;
+ while (eot_count < 10 && size < writesize) {
+#ifdef RDUMP
+ if (host)
+ wrote = rmtwrite(slp->tblock[0]+size,
+ writesize-size);
+ else
+#endif
+ wrote = write(tapefd, slp->tblock[0]+size,
+ writesize-size);
+#ifdef WRITEDEBUG
+ printf("slave %d wrote %d\n", slave_number, wrote);
+#endif
+ if (wrote < 0)
+ break;
+ if (wrote == 0)
+ eot_count++;
+ size += wrote;
+ }
+
+#ifdef WRITEDEBUG
+ if (size != writesize)
+ printf("slave %d only wrote %d out of %d bytes and gave up.\n",
+ slave_number, size, writesize);
+#endif
+
+ /*
+ * Handle ENOSPC as an EOT condition.
+ */
+ if (wrote < 0 && errno == ENOSPC) {
+ wrote = 0;
+ eot_count++;
+ }
+
+ if (eot_count > 0)
+ size = 0;
+
+ if (wrote < 0) {
+ (void) kill(master, SIGUSR1);
+ for (;;)
+ (void) sigpause(0);
+ } else {
+ /*
+ * pass size of write back to master
+ * (for EOT handling)
+ */
+ (void) atomic(write, cmd, (char *)&size, sizeof size);
+ }
+
+ /*
+ * If partial write, don't want next slave to go.
+ * Also jolts him awake.
+ */
+ (void) kill(nextslave, SIGUSR2);
+ }
+ if (nread != 0)
+ quit("error reading command pipe: %s\n", strerror(errno));
+}
+
+/*
+ * Since a read from a pipe may not return all we asked for,
+ * or a write may not write all we ask if we get a signal,
+ * loop until the count is satisfied (or error).
+ */
+static int
+atomic(ssize_t (*func)(), int fd, char *buf, int count)
+{
+ int got, need = count;
+
+ while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
+ buf += got;
+ return (got < 0 ? got : count - need);
+}
diff --git a/sbin/dump/traverse.c b/sbin/dump/traverse.c
new file mode 100644
index 0000000..878f400
--- /dev/null
+++ b/sbin/dump/traverse.c
@@ -0,0 +1,838 @@
+/*-
+ * Copyright (c) 1980, 1988, 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.
+ * 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 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 lint
+#if 0
+static char sccsid[] = "@(#)traverse.c 8.7 (Berkeley) 6/15/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <protocols/dumprestore.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <timeconv.h>
+#include <unistd.h>
+
+#include "dump.h"
+
+union dinode {
+ struct ufs1_dinode dp1;
+ struct ufs2_dinode dp2;
+};
+#define DIP(dp, field) \
+ ((sblock->fs_magic == FS_UFS1_MAGIC) ? \
+ (dp)->dp1.field : (dp)->dp2.field)
+#define DIP_SET(dp, field, val) do {\
+ if (sblock->fs_magic == FS_UFS1_MAGIC) \
+ (dp)->dp1.field = (val); \
+ else \
+ (dp)->dp2.field = (val); \
+ } while (0)
+
+#define HASDUMPEDFILE 0x1
+#define HASSUBDIRS 0x2
+
+static int dirindir(ino_t ino, ufs2_daddr_t blkno, int level, long *size,
+ long *tapesize, int nodump);
+static void dmpindir(ino_t ino, ufs2_daddr_t blk, int level, off_t *size);
+static int searchdir(ino_t ino, ufs2_daddr_t blkno, long size, long filesize,
+ long *tapesize, int nodump);
+static long blockest(union dinode *dp);
+
+/*
+ * This is an estimation of the number of TP_BSIZE blocks in the file.
+ * It estimates the number of blocks in files with holes by assuming
+ * that all of the blocks accounted for by di_blocks are data blocks
+ * (when some of the blocks are usually used for indirect pointers);
+ * hence the estimate may be high.
+ */
+static long
+blockest(union dinode *dp)
+{
+ long blkest, sizeest;
+
+ /*
+ * dp->di_size is the size of the file in bytes.
+ * dp->di_blocks stores the number of sectors actually in the file.
+ * If there are more sectors than the size would indicate, this just
+ * means that there are indirect blocks in the file or unused
+ * sectors in the last file block; we can safely ignore these
+ * (blkest = sizeest below).
+ * If the file is bigger than the number of sectors would indicate,
+ * then the file has holes in it. In this case we must use the
+ * block count to estimate the number of data blocks used, but
+ * we use the actual size for estimating the number of indirect
+ * dump blocks (sizeest vs. blkest in the indirect block
+ * calculation).
+ */
+ if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0)
+ return (1);
+ blkest = howmany(dbtob(DIP(dp, di_blocks)), TP_BSIZE);
+ sizeest = howmany(DIP(dp, di_size), TP_BSIZE);
+ if (blkest > sizeest)
+ blkest = sizeest;
+ if (DIP(dp, di_size) > sblock->fs_bsize * NDADDR) {
+ /* calculate the number of indirect blocks on the dump tape */
+ blkest +=
+ howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE,
+ TP_NINDIR);
+ }
+ return (blkest + 1);
+}
+
+/* Auxiliary macro to pick up files changed since previous dump. */
+#define CHANGEDSINCE(dp, t) \
+ (DIP(dp, di_mtime) >= (t) || DIP(dp, di_ctime) >= (t))
+
+/* The WANTTODUMP macro decides whether a file should be dumped. */
+#ifdef UF_NODUMP
+#define WANTTODUMP(dp) \
+ (CHANGEDSINCE(dp, spcl.c_ddate) && \
+ (nonodump || (DIP(dp, di_flags) & UF_NODUMP) != UF_NODUMP))
+#else
+#define WANTTODUMP(dp) CHANGEDSINCE(dp, spcl.c_ddate)
+#endif
+
+/*
+ * Dump pass 1.
+ *
+ * Walk the inode list for a file system to find all allocated inodes
+ * that have been modified since the previous dump time. Also, find all
+ * the directories in the file system.
+ */
+int
+mapfiles(ino_t maxino, long *tapesize)
+{
+ int i, cg, mode, inosused;
+ int anydirskipped = 0;
+ union dinode *dp;
+ struct cg *cgp;
+ ino_t ino;
+ u_char *cp;
+
+ if ((cgp = malloc(sblock->fs_cgsize)) == NULL)
+ quit("mapfiles: cannot allocate memory.\n");
+ for (cg = 0; cg < sblock->fs_ncg; cg++) {
+ ino = cg * sblock->fs_ipg;
+ bread(fsbtodb(sblock, cgtod(sblock, cg)), (char *)cgp,
+ sblock->fs_cgsize);
+ if (sblock->fs_magic == FS_UFS2_MAGIC)
+ inosused = cgp->cg_initediblk;
+ else
+ inosused = sblock->fs_ipg;
+ /*
+ * If we are using soft updates, then we can trust the
+ * cylinder group inode allocation maps to tell us which
+ * inodes are allocated. We will scan the used inode map
+ * to find the inodes that are really in use, and then
+ * read only those inodes in from disk.
+ */
+ if (sblock->fs_flags & FS_DOSOFTDEP) {
+ if (!cg_chkmagic(cgp))
+ quit("mapfiles: cg %d: bad magic number\n", cg);
+ cp = &cg_inosused(cgp)[(inosused - 1) / CHAR_BIT];
+ for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) {
+ if (*cp == 0)
+ continue;
+ for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) {
+ if (*cp & i)
+ break;
+ inosused--;
+ }
+ break;
+ }
+ if (inosused <= 0)
+ continue;
+ }
+ for (i = 0; i < inosused; i++, ino++) {
+ if (ino < ROOTINO ||
+ (dp = getino(ino, &mode)) == NULL ||
+ (mode & IFMT) == 0)
+ continue;
+ /*
+ * Everything must go in usedinomap so that a check
+ * for "in dumpdirmap but not in usedinomap" to detect
+ * dirs with nodump set has a chance of succeeding
+ * (this is used in mapdirs()).
+ */
+ SETINO(ino, usedinomap);
+ if (mode == IFDIR)
+ SETINO(ino, dumpdirmap);
+ if (WANTTODUMP(dp)) {
+ SETINO(ino, dumpinomap);
+ if (mode != IFREG &&
+ mode != IFDIR &&
+ mode != IFLNK)
+ *tapesize += 1;
+ else
+ *tapesize += blockest(dp);
+ continue;
+ }
+ if (mode == IFDIR) {
+ if (!nonodump &&
+ (DIP(dp, di_flags) & UF_NODUMP))
+ CLRINO(ino, usedinomap);
+ anydirskipped = 1;
+ }
+ }
+ }
+ /*
+ * Restore gets very upset if the root is not dumped,
+ * so ensure that it always is dumped.
+ */
+ SETINO(ROOTINO, dumpinomap);
+ return (anydirskipped);
+}
+
+/*
+ * Dump pass 2.
+ *
+ * Scan each directory on the file system to see if it has any modified
+ * files in it. If it does, and has not already been added to the dump
+ * list (because it was itself modified), then add it. If a directory
+ * has not been modified itself, contains no modified files and has no
+ * subdirectories, then it can be deleted from the dump list and from
+ * the list of directories. By deleting it from the list of directories,
+ * its parent may now qualify for the same treatment on this or a later
+ * pass using this algorithm.
+ */
+int
+mapdirs(ino_t maxino, long *tapesize)
+{
+ union dinode *dp;
+ int i, isdir, nodump;
+ char *map;
+ ino_t ino;
+ union dinode di;
+ long filesize;
+ int ret, change = 0;
+
+ isdir = 0; /* XXX just to get gcc to shut up */
+ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
+ if (((ino - 1) % CHAR_BIT) == 0) /* map is offset by 1 */
+ isdir = *map++;
+ else
+ isdir >>= 1;
+ /*
+ * If a directory has been removed from usedinomap, it
+ * either has the nodump flag set, or has inherited
+ * it. Although a directory can't be in dumpinomap if
+ * it isn't in usedinomap, we have to go through it to
+ * propagate the nodump flag.
+ */
+ nodump = !nonodump && (TSTINO(ino, usedinomap) == 0);
+ if ((isdir & 1) == 0 || (TSTINO(ino, dumpinomap) && !nodump))
+ continue;
+ dp = getino(ino, &i);
+ /*
+ * inode buf may change in searchdir().
+ */
+ if (sblock->fs_magic == FS_UFS1_MAGIC)
+ di.dp1 = dp->dp1;
+ else
+ di.dp2 = dp->dp2;
+ filesize = DIP(&di, di_size);
+ for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) {
+ if (DIP(&di, di_db[i]) != 0)
+ ret |= searchdir(ino, DIP(&di, di_db[i]),
+ (long)sblksize(sblock, DIP(&di, di_size),
+ i), filesize, tapesize, nodump);
+ if (ret & HASDUMPEDFILE)
+ filesize = 0;
+ else
+ filesize -= sblock->fs_bsize;
+ }
+ for (i = 0; filesize > 0 && i < NIADDR; i++) {
+ if (DIP(&di, di_ib[i]) == 0)
+ continue;
+ ret |= dirindir(ino, DIP(&di, di_ib[i]), i, &filesize,
+ tapesize, nodump);
+ }
+ if (ret & HASDUMPEDFILE) {
+ SETINO(ino, dumpinomap);
+ *tapesize += blockest(&di);
+ change = 1;
+ continue;
+ }
+ if (nodump) {
+ if (ret & HASSUBDIRS)
+ change = 1; /* subdirs inherit nodump */
+ CLRINO(ino, dumpdirmap);
+ } else if ((ret & HASSUBDIRS) == 0)
+ if (!TSTINO(ino, dumpinomap)) {
+ CLRINO(ino, dumpdirmap);
+ change = 1;
+ }
+ }
+ return (change);
+}
+
+/*
+ * Read indirect blocks, and pass the data blocks to be searched
+ * as directories. Quit as soon as any entry is found that will
+ * require the directory to be dumped.
+ */
+static int
+dirindir(
+ ino_t ino,
+ ufs2_daddr_t blkno,
+ int ind_level,
+ long *filesize,
+ long *tapesize,
+ int nodump)
+{
+ union {
+ ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)];
+ ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)];
+ } idblk;
+ int ret = 0;
+ int i;
+
+ bread(fsbtodb(sblock, blkno), (char *)&idblk, (int)sblock->fs_bsize);
+ if (ind_level <= 0) {
+ for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
+ if (sblock->fs_magic == FS_UFS1_MAGIC)
+ blkno = idblk.ufs1[i];
+ else
+ blkno = idblk.ufs2[i];
+ if (blkno != 0)
+ ret |= searchdir(ino, blkno, sblock->fs_bsize,
+ *filesize, tapesize, nodump);
+ if (ret & HASDUMPEDFILE)
+ *filesize = 0;
+ else
+ *filesize -= sblock->fs_bsize;
+ }
+ return (ret);
+ }
+ ind_level--;
+ for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
+ if (sblock->fs_magic == FS_UFS1_MAGIC)
+ blkno = idblk.ufs1[i];
+ else
+ blkno = idblk.ufs2[i];
+ if (blkno != 0)
+ ret |= dirindir(ino, blkno, ind_level, filesize,
+ tapesize, nodump);
+ }
+ return (ret);
+}
+
+/*
+ * Scan a disk block containing directory information looking to see if
+ * any of the entries are on the dump list and to see if the directory
+ * contains any subdirectories.
+ */
+static int
+searchdir(
+ ino_t ino,
+ ufs2_daddr_t blkno,
+ long size,
+ long filesize,
+ long *tapesize,
+ int nodump)
+{
+ int mode;
+ struct direct *dp;
+ union dinode *ip;
+ long loc, ret = 0;
+ static caddr_t dblk;
+
+ if (dblk == NULL && (dblk = malloc(sblock->fs_bsize)) == NULL)
+ quit("searchdir: cannot allocate indirect memory.\n");
+ bread(fsbtodb(sblock, blkno), dblk, (int)size);
+ if (filesize < size)
+ size = filesize;
+ for (loc = 0; loc < size; ) {
+ dp = (struct direct *)(dblk + loc);
+ if (dp->d_reclen == 0) {
+ msg("corrupted directory, inumber %d\n", ino);
+ break;
+ }
+ loc += dp->d_reclen;
+ if (dp->d_ino == 0)
+ continue;
+ if (dp->d_name[0] == '.') {
+ if (dp->d_name[1] == '\0')
+ continue;
+ if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
+ continue;
+ }
+ if (nodump) {
+ ip = getino(dp->d_ino, &mode);
+ if (TSTINO(dp->d_ino, dumpinomap)) {
+ CLRINO(dp->d_ino, dumpinomap);
+ *tapesize -= blockest(ip);
+ }
+ /*
+ * Add back to dumpdirmap and remove from usedinomap
+ * to propagate nodump.
+ */
+ if (mode == IFDIR) {
+ SETINO(dp->d_ino, dumpdirmap);
+ CLRINO(dp->d_ino, usedinomap);
+ ret |= HASSUBDIRS;
+ }
+ } else {
+ if (TSTINO(dp->d_ino, dumpinomap)) {
+ ret |= HASDUMPEDFILE;
+ if (ret & HASSUBDIRS)
+ break;
+ }
+ if (TSTINO(dp->d_ino, dumpdirmap)) {
+ ret |= HASSUBDIRS;
+ if (ret & HASDUMPEDFILE)
+ break;
+ }
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Dump passes 3 and 4.
+ *
+ * Dump the contents of an inode to tape.
+ */
+void
+dumpino(union dinode *dp, ino_t ino)
+{
+ int ind_level, cnt;
+ off_t size;
+ char buf[TP_BSIZE];
+
+ if (newtape) {
+ newtape = 0;
+ dumpmap(dumpinomap, TS_BITS, ino);
+ }
+ CLRINO(ino, dumpinomap);
+ /*
+ * Zero out the size of a snapshot so that it will be dumped
+ * as a zero length file.
+ */
+ if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0) {
+ DIP_SET(dp, di_size, 0);
+ DIP_SET(dp, di_flags, DIP(dp, di_flags) & ~SF_SNAPSHOT);
+ }
+ if (sblock->fs_magic == FS_UFS1_MAGIC) {
+ spcl.c_mode = dp->dp1.di_mode;
+ spcl.c_size = dp->dp1.di_size;
+ spcl.c_atime = _time32_to_time(dp->dp1.di_atime);
+ spcl.c_atimensec = dp->dp1.di_atimensec;
+ spcl.c_mtime = _time32_to_time(dp->dp1.di_mtime);
+ spcl.c_mtimensec = dp->dp1.di_mtimensec;
+ spcl.c_birthtime = 0;
+ spcl.c_birthtimensec = 0;
+ spcl.c_rdev = dp->dp1.di_rdev;
+ spcl.c_file_flags = dp->dp1.di_flags;
+ spcl.c_uid = dp->dp1.di_uid;
+ spcl.c_gid = dp->dp1.di_gid;
+ } else {
+ spcl.c_mode = dp->dp2.di_mode;
+ spcl.c_size = dp->dp2.di_size;
+ spcl.c_atime = _time64_to_time(dp->dp2.di_atime);
+ spcl.c_atimensec = dp->dp2.di_atimensec;
+ spcl.c_mtime = _time64_to_time(dp->dp2.di_mtime);
+ spcl.c_mtimensec = dp->dp2.di_mtimensec;
+ spcl.c_birthtime = _time64_to_time(dp->dp2.di_birthtime);
+ spcl.c_birthtimensec = dp->dp2.di_birthnsec;
+ spcl.c_rdev = dp->dp2.di_rdev;
+ spcl.c_file_flags = dp->dp2.di_flags;
+ spcl.c_uid = dp->dp2.di_uid;
+ spcl.c_gid = dp->dp2.di_gid;
+ }
+ spcl.c_type = TS_INODE;
+ spcl.c_count = 0;
+ switch (DIP(dp, di_mode) & S_IFMT) {
+
+ case 0:
+ /*
+ * Freed inode.
+ */
+ return;
+
+ case S_IFLNK:
+ /*
+ * Check for short symbolic link.
+ */
+ if (DIP(dp, di_size) > 0 &&
+ DIP(dp, di_size) < sblock->fs_maxsymlinklen) {
+ spcl.c_addr[0] = 1;
+ spcl.c_count = 1;
+ writeheader(ino);
+ if (sblock->fs_magic == FS_UFS1_MAGIC)
+ memmove(buf, (caddr_t)dp->dp1.di_db,
+ (u_long)DIP(dp, di_size));
+ else
+ memmove(buf, (caddr_t)dp->dp2.di_db,
+ (u_long)DIP(dp, di_size));
+ buf[DIP(dp, di_size)] = '\0';
+ writerec(buf, 0);
+ return;
+ }
+ /* FALLTHROUGH */
+
+ case S_IFDIR:
+ case S_IFREG:
+ if (DIP(dp, di_size) > 0)
+ break;
+ /* FALLTHROUGH */
+
+ case S_IFIFO:
+ case S_IFSOCK:
+ case S_IFCHR:
+ case S_IFBLK:
+ writeheader(ino);
+ return;
+
+ default:
+ msg("Warning: undefined file type 0%o\n",
+ DIP(dp, di_mode) & IFMT);
+ return;
+ }
+ if (DIP(dp, di_size) > NDADDR * sblock->fs_bsize)
+ cnt = NDADDR * sblock->fs_frag;
+ else
+ cnt = howmany(DIP(dp, di_size), sblock->fs_fsize);
+ if (sblock->fs_magic == FS_UFS1_MAGIC)
+ ufs1_blksout(&dp->dp1.di_db[0], cnt, ino);
+ else
+ ufs2_blksout(&dp->dp2.di_db[0], cnt, ino);
+ if ((size = DIP(dp, di_size) - NDADDR * sblock->fs_bsize) <= 0)
+ return;
+ for (ind_level = 0; ind_level < NIADDR; ind_level++) {
+ dmpindir(ino, DIP(dp, di_ib[ind_level]), ind_level, &size);
+ if (size <= 0)
+ return;
+ }
+}
+
+/*
+ * Read indirect blocks, and pass the data blocks to be dumped.
+ */
+static void
+dmpindir(ino_t ino, ufs2_daddr_t blk, int ind_level, off_t *size)
+{
+ union {
+ ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)];
+ ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)];
+ } idblk;
+ int i, cnt;
+
+ if (blk != 0)
+ bread(fsbtodb(sblock, blk), (char *)&idblk,
+ (int)sblock->fs_bsize);
+ else
+ memset(&idblk, 0, sblock->fs_bsize);
+ if (ind_level <= 0) {
+ if (*size < NINDIR(sblock) * sblock->fs_bsize)
+ cnt = howmany(*size, sblock->fs_fsize);
+ else
+ cnt = NINDIR(sblock) * sblock->fs_frag;
+ *size -= NINDIR(sblock) * sblock->fs_bsize;
+ if (sblock->fs_magic == FS_UFS1_MAGIC)
+ ufs1_blksout(idblk.ufs1, cnt, ino);
+ else
+ ufs2_blksout(idblk.ufs2, cnt, ino);
+ return;
+ }
+ ind_level--;
+ for (i = 0; i < NINDIR(sblock); i++) {
+ if (sblock->fs_magic == FS_UFS1_MAGIC)
+ dmpindir(ino, idblk.ufs1[i], ind_level, size);
+ else
+ dmpindir(ino, idblk.ufs2[i], ind_level, size);
+ if (*size <= 0)
+ return;
+ }
+}
+
+/*
+ * Collect up the data into tape record sized buffers and output them.
+ */
+void
+ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino)
+{
+ ufs1_daddr_t *bp;
+ int i, j, count, blks, tbperdb;
+
+ blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
+ tbperdb = sblock->fs_bsize >> tp_bshift;
+ for (i = 0; i < blks; i += TP_NINDIR) {
+ if (i + TP_NINDIR > blks)
+ count = blks;
+ else
+ count = i + TP_NINDIR;
+ for (j = i; j < count; j++)
+ if (blkp[j / tbperdb] != 0)
+ spcl.c_addr[j - i] = 1;
+ else
+ spcl.c_addr[j - i] = 0;
+ spcl.c_count = count - i;
+ writeheader(ino);
+ bp = &blkp[i / tbperdb];
+ for (j = i; j < count; j += tbperdb, bp++)
+ if (*bp != 0) {
+ if (j + tbperdb <= count)
+ dumpblock(*bp, (int)sblock->fs_bsize);
+ else
+ dumpblock(*bp, (count - j) * TP_BSIZE);
+ }
+ spcl.c_type = TS_ADDR;
+ }
+}
+
+/*
+ * Collect up the data into tape record sized buffers and output them.
+ */
+void
+ufs2_blksout(ufs2_daddr_t *blkp, int frags, ino_t ino)
+{
+ ufs2_daddr_t *bp;
+ int i, j, count, blks, tbperdb;
+
+ blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
+ tbperdb = sblock->fs_bsize >> tp_bshift;
+ for (i = 0; i < blks; i += TP_NINDIR) {
+ if (i + TP_NINDIR > blks)
+ count = blks;
+ else
+ count = i + TP_NINDIR;
+ for (j = i; j < count; j++)
+ if (blkp[j / tbperdb] != 0)
+ spcl.c_addr[j - i] = 1;
+ else
+ spcl.c_addr[j - i] = 0;
+ spcl.c_count = count - i;
+ writeheader(ino);
+ bp = &blkp[i / tbperdb];
+ for (j = i; j < count; j += tbperdb, bp++)
+ if (*bp != 0) {
+ if (j + tbperdb <= count)
+ dumpblock(*bp, (int)sblock->fs_bsize);
+ else
+ dumpblock(*bp, (count - j) * TP_BSIZE);
+ }
+ spcl.c_type = TS_ADDR;
+ }
+}
+
+/*
+ * Dump a map to the tape.
+ */
+void
+dumpmap(char *map, int type, ino_t ino)
+{
+ int i;
+ char *cp;
+
+ spcl.c_type = type;
+ spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE);
+ writeheader(ino);
+ for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE)
+ writerec(cp, 0);
+}
+
+/*
+ * Write a header record to the dump tape.
+ */
+void
+writeheader(ino_t ino)
+{
+ int32_t sum, cnt, *lp;
+
+ spcl.c_inumber = ino;
+ spcl.c_magic = FS_UFS2_MAGIC;
+ spcl.c_checksum = 0;
+ lp = (int32_t *)&spcl;
+ sum = 0;
+ cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t));
+ while (--cnt >= 0) {
+ sum += *lp++;
+ sum += *lp++;
+ sum += *lp++;
+ sum += *lp++;
+ }
+ spcl.c_checksum = CHECKSUM - sum;
+ writerec((char *)&spcl, 1);
+}
+
+union dinode *
+getino(ino_t inum, int *modep)
+{
+ static ino_t minino, maxino;
+ static caddr_t inoblock;
+ struct ufs1_dinode *dp1;
+ struct ufs2_dinode *dp2;
+
+ if (inoblock == NULL && (inoblock = malloc(sblock->fs_bsize)) == NULL)
+ quit("cannot allocate inode memory.\n");
+ curino = inum;
+ if (inum >= minino && inum < maxino)
+ goto gotit;
+ bread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), inoblock,
+ (int)sblock->fs_bsize);
+ minino = inum - (inum % INOPB(sblock));
+ maxino = minino + INOPB(sblock);
+gotit:
+ if (sblock->fs_magic == FS_UFS1_MAGIC) {
+ dp1 = &((struct ufs1_dinode *)inoblock)[inum - minino];
+ *modep = (dp1->di_mode & IFMT);
+ return ((union dinode *)dp1);
+ }
+ dp2 = &((struct ufs2_dinode *)inoblock)[inum - minino];
+ *modep = (dp2->di_mode & IFMT);
+ return ((union dinode *)dp2);
+}
+
+/*
+ * Read a chunk of data from the disk.
+ * Try to recover from hard errors by reading in sector sized pieces.
+ * Error recovery is attempted at most BREADEMAX times before seeking
+ * consent from the operator to continue.
+ */
+int breaderrors = 0;
+#define BREADEMAX 32
+
+void
+bread(ufs2_daddr_t blkno, char *buf, int size)
+{
+ int secsize, bytes, resid, xfer, base, cnt, i;
+ static char *tmpbuf;
+ off_t offset;
+
+loop:
+ offset = blkno << dev_bshift;
+ secsize = sblock->fs_fsize;
+ base = offset % secsize;
+ resid = size % secsize;
+ /*
+ * If the transfer request starts or ends on a non-sector
+ * boundary, we must read the entire sector and copy out
+ * just the part that we need.
+ */
+ if (base == 0 && resid == 0) {
+ cnt = cread(diskfd, buf, size, offset);
+ if (cnt == size)
+ return;
+ } else {
+ if (tmpbuf == NULL && (tmpbuf = malloc(secsize)) == 0)
+ quit("buffer malloc failed\n");
+ xfer = 0;
+ bytes = size;
+ if (base != 0) {
+ cnt = cread(diskfd, tmpbuf, secsize, offset - base);
+ if (cnt != secsize)
+ goto bad;
+ xfer = secsize - base;
+ offset += xfer;
+ bytes -= xfer;
+ resid = bytes % secsize;
+ memcpy(buf, &tmpbuf[base], xfer);
+ }
+ if (bytes >= secsize) {
+ cnt = cread(diskfd, &buf[xfer], bytes - resid, offset);
+ if (cnt != bytes - resid)
+ goto bad;
+ xfer += cnt;
+ offset += cnt;
+ }
+ if (resid == 0)
+ return;
+ cnt = cread(diskfd, tmpbuf, secsize, offset);
+ if (cnt == secsize) {
+ memcpy(&buf[xfer], tmpbuf, resid);
+ return;
+ }
+ }
+bad:
+ if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) {
+ /*
+ * Trying to read the final fragment.
+ *
+ * NB - dump only works in TP_BSIZE blocks, hence
+ * rounds `dev_bsize' fragments up to TP_BSIZE pieces.
+ * It should be smarter about not actually trying to
+ * read more than it can get, but for the time being
+ * we punt and scale back the read only when it gets
+ * us into trouble. (mkm 9/25/83)
+ */
+ size -= dev_bsize;
+ goto loop;
+ }
+ if (cnt == -1)
+ msg("read error from %s: %s: [block %jd]: count=%d\n",
+ disk, strerror(errno), (intmax_t)blkno, size);
+ else
+ msg("short read error from %s: [block %jd]: count=%d, got=%d\n",
+ disk, (intmax_t)blkno, size, cnt);
+ if (++breaderrors > BREADEMAX) {
+ msg("More than %d block read errors from %s\n",
+ BREADEMAX, disk);
+ broadcast("DUMP IS AILING!\n");
+ msg("This is an unrecoverable error.\n");
+ if (!query("Do you want to attempt to continue?")){
+ dumpabort(0);
+ /*NOTREACHED*/
+ } else
+ breaderrors = 0;
+ }
+ /*
+ * Zero buffer, then try to read each sector of buffer separately,
+ * and bypass the cache.
+ */
+ memset(buf, 0, size);
+ for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) {
+ if ((cnt = pread(diskfd, buf, (int)dev_bsize,
+ ((off_t)blkno << dev_bshift))) == dev_bsize)
+ continue;
+ if (cnt == -1) {
+ msg("read error from %s: %s: [sector %jd]: count=%ld\n",
+ disk, strerror(errno), (intmax_t)blkno, dev_bsize);
+ continue;
+ }
+ msg("short read from %s: [sector %jd]: count=%ld, got=%d\n",
+ disk, (intmax_t)blkno, dev_bsize, cnt);
+ }
+}
diff --git a/sbin/dump/unctime.c b/sbin/dump/unctime.c
new file mode 100644
index 0000000..3720ebf
--- /dev/null
+++ b/sbin/dump/unctime.c
@@ -0,0 +1,56 @@
+/*-
+ * 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.
+ * 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 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 lint
+#if 0
+static char sccsid[] = "@(#)unctime.c 8.2 (Berkeley) 6/14/94";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <time.h>
+
+/*
+ * Convert a ctime(3) format string into a system format date.
+ * Return the date thus calculated.
+ *
+ * Return -1 if the string is not in ctime format.
+ */
+time_t
+unctime(char *str)
+{
+ struct tm then;
+
+ str = strptime(str, "%a %b %e %T %Y", &then);
+ if (str == NULL || (*str != '\n' && *str != '\0'))
+ return ((time_t)-1);
+ then.tm_isdst = -1;
+ return (mktime(&then));
+}
diff --git a/sbin/dumpfs/Makefile b/sbin/dumpfs/Makefile
new file mode 100644
index 0000000..99faa77
--- /dev/null
+++ b/sbin/dumpfs/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= dumpfs
+DPADD= ${LIBUFS}
+LDADD= -lufs
+MAN= dumpfs.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/dumpfs/dumpfs.8 b/sbin/dumpfs/dumpfs.8
new file mode 100644
index 0000000..c673b08
--- /dev/null
+++ b/sbin/dumpfs/dumpfs.8
@@ -0,0 +1,70 @@
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)dumpfs.8 8.1 (Berkeley) 6/5/93
+.\" $FreeBSD$
+.\"
+.Dd January 19, 2003
+.Dt DUMPFS 8
+.Os
+.Sh NAME
+.Nm dumpfs
+.Nd dump file system information
+.Sh SYNOPSIS
+.Nm
+.Op Fl m
+.Ar filesys | device
+.Sh DESCRIPTION
+The
+.Nm
+utility prints out the super block and cylinder group information
+for the file system or special device specified, unless
+.Fl m
+is specified.
+The listing is very long and detailed.
+This
+command is useful mostly for finding out certain file system
+information such as the file system block size and minimum
+free space percentage.
+.Pp
+If
+.Fl m
+is specified, the file system is marshalled in terms of a
+.Xr newfs 8
+command to generate the file system.
+.Sh SEE ALSO
+.Xr disktab 5 ,
+.Xr fs 5 ,
+.Xr disklabel 8 ,
+.Xr fsck 8 ,
+.Xr newfs 8 ,
+.Xr tunefs 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
diff --git a/sbin/dumpfs/dumpfs.c b/sbin/dumpfs/dumpfs.c
new file mode 100644
index 0000000..325cdcb
--- /dev/null
+++ b/sbin/dumpfs/dumpfs.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and Network Associates Laboratories, the Security
+ * Research Division of Network Associates, Inc. under DARPA/SPAWAR
+ * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+ * research program.
+ *
+ * Copyright (c) 1983, 1992, 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)dumpfs.c 8.5 (Berkeley) 4/29/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/disklabel.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <libufs.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define afs disk.d_fs
+#define acg disk.d_cg
+
+struct uufsd disk;
+
+int dumpfs(const char *);
+int dumpcg(void);
+int marshal(const char *);
+void pbits(void *, int);
+void ufserr(const char *);
+void usage(void) __dead2;
+
+int
+main(int argc, char *argv[])
+{
+ const char *name;
+ int ch, domarshal, eval;
+
+ domarshal = eval = 0;
+
+ while ((ch = getopt(argc, argv, "m")) != -1) {
+ switch (ch) {
+ case 'm':
+ domarshal = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage();
+
+ while ((name = *argv++) != NULL) {
+ if (ufs_disk_fillout(&disk, name) == -1) {
+ ufserr(name);
+ eval |= 1;
+ continue;
+ }
+ if (domarshal)
+ eval |= marshal(name);
+ else
+ eval |= dumpfs(name);
+ ufs_disk_close(&disk);
+ }
+ exit(eval);
+}
+
+int
+dumpfs(const char *name)
+{
+ time_t fstime;
+ int64_t fssize;
+ int32_t fsflags;
+ int i;
+
+ switch (disk.d_ufs) {
+ case 2:
+ fssize = afs.fs_size;
+ fstime = afs.fs_time;
+ printf("magic\t%x (UFS2)\ttime\t%s",
+ afs.fs_magic, ctime(&fstime));
+ printf("superblock location\t%jd\tid\t[ %x %x ]\n",
+ (intmax_t)afs.fs_sblockloc, afs.fs_id[0], afs.fs_id[1]);
+ printf("ncg\t%d\tsize\t%jd\tblocks\t%jd\n",
+ afs.fs_ncg, (intmax_t)fssize, (intmax_t)afs.fs_dsize);
+ break;
+ case 1:
+ fssize = afs.fs_old_size;
+ fstime = afs.fs_old_time;
+ printf("magic\t%x (UFS1)\ttime\t%s",
+ afs.fs_magic, ctime(&fstime));
+ printf("id\t[ %x %x ]\n", afs.fs_id[0], afs.fs_id[1]);
+ printf("ncg\t%d\tsize\t%jd\tblocks\t%jd\n",
+ afs.fs_ncg, (intmax_t)fssize, (intmax_t)afs.fs_dsize);
+ break;
+ default:
+ goto err;
+ }
+ printf("bsize\t%d\tshift\t%d\tmask\t0x%08x\n",
+ afs.fs_bsize, afs.fs_bshift, afs.fs_bmask);
+ printf("fsize\t%d\tshift\t%d\tmask\t0x%08x\n",
+ afs.fs_fsize, afs.fs_fshift, afs.fs_fmask);
+ printf("frag\t%d\tshift\t%d\tfsbtodb\t%d\n",
+ afs.fs_frag, afs.fs_fragshift, afs.fs_fsbtodb);
+ printf("minfree\t%d%%\toptim\t%s\tsymlinklen %d\n",
+ afs.fs_minfree, afs.fs_optim == FS_OPTSPACE ? "space" : "time",
+ afs.fs_maxsymlinklen);
+ switch (disk.d_ufs) {
+ case 2:
+ printf("%s %d\tmaxbpg\t%d\tmaxcontig %d\tcontigsumsize %d\n",
+ "maxbsize", afs.fs_maxbsize, afs.fs_maxbpg,
+ afs.fs_maxcontig, afs.fs_contigsumsize);
+ printf("nbfree\t%jd\tndir\t%jd\tnifree\t%jd\tnffree\t%jd\n",
+ (intmax_t)afs.fs_cstotal.cs_nbfree,
+ (intmax_t)afs.fs_cstotal.cs_ndir,
+ (intmax_t)afs.fs_cstotal.cs_nifree,
+ (intmax_t)afs.fs_cstotal.cs_nffree);
+ printf("bpg\t%d\tfpg\t%d\tipg\t%d\n",
+ afs.fs_fpg / afs.fs_frag, afs.fs_fpg, afs.fs_ipg);
+ printf("nindir\t%d\tinopb\t%d\tmaxfilesize\t%ju\n",
+ afs.fs_nindir, afs.fs_inopb,
+ (uintmax_t)afs.fs_maxfilesize);
+ printf("sbsize\t%d\tcgsize\t%d\tcsaddr\t%jd\tcssize\t%d\n",
+ afs.fs_sbsize, afs.fs_cgsize, (intmax_t)afs.fs_csaddr,
+ afs.fs_cssize);
+ break;
+ case 1:
+ printf("maxbpg\t%d\tmaxcontig %d\tcontigsumsize %d\n",
+ afs.fs_maxbpg, afs.fs_maxcontig, afs.fs_contigsumsize);
+ printf("nbfree\t%d\tndir\t%d\tnifree\t%d\tnffree\t%d\n",
+ afs.fs_old_cstotal.cs_nbfree, afs.fs_old_cstotal.cs_ndir,
+ afs.fs_old_cstotal.cs_nifree, afs.fs_old_cstotal.cs_nffree);
+ printf("cpg\t%d\tbpg\t%d\tfpg\t%d\tipg\t%d\n",
+ afs.fs_old_cpg, afs.fs_fpg / afs.fs_frag, afs.fs_fpg,
+ afs.fs_ipg);
+ printf("nindir\t%d\tinopb\t%d\tnspf\t%d\tmaxfilesize\t%ju\n",
+ afs.fs_nindir, afs.fs_inopb, afs.fs_old_nspf,
+ (uintmax_t)afs.fs_maxfilesize);
+ printf("sbsize\t%d\tcgsize\t%d\tcgoffset %d\tcgmask\t0x%08x\n",
+ afs.fs_sbsize, afs.fs_cgsize, afs.fs_old_cgoffset,
+ afs.fs_old_cgmask);
+ printf("csaddr\t%d\tcssize\t%d\n",
+ afs.fs_old_csaddr, afs.fs_cssize);
+ printf("rotdelay %dms\trps\t%d\ttrackskew %d\tinterleave %d\n",
+ afs.fs_old_rotdelay, afs.fs_old_rps, afs.fs_old_trackskew,
+ afs.fs_old_interleave);
+ printf("nsect\t%d\tnpsect\t%d\tspc\t%d\n",
+ afs.fs_old_nsect, afs.fs_old_npsect, afs.fs_old_spc);
+ break;
+ default:
+ goto err;
+ }
+ printf("sblkno\t%d\tcblkno\t%d\tiblkno\t%d\tdblkno\t%d\n",
+ afs.fs_sblkno, afs.fs_cblkno, afs.fs_iblkno, afs.fs_dblkno);
+ printf("cgrotor\t%d\tfmod\t%d\tronly\t%d\tclean\t%d\n",
+ afs.fs_cgrotor, afs.fs_fmod, afs.fs_ronly, afs.fs_clean);
+ printf("avgfpdir %d\tavgfilesize %d\n",
+ afs.fs_avgfpdir, afs.fs_avgfilesize);
+ printf("flags\t");
+ if (afs.fs_old_flags & FS_FLAGS_UPDATED)
+ fsflags = afs.fs_flags;
+ else
+ fsflags = afs.fs_old_flags;
+ if (fsflags == 0)
+ printf("none");
+ if (fsflags & FS_UNCLEAN)
+ printf("unclean ");
+ if (fsflags & FS_DOSOFTDEP)
+ printf("soft-updates ");
+ if (fsflags & FS_NEEDSFSCK)
+ printf("needs fsck run ");
+ if (fsflags & FS_INDEXDIRS)
+ printf("indexed directories ");
+ if (fsflags & FS_ACLS)
+ printf("acls ");
+ if (fsflags & FS_MULTILABEL)
+ printf("multilabel ");
+ if (fsflags & FS_FLAGS_UPDATED)
+ printf("fs_flags expanded ");
+ fsflags &= ~(FS_UNCLEAN | FS_DOSOFTDEP | FS_NEEDSFSCK | FS_INDEXDIRS |
+ FS_ACLS | FS_MULTILABEL | FS_FLAGS_UPDATED);
+ if (fsflags != 0)
+ printf("unknown flags (%#x)", fsflags);
+ putchar('\n');
+ printf("fsmnt\t%s\n", afs.fs_fsmnt);
+ printf("volname\t%s\tswuid\t%ju\n",
+ afs.fs_volname, (uintmax_t)afs.fs_swuid);
+ printf("\ncs[].cs_(nbfree,ndir,nifree,nffree):\n\t");
+ afs.fs_csp = calloc(1, afs.fs_cssize);
+ if (bread(&disk, fsbtodb(&afs, afs.fs_csaddr), afs.fs_csp, afs.fs_cssize) == -1)
+ goto err;
+ for (i = 0; i < afs.fs_ncg; i++) {
+ struct csum *cs = &afs.fs_cs(&afs, i);
+ if (i && i % 4 == 0)
+ printf("\n\t");
+ printf("(%d,%d,%d,%d) ",
+ cs->cs_nbfree, cs->cs_ndir, cs->cs_nifree, cs->cs_nffree);
+ }
+ printf("\n");
+ if (fssize % afs.fs_fpg) {
+ if (disk.d_ufs == 1)
+ printf("cylinders in last group %d\n",
+ howmany(afs.fs_old_size % afs.fs_fpg,
+ afs.fs_old_spc / afs.fs_old_nspf));
+ printf("blocks in last group %ld\n\n",
+ (long)((fssize % afs.fs_fpg) / afs.fs_frag));
+ }
+ while ((i = cgread(&disk)) != 0) {
+ if (i == -1 || dumpcg())
+ goto err;
+ }
+ return (0);
+
+err: ufserr(name);
+ return (1);
+}
+
+int
+dumpcg(void)
+{
+ time_t cgtime;
+ off_t cur;
+ int i, j;
+
+ printf("\ncg %d:\n", disk.d_lcg);
+ cur = fsbtodb(&afs, cgtod(&afs, disk.d_lcg)) * disk.d_bsize;
+ switch (disk.d_ufs) {
+ case 2:
+ cgtime = acg.cg_time;
+ printf("magic\t%x\ttell\t%jx\ttime\t%s",
+ acg.cg_magic, (intmax_t)cur, ctime(&cgtime));
+ printf("cgx\t%d\tndblk\t%d\tniblk\t%d\tinitiblk %d\n",
+ acg.cg_cgx, acg.cg_ndblk, acg.cg_niblk, acg.cg_initediblk);
+ break;
+ case 1:
+ cgtime = acg.cg_old_time;
+ printf("magic\t%x\ttell\t%jx\ttime\t%s",
+ acg.cg_magic, (intmax_t)cur, ctime(&cgtime));
+ printf("cgx\t%d\tncyl\t%d\tniblk\t%d\tndblk\t%d\n",
+ acg.cg_cgx, acg.cg_old_ncyl, acg.cg_old_niblk,
+ acg.cg_ndblk);
+ break;
+ default:
+ break;
+ }
+ printf("nbfree\t%d\tndir\t%d\tnifree\t%d\tnffree\t%d\n",
+ acg.cg_cs.cs_nbfree, acg.cg_cs.cs_ndir,
+ acg.cg_cs.cs_nifree, acg.cg_cs.cs_nffree);
+ printf("rotor\t%d\tirotor\t%d\tfrotor\t%d\nfrsum",
+ acg.cg_rotor, acg.cg_irotor, acg.cg_frotor);
+ for (i = 1, j = 0; i < afs.fs_frag; i++) {
+ printf("\t%d", acg.cg_frsum[i]);
+ j += i * acg.cg_frsum[i];
+ }
+ printf("\nsum of frsum: %d", j);
+ if (afs.fs_contigsumsize > 0) {
+ for (i = 1; i < afs.fs_contigsumsize; i++) {
+ if ((i - 1) % 8 == 0)
+ printf("\nclusters %d-%d:", i,
+ afs.fs_contigsumsize - 1 < i + 7 ?
+ afs.fs_contigsumsize - 1 : i + 7);
+ printf("\t%d", cg_clustersum(&acg)[i]);
+ }
+ printf("\nclusters size %d and over: %d\n",
+ afs.fs_contigsumsize,
+ cg_clustersum(&acg)[afs.fs_contigsumsize]);
+ printf("clusters free:\t");
+ pbits(cg_clustersfree(&acg), acg.cg_nclusterblks);
+ } else
+ printf("\n");
+ printf("inodes used:\t");
+ pbits(cg_inosused(&acg), afs.fs_ipg);
+ printf("blks free:\t");
+ pbits(cg_blksfree(&acg), afs.fs_fpg);
+ return (0);
+}
+
+int
+marshal(const char *name)
+{
+ struct fs *fs;
+
+ fs = &disk.d_fs;
+
+ printf("# newfs command for %s (%s)\n", name, disk.d_name);
+ printf("newfs ");
+ if (fs->fs_volname[0] != '\0')
+ printf("-L %s ", fs->fs_volname);
+ printf("-O %d ", disk.d_ufs);
+ if (fs->fs_flags & FS_DOSOFTDEP)
+ printf("-U ");
+ printf("-a %d ", fs->fs_maxcontig);
+ printf("-b %d ", fs->fs_bsize);
+ /* -c is dumb */
+ printf("-d %d ", fs->fs_maxbsize);
+ printf("-e %d ", fs->fs_maxbpg);
+ printf("-f %d ", fs->fs_fsize);
+ printf("-g %d ", fs->fs_avgfilesize);
+ printf("-h %d ", fs->fs_avgfpdir);
+ /* -i is dumb */
+ /* -j..l unimplemented */
+ printf("-m %d ", fs->fs_minfree);
+ /* -n unimplemented */
+ printf("-o ");
+ switch (fs->fs_optim) {
+ case FS_OPTSPACE:
+ printf("space ");
+ break;
+ case FS_OPTTIME:
+ printf("time ");
+ break;
+ default:
+ printf("unknown ");
+ break;
+ }
+ /* -p..r unimplemented */
+ printf("-s %jd ", (intmax_t)fs->fs_size);
+ printf("%s ", disk.d_name);
+ printf("\n");
+
+ return 0;
+}
+
+void
+pbits(void *vp, int max)
+{
+ int i;
+ char *p;
+ int count, j;
+
+ for (count = i = 0, p = vp; i < max; i++)
+ if (isset(p, i)) {
+ if (count)
+ printf(",%s", count % 6 ? " " : "\n\t");
+ count++;
+ printf("%d", i);
+ j = i;
+ while ((i+1)<max && isset(p, i+1))
+ i++;
+ if (i != j)
+ printf("-%d", i);
+ }
+ printf("\n");
+}
+
+void
+ufserr(const char *name)
+{
+ if (disk.d_error != NULL)
+ warnx("%s: %s", name, disk.d_error);
+ else if (errno)
+ warn("%s", name);
+}
+
+void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: dumpfs [-m] filesys | device\n");
+ exit(1);
+}
diff --git a/sbin/dumpon/Makefile b/sbin/dumpon/Makefile
new file mode 100644
index 0000000..c273f93
--- /dev/null
+++ b/sbin/dumpon/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= dumpon
+WARNS?= 6
+MAN= dumpon.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/dumpon/dumpon.8 b/sbin/dumpon/dumpon.8
new file mode 100644
index 0000000..0c478e0
--- /dev/null
+++ b/sbin/dumpon/dumpon.8
@@ -0,0 +1,143 @@
+.\" 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.
+.\" 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 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: @(#)swapon.8 8.1 (Berkeley) 6/5/93
+.\" $FreeBSD$
+.\"
+.Dd May 12, 1995
+.Dt DUMPON 8
+.Os
+.Sh NAME
+.Nm dumpon
+.Nd "specify a device for crash dumps"
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Ar special_file
+.Nm
+.Op Fl v
+.Cm off
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to specify a device where the kernel can save a crash
+dump in the case of a panic.
+.Pp
+Calls to
+.Nm
+normally occur from the system multi-user initialization file
+.Pa /etc/rc ,
+controlled by the
+.Dq dumpdev
+variable in the boot time configuration file
+.Pa /etc/rc.conf .
+.Pp
+For most systems the size of the specified dump device must be at
+least the size of physical memory.
+Even though an additional 64 kB header is added to the dump, the BIOS for a
+platform typically holds back some memory, so it is not usually
+necessary to size the dump device larger than the actual amount of RAM
+available in the machine.
+.Pp
+The
+.Nm
+utility will refuse to enable a dump device which is smaller than the
+total amount of physical memory as reported by the
+.Va hw.physmem
+.Xr sysctl 8
+variable.
+.Pp
+The
+.Fl v
+flag causes
+.Nm
+to be verbose about its activity.
+.Sh IMPLEMENTATION NOTES
+Since a
+.Xr panic 9
+condition may occur in a situation
+where the kernel cannot trust its internal representation
+of the state of any given file system,
+one of the system swap devices,
+and
+.Em not
+a device containing a file system,
+should be used as the dump device.
+.Pp
+The
+.Nm
+utility operates by opening
+.Ar special_file
+and making a
+.Dv DIOCSKERNELDUMP
+.Xr ioctl 2
+request on it to save kernel crash dumps.
+If
+.Ar special_file
+is the text string:
+.Dq Li off ,
+.Nm
+performs a
+.Dv DIOCSKERNELDUMP
+.Xr ioctl 2
+on
+.Pa /dev/null
+and thus instructs the kernel not to save crash dumps.
+.Pp
+Since
+.Nm
+cannot be used during kernel initialization, the
+.Va dumpdev
+variable of
+.Xr loader 8
+must be used to enable dumps for system panics which occur
+during kernel initialization.
+.Sh FILES
+.Bl -tag -width "/dev/{ad,da}?s?b" -compact
+.It Pa /dev/{ad,da}?s?b
+standard swap areas
+.It Pa /etc/rc.conf
+boot-time system configuration
+.El
+.Sh SEE ALSO
+.Xr fstab 5 ,
+.Xr rc.conf 5 ,
+.Xr config 8 ,
+.Xr init 8 ,
+.Xr loader 8 ,
+.Xr rc 8 ,
+.Xr savecore 8 ,
+.Xr swapon 8 ,
+.Xr panic 9
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 2.1 .
+.Sh BUGS
+Because the file system layer is already dead by the time a crash dump
+is taken, it is not possible to send crash dumps directly to a file.
diff --git a/sbin/dumpon/dumpon.c b/sbin/dumpon/dumpon.c
new file mode 100644
index 0000000..df6f88a
--- /dev/null
+++ b/sbin/dumpon/dumpon.c
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "From: @(#)swapon.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/disk.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+static int verbose;
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: dumpon [-v] special_file",
+ " dumpon [-v] off");
+ exit(EX_USAGE);
+}
+
+static void
+check_size(int fd, const char *fn)
+{
+ int name[] = { CTL_HW, HW_PHYSMEM };
+ size_t namelen = sizeof name / sizeof *name;
+ unsigned long physmem;
+ size_t len = sizeof physmem;
+ off_t mediasize;
+
+ if (sysctl(name, namelen, &physmem, &len, NULL, 0) != 0)
+ err(EX_OSERR, "can't get memory size");
+ if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) != 0)
+ err(EX_OSERR, "%s: can't get size", fn);
+ if ((uintmax_t)mediasize < (uintmax_t)physmem) {
+ if (verbose)
+ printf("%s is smaller than physical memory\n", fn);
+ exit(EX_IOERR);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ int i, fd;
+ u_int u;
+
+ while ((ch = getopt(argc, argv, "v")) != -1)
+ switch((char)ch) {
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ if (strcmp(argv[0], "off") != 0) {
+ fd = open(argv[0], O_RDONLY);
+ if (fd < 0)
+ err(EX_OSFILE, "%s", argv[0]);
+ check_size(fd, argv[0]);
+ u = 0;
+ i = ioctl(fd, DIOCSKERNELDUMP, &u);
+ u = 1;
+ i = ioctl(fd, DIOCSKERNELDUMP, &u);
+ if (i == 0 && verbose)
+ printf("kernel dumps on %s\n", argv[0]);
+ } else {
+ fd = open(_PATH_DEVNULL, O_RDONLY);
+ if (fd < 0)
+ err(EX_OSFILE, "%s", _PATH_DEVNULL);
+ u = 0;
+ i = ioctl(fd, DIOCSKERNELDUMP, &u);
+ if (i == 0 && verbose)
+ printf("kernel dumps disabled\n");
+ }
+ if (i < 0)
+ err(EX_OSERR, "ioctl(DIOCSKERNELDUMP)");
+
+ exit (0);
+}
diff --git a/sbin/fdisk/Makefile b/sbin/fdisk/Makefile
new file mode 100644
index 0000000..6899c47
--- /dev/null
+++ b/sbin/fdisk/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+PROG= fdisk
+SRCS= fdisk.c geom_mbr_enc.c
+WARNS?= 4
+MAN= fdisk.8
+
+.PATH: ${.CURDIR}/../../sys/geom
+
+DPADD += ${LIBGEOM}
+LDADD += -lgeom
+
+.include <bsd.prog.mk>
+
+test: ${PROG}
+ sh ${.CURDIR}/runtest.sh
diff --git a/sbin/fdisk/fdisk.8 b/sbin/fdisk/fdisk.8
new file mode 100644
index 0000000..a7730ea
--- /dev/null
+++ b/sbin/fdisk/fdisk.8
@@ -0,0 +1,457 @@
+.\" $FreeBSD$
+.\"
+.Dd April 18, 2002
+.Dt FDISK 8
+.Os
+.Sh NAME
+.Nm fdisk
+.Nd PC slice table maintenance utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl BIaistu
+.Op Fl b Ar bootcode
+.Op Fl 1234
+.Op Ar disk
+.Nm
+.Fl f Ar configfile
+.Op Fl itv
+.Op Ar disk
+.Sh PROLOGUE
+In order for the BIOS to boot the kernel,
+certain conventions must be adhered to.
+Sector 0 of the disk must contain boot code,
+a slice table,
+and a magic number.
+BIOS slices can be used to break the disk up into several pieces.
+The BIOS brings in sector 0 and verifies the magic number.
+The sector
+0 boot code then searches the slice table to determine which
+slice is marked
+.Dq active .
+This boot code then brings in the bootstrap from the
+active slice and, if marked bootable, runs it.
+Under
+.Tn DOS ,
+you can have one or more slices with one active.
+The
+.Tn DOS
+.Nm
+utility can be used to divide space on the disk into slices and set one
+active.
+.Sh DESCRIPTION
+The
+.Fx
+utility,
+.Nm ,
+serves a similar purpose to the
+.Tn DOS
+utility.
+The first form is used to
+display slice information or to interactively edit the slice
+table.
+The second is used to write a slice table using a
+.Ar configfile ,
+and is designed to be used by other scripts/programs.
+.Pp
+Options are:
+.Bl -tag -width indent
+.It Fl a
+Change the active slice only.
+Ignored if
+.Fl f
+is given.
+.It Fl b Ar bootcode
+Get the boot code from the file
+.Ar bootcode .
+Default is
+.Pa /boot/mbr .
+.It Fl B
+Reinitialize the boot code contained in sector 0 of the disk.
+Ignored if
+.Fl f
+is given.
+.It Fl f Ar configfile
+Set slice values using the file
+.Ar configfile .
+The
+.Ar configfile
+always modifies existing slices, unless
+.Fl i
+is also given, in which case all existing slices are deleted (marked
+as
+.Dq unused )
+before the
+.Ar configfile
+is read.
+The
+.Ar configfile
+can be
+.Sq Fl ,
+in which case standard input is read.
+See
+.Sx CONFIGURATION FILE ,
+below, for file syntax.
+.Pp
+.Em WARNING :
+when
+.Fl f
+is used, you are not asked if you really want to write the slices
+table (as you are in the interactive mode).
+Use with caution!
+.It Fl i
+Initialize sector 0 of the disk.
+This implies
+.Fl u ,
+unless
+.Fl f
+is given.
+.It Fl I
+Initialize the contents of sector 0
+for one
+.Fx
+slice covering the entire disk.
+.It Fl s
+Print summary information and exit.
+.It Fl t
+Test mode; do not write slice values.
+Generally used with the
+.Fl f
+option to see what would be written to the slice table.
+Implies
+.Fl v .
+.It Fl u
+Is used for updating (editing) sector 0 of the disk.
+Ignored if
+.Fl f
+is given.
+.It Fl v
+Be verbose.
+When
+.Fl f
+is used,
+.Nm
+prints out the slice table that is written to the disk.
+.It Fl 1234
+Operate on a single slice table entry only.
+Ignored if
+.Fl f
+is given.
+.El
+.Pp
+The final disk name can be provided as a
+.Dq bare
+disk name only, e.g.\&
+.Pa da0 ,
+or as a full pathname.
+If omitted,
+.Nm
+tries to figure out the default disk device name from the
+mounted root device.
+.Pp
+When called with no arguments, it prints the sector 0 slice table.
+An example follows:
+.Bd -literal
+ ******* Working on device /dev/ad0 *******
+ parameters extracted from in-core disklabel are:
+ cylinders=769 heads=15 sectors/track=33 (495 blks/cyl)
+
+ parameters to be used for BIOS calculations are:
+ cylinders=769 heads=15 sectors/track=33 (495 blks/cyl)
+
+ Warning: BIOS sector numbering starts with sector 1
+ Information from DOS bootblock is:
+ The data for partition 1 is:
+ sysid 165,(FreeBSD/NetBSD/386BSD)
+ start 495, size 380160 (185 Meg), flag 0
+ beg: cyl 1/ sector 1/ head 0;
+ end: cyl 768/ sector 33/ head 14
+ The data for partition 2 is:
+ sysid 164,(unknown)
+ start 378180, size 2475 (1 Meg), flag 0
+ beg: cyl 764/ sector 1/ head 0;
+ end: cyl 768/ sector 33/ head 14
+ The data for partition 3 is:
+ <UNUSED>
+ The data for partition 4 is:
+ sysid 99,(ISC UNIX, other System V/386, GNU HURD or Mach)
+ start 380656, size 224234 (109 Meg), flag 80
+ beg: cyl 769/ sector 2/ head 0;
+ end: cyl 197/ sector 33/ head 14
+.Ed
+.Pp
+The disk is divided into three slices that happen to fill the disk.
+The second slice overlaps the end of the first.
+(Used for debugging purposes.)
+.Bl -tag -width ".Em cyl , sector No and Em head"
+.It Em sysid
+is used to label the slice.
+.Fx
+reserves the
+magic number 165 decimal (A5 in hex).
+.It Xo
+.Em start
+and
+.Em size
+.Xc
+fields provide the start address
+and size of a slice in sectors.
+.It Em "flag 80"
+specifies that this is the active slice.
+.It Xo
+.Em cyl , sector
+and
+.Em head
+.Xc
+fields are used to specify the beginning and end addresses of the slice.
+.El
+.Pp
+.Em Note :
+these numbers are calculated using BIOS's understanding of the disk geometry
+and saved in the bootblock.
+.Pp
+The
+.Fl i
+and
+.Fl u
+flags are used to indicate that the slice data is to be updated.
+Unless the
+.Fl f
+option is also given,
+.Nm
+will enter a conversational mode.
+In this mode, no changes will be written to disk unless you explicitly tell
+.Nm
+to.
+.Pp
+The
+.Nm
+utility will display each slice and ask whether you want to edit it.
+If you say yes,
+.Nm
+will step through each field, show you the old value,
+and ask you for a new one.
+When you are done with the slice,
+.Nm
+will display it and ask you whether it is correct.
+It will then proceed to the next entry.
+.Pp
+Getting the
+.Em cyl , sector ,
+and
+.Em head
+fields correct is tricky, so by default,
+they will be calculated for you;
+you can specify them if you choose to though.
+.Pp
+After all the slices are processed,
+you are given the option to change the
+.Dq active
+slice.
+Finally, when all the new data for sector 0 has been accumulated,
+you are asked to confirm whether you really want to rewrite it.
+.Pp
+The difference between the
+.Fl u
+and
+.Fl i
+flags is that
+the
+.Fl u
+flag just edits (updates) the fields as they appear on the disk,
+while the
+.Fl i
+flag is used to
+.Dq initialize
+sector 0;
+it will set up the last BIOS slice to use the whole disk for
+.Fx
+and make it active.
+.Sh NOTES
+The automatic calculation of starting cylinder etc.\& uses
+a set of figures that represent what the BIOS thinks the
+geometry of the drive is.
+These figures are taken from the in-core disklabel by default,
+but
+.Nm
+initially gives you an opportunity to change them.
+This allows you to create a bootblock that can work with drives
+that use geometry translation under the BIOS.
+.Pp
+If you hand craft your disk layout,
+please make sure that the
+.Fx
+slice starts on a cylinder boundary.
+A number of decisions made later may assume this.
+(This might not be necessary later.)
+.Pp
+Editing an existing slice will most likely result in the loss of
+all data in that slice.
+.Pp
+You should run
+.Nm
+interactively once or twice to see how it works.
+This is completely safe as long as you answer the last question
+in the negative.
+There are subtleties that
+.Nm
+detects that are not fully explained in this manual page.
+.Sh CONFIGURATION FILE
+When the
+.Fl f
+option is given, a disk's slice table can be written using values
+from a
+.Ar configfile .
+The syntax of this file is very simple;
+each line is either a comment or a specification, as follows:
+.Bl -tag -width indent
+.It Ic # Ar comment ...
+Lines beginning with a
+.Ic #
+are comments and are ignored.
+.It Ic g Ar spec1 spec2 spec3
+Set the BIOS geometry used in slice calculations.
+There must be
+three values specified, with a letter preceding each number:
+.Bl -tag -width indent
+.It Cm c Ns Ar num
+Set the number of cylinders to
+.Ar num .
+.It Cm h Ns Ar num
+Set the number of heads to
+.Ar num .
+.It Cm s Ns Ar num
+Set the number of sectors/track to
+.Ar num .
+.El
+.Pp
+These specs can occur in any order, as the leading letter determines
+which value is which; however, all three must be specified.
+.Pp
+This line must occur before any lines that specify slice
+information.
+.Pp
+It is an error if the following is not true:
+.Bd -literal -offset indent
+1 <= number of cylinders
+1 <= number of heads <= 256
+1 <= number of sectors/track < 64
+.Ed
+.Pp
+The number of cylinders should be less than or equal to 1024, but this
+is not enforced, although a warning will be printed.
+Note that bootable
+.Fx
+slices (the
+.Dq Pa /
+file system) must lie completely within the
+first 1024 cylinders; if this is not true, booting may fail.
+Non-bootable slices do not have this restriction.
+.Pp
+Example (all of these are equivalent), for a disk with 1019 cylinders,
+39 heads, and 63 sectors:
+.Bd -literal -offset indent
+g c1019 h39 s63
+g h39 c1019 s63
+g s63 h39 c1019
+.Ed
+.It Ic p Ar slice type start length
+Set the slice given by
+.Ar slice
+(1-4) to type
+.Ar type ,
+starting at sector
+.Ar start
+for
+.Ar length
+sectors.
+.Pp
+Only those slices explicitly mentioned by these lines are modified;
+any slice not referenced by a
+.Ic p
+line will not be modified.
+However, if an invalid slice table is present, or the
+.Fl i
+option is specified, all existing slice entries will be cleared
+(marked as unused), and these
+.Ic p
+lines will have to be used to
+explicitly set slice information.
+If multiple slices need to be
+set, multiple
+.Ic p
+lines must be specified; one for each slice.
+.Pp
+These slice lines must occur after any geometry specification lines,
+if one is present.
+.Pp
+The
+.Ar type
+is 165 for
+.Fx
+slices.
+Specifying a slice type of zero is
+the same as clearing the slice and marking it as unused; however,
+dummy values (such as
+.Dq 0 )
+must still be specified for
+.Ar start
+and
+.Ar length .
+.Pp
+Note: the start offset will be rounded upwards to a head boundary if
+necessary, and the end offset will be rounded downwards to a cylinder
+boundary if necessary.
+.Pp
+Example: to clear slice 4 and mark it as unused:
+.Pp
+.Dl "p 4 0 0 0"
+.Pp
+Example: to set slice 1 to a
+.Fx
+slice, starting at sector 1
+for 2503871 sectors (note: these numbers will be rounded upwards and
+downwards to correspond to head and cylinder boundaries):
+.Pp
+.Dl "p 1 165 1 2503871"
+.Pp
+.It Ic a Ar slice
+Make
+.Ar slice
+the active slice.
+Can occur anywhere in the config file, but only
+one must be present.
+.Pp
+Example: to make slice 1 the active slice:
+.Pp
+.Dl "a 1"
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /boot/mbr" -compact
+.It Pa /boot/mbr
+The default boot code.
+.El
+.Sh SEE ALSO
+.Xr boot0cfg 8 ,
+.Xr bsdlabel 8 ,
+.Xr newfs 8
+.Sh BUGS
+The default boot code will not necessarily handle all slice types
+correctly, in particular those introduced since
+.Tn MS-DOS
+6.x.
+.Pp
+The entire utility should be made more user-friendly.
+.Pp
+Most users new to
+.Fx
+do not understand the difference between
+.Dq slice
+and
+.Dq partition ,
+causing difficulty to adjust.
+.Pp
+You cannot use this command to completely dedicate a disk to
+.Fx .
+The
+.Xr bsdlabel 8
+command must be used for this.
diff --git a/sbin/fdisk/fdisk.c b/sbin/fdisk/fdisk.c
new file mode 100644
index 0000000..d0b12b7
--- /dev/null
+++ b/sbin/fdisk/fdisk.c
@@ -0,0 +1,1412 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/diskmbr.h>
+#include <sys/endian.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <err.h>
+#include <errno.h>
+#include <libgeom.h>
+#include <paths.h>
+#include <regex.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int iotest;
+
+#define LBUF 100
+static char lbuf[LBUF];
+
+/*
+ *
+ * Ported to 386bsd by Julian Elischer Thu Oct 15 20:26:46 PDT 1992
+ *
+ * 14-Dec-89 Robert Baron (rvb) at Carnegie-Mellon University
+ * Copyright (c) 1989 Robert. V. Baron
+ * Created.
+ */
+
+#define Decimal(str, ans, tmp) if (decimal(str, &tmp, ans)) ans = tmp
+
+#define RoundCyl(x) ((((x) + cylsecs - 1) / cylsecs) * cylsecs)
+
+#define MAX_SEC_SIZE 2048 /* maximum section size that is supported */
+#define MIN_SEC_SIZE 512 /* the sector size to start sensing at */
+static int secsize = 0; /* the sensed sector size */
+
+static char *disk;
+
+static int cyls, sectors, heads, cylsecs, disksecs;
+
+struct mboot {
+ unsigned char padding[2]; /* force the longs to be long aligned */
+ unsigned char *bootinst; /* boot code */
+ off_t bootinst_size;
+ struct dos_partition parts[NDOSPART];
+};
+
+static struct mboot mboot;
+static int fd;
+
+#define ACTIVE 0x80
+
+static uint dos_cyls;
+static uint dos_heads;
+static uint dos_sectors;
+static uint dos_cylsecs;
+
+#define DOSSECT(s,c) ((s & 0x3f) | ((c >> 2) & 0xc0))
+#define DOSCYL(c) (c & 0xff)
+
+#define MAX_ARGS 10
+
+static int current_line_number;
+
+static int geom_processed = 0;
+static int part_processed = 0;
+static int active_processed = 0;
+
+typedef struct cmd {
+ char cmd;
+ int n_args;
+ struct arg {
+ char argtype;
+ int arg_val;
+ } args[MAX_ARGS];
+} CMD;
+
+static int B_flag = 0; /* replace boot code */
+static int I_flag = 0; /* use entire disk for FreeBSD */
+static int a_flag = 0; /* set active partition */
+static char *b_flag = NULL; /* path to boot code */
+static int i_flag = 0; /* replace partition data */
+static int u_flag = 0; /* update partition data */
+static int s_flag = 0; /* Print a summary and exit */
+static int t_flag = 0; /* test only */
+static char *f_flag = NULL; /* Read config info from file */
+static int v_flag = 0; /* Be verbose */
+
+static struct part_type
+{
+ unsigned char type;
+ const char *name;
+} part_types[] = {
+ {0x00, "unused"}
+ ,{0x01, "Primary DOS with 12 bit FAT"}
+ ,{0x02, "XENIX / file system"}
+ ,{0x03, "XENIX /usr file system"}
+ ,{0x04, "Primary DOS with 16 bit FAT (< 32MB)"}
+ ,{0x05, "Extended DOS"}
+ ,{0x06, "Primary 'big' DOS (>= 32MB)"}
+ ,{0x07, "OS/2 HPFS, NTFS, QNX-2 (16 bit) or Advanced UNIX"}
+ ,{0x08, "AIX file system or SplitDrive"}
+ ,{0x09, "AIX boot partition or Coherent"}
+ ,{0x0A, "OS/2 Boot Manager, OPUS or Coherent swap"}
+ ,{0x0B, "DOS or Windows 95 with 32 bit FAT"}
+ ,{0x0C, "DOS or Windows 95 with 32 bit FAT (LBA)"}
+ ,{0x0E, "Primary 'big' DOS (>= 32MB, LBA)"}
+ ,{0x0F, "Extended DOS (LBA)"}
+ ,{0x10, "OPUS"}
+ ,{0x11, "OS/2 BM: hidden DOS with 12-bit FAT"}
+ ,{0x12, "Compaq diagnostics"}
+ ,{0x14, "OS/2 BM: hidden DOS with 16-bit FAT (< 32MB)"}
+ ,{0x16, "OS/2 BM: hidden DOS with 16-bit FAT (>= 32MB)"}
+ ,{0x17, "OS/2 BM: hidden IFS (e.g. HPFS)"}
+ ,{0x18, "AST Windows swapfile"}
+ ,{0x24, "NEC DOS"}
+ ,{0x3C, "PartitionMagic recovery"}
+ ,{0x39, "plan9"}
+ ,{0x40, "VENIX 286"}
+ ,{0x41, "Linux/MINIX (sharing disk with DRDOS)"}
+ ,{0x42, "SFS or Linux swap (sharing disk with DRDOS)"}
+ ,{0x43, "Linux native (sharing disk with DRDOS)"}
+ ,{0x4D, "QNX 4.2 Primary"}
+ ,{0x4E, "QNX 4.2 Secondary"}
+ ,{0x4F, "QNX 4.2 Tertiary"}
+ ,{0x50, "DM (disk manager)"}
+ ,{0x51, "DM6 Aux1 (or Novell)"}
+ ,{0x52, "CP/M or Microport SysV/AT"}
+ ,{0x53, "DM6 Aux3"}
+ ,{0x54, "DM6"}
+ ,{0x55, "EZ-Drive (disk manager)"}
+ ,{0x56, "Golden Bow (disk manager)"}
+ ,{0x5c, "Priam Edisk (disk manager)"} /* according to S. Widlake */
+ ,{0x61, "SpeedStor"}
+ ,{0x63, "System V/386 (such as ISC UNIX), GNU HURD or Mach"}
+ ,{0x64, "Novell Netware/286 2.xx"}
+ ,{0x65, "Novell Netware/386 3.xx"}
+ ,{0x70, "DiskSecure Multi-Boot"}
+ ,{0x75, "PCIX"}
+ ,{0x77, "QNX4.x"}
+ ,{0x78, "QNX4.x 2nd part"}
+ ,{0x79, "QNX4.x 3rd part"}
+ ,{0x80, "Minix until 1.4a"}
+ ,{0x81, "Minix since 1.4b, early Linux partition or Mitac disk manager"}
+ ,{0x82, "Linux swap or Solaris x86"}
+ ,{0x83, "Linux native"}
+ ,{0x84, "OS/2 hidden C: drive"}
+ ,{0x85, "Linux extended"}
+ ,{0x86, "NTFS volume set??"}
+ ,{0x87, "NTFS volume set??"}
+ ,{0x93, "Amoeba file system"}
+ ,{0x94, "Amoeba bad block table"}
+ ,{0x9F, "BSD/OS"}
+ ,{0xA0, "Suspend to Disk"}
+ ,{0xA5, "FreeBSD/NetBSD/386BSD"}
+ ,{0xA6, "OpenBSD"}
+ ,{0xA7, "NeXTSTEP"}
+ ,{0xA9, "NetBSD"}
+ ,{0xAC, "IBM JFS"}
+ ,{0xB7, "BSDI BSD/386 file system"}
+ ,{0xB8, "BSDI BSD/386 swap"}
+ ,{0xBE, "Solaris x86 boot"}
+ ,{0xBF, "Solaris x86 (new)"}
+ ,{0xC1, "DRDOS/sec with 12-bit FAT"}
+ ,{0xC4, "DRDOS/sec with 16-bit FAT (< 32MB)"}
+ ,{0xC6, "DRDOS/sec with 16-bit FAT (>= 32MB)"}
+ ,{0xC7, "Syrinx"}
+ ,{0xDB, "CP/M, Concurrent CP/M, Concurrent DOS or CTOS"}
+ ,{0xE1, "DOS access or SpeedStor with 12-bit FAT extended partition"}
+ ,{0xE3, "DOS R/O or SpeedStor"}
+ ,{0xE4, "SpeedStor with 16-bit FAT extended partition < 1024 cyl."}
+ ,{0xEB, "BeOS file system"}
+ ,{0xEE, "EFI GPT"}
+ ,{0xEF, "EFI System Partition"}
+ ,{0xF1, "SpeedStor"}
+ ,{0xF2, "DOS 3.3+ Secondary"}
+ ,{0xF4, "SpeedStor large partition"}
+ ,{0xFE, "SpeedStor >1024 cyl. or LANstep"}
+ ,{0xFF, "Xenix bad blocks table"}
+};
+
+static void print_s0(int which);
+static void print_part(int i);
+static void init_sector0(unsigned long start);
+static void init_boot(void);
+static void change_part(int i);
+static void print_params(void);
+static void change_active(int which);
+static void change_code(void);
+static void get_params_to_use(void);
+static char *get_rootdisk(void);
+static void dos(struct dos_partition *partp);
+static int open_disk(int flag);
+static ssize_t read_disk(off_t sector, void *buf);
+static int write_disk(off_t sector, void *buf);
+static int get_params(void);
+static int read_s0(void);
+static int write_s0(void);
+static int ok(const char *str);
+static int decimal(const char *str, int *num, int deflt);
+static const char *get_type(int type);
+static int read_config(char *config_file);
+static void reset_boot(void);
+static int sanitize_partition(struct dos_partition *);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ struct stat sb;
+ int c, i;
+ int partition = -1;
+ struct dos_partition *partp;
+
+ while ((c = getopt(argc, argv, "BIab:f:istuv1234")) != -1)
+ switch (c) {
+ case 'B':
+ B_flag = 1;
+ break;
+ case 'I':
+ I_flag = 1;
+ break;
+ case 'a':
+ a_flag = 1;
+ break;
+ case 'b':
+ b_flag = optarg;
+ break;
+ case 'f':
+ f_flag = optarg;
+ break;
+ case 'i':
+ i_flag = 1;
+ break;
+ case 's':
+ s_flag = 1;
+ break;
+ case 't':
+ t_flag = 1;
+ break;
+ case 'u':
+ u_flag = 1;
+ break;
+ case 'v':
+ v_flag = 1;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ partition = c - '0';
+ break;
+ default:
+ usage();
+ }
+ if (f_flag || i_flag)
+ u_flag = 1;
+ if (t_flag)
+ v_flag = 1;
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0) {
+ disk = get_rootdisk();
+ } else {
+ if (stat(argv[0], &sb) == 0) {
+ /* OK, full pathname given */
+ disk = argv[0];
+ } else if (errno == ENOENT && argv[0][0] != '/') {
+ /* Try prepending "/dev" */
+ asprintf(&disk, "%s%s", _PATH_DEV, argv[0]);
+ if (disk == NULL)
+ errx(1, "out of memory");
+ } else {
+ /* other stat error, let it fail below */
+ disk = argv[0];
+ }
+ }
+ if (open_disk(u_flag) < 0)
+ err(1, "cannot open disk %s", disk);
+
+ /* (abu)use mboot.bootinst to probe for the sector size */
+ if ((mboot.bootinst = malloc(MAX_SEC_SIZE)) == NULL)
+ err(1, "cannot allocate buffer to determine disk sector size");
+ read_disk(0, mboot.bootinst);
+ free(mboot.bootinst);
+ mboot.bootinst = NULL;
+
+ if (s_flag) {
+ if (read_s0())
+ err(1, "read_s0");
+ printf("%s: %d cyl %d hd %d sec\n", disk, dos_cyls, dos_heads,
+ dos_sectors);
+ printf("Part %11s %11s Type Flags\n", "Start", "Size");
+ for (i = 0; i < NDOSPART; i++) {
+ partp = ((struct dos_partition *) &mboot.parts) + i;
+ if (partp->dp_start == 0 && partp->dp_size == 0)
+ continue;
+ printf("%4d: %11lu %11lu 0x%02x 0x%02x\n", i + 1,
+ (u_long) partp->dp_start,
+ (u_long) partp->dp_size, partp->dp_typ,
+ partp->dp_flag);
+ }
+ exit(0);
+ }
+
+ printf("******* Working on device %s *******\n",disk);
+
+ if (I_flag) {
+ read_s0();
+ reset_boot();
+ partp = (struct dos_partition *) (&mboot.parts[0]);
+ partp->dp_typ = DOSPTYP_386BSD;
+ partp->dp_flag = ACTIVE;
+ partp->dp_start = dos_sectors;
+ partp->dp_size = (disksecs / dos_cylsecs) * dos_cylsecs -
+ dos_sectors;
+ dos(partp);
+ if (v_flag)
+ print_s0(-1);
+ if (!t_flag)
+ write_s0();
+ exit(0);
+ }
+ if (f_flag) {
+ if (read_s0() || i_flag)
+ reset_boot();
+ if (!read_config(f_flag))
+ exit(1);
+ if (v_flag)
+ print_s0(-1);
+ if (!t_flag)
+ write_s0();
+ } else {
+ if(u_flag)
+ get_params_to_use();
+ else
+ print_params();
+
+ if (read_s0())
+ init_sector0(dos_sectors);
+
+ printf("Media sector size is %d\n", secsize);
+ printf("Warning: BIOS sector numbering starts with sector 1\n");
+ printf("Information from DOS bootblock is:\n");
+ if (partition == -1)
+ for (i = 1; i <= NDOSPART; i++)
+ change_part(i);
+ else
+ change_part(partition);
+
+ if (u_flag || a_flag)
+ change_active(partition);
+
+ if (B_flag)
+ change_code();
+
+ if (u_flag || a_flag || B_flag) {
+ if (!t_flag) {
+ printf("\nWe haven't changed the partition table yet. ");
+ printf("This is your last chance.\n");
+ }
+ print_s0(-1);
+ if (!t_flag) {
+ if (ok("Should we write new partition table?"))
+ write_s0();
+ } else {
+ printf("\n-t flag specified -- partition table not written.\n");
+ }
+ }
+ }
+
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s%s",
+ "usage: fdisk [-BIaistu] [-b bootcode] [-1234] [disk]\n",
+ " fdisk -f configfile [-itv] [disk]\n");
+ exit(1);
+}
+
+static void
+print_s0(int which)
+{
+ int i;
+
+ print_params();
+ printf("Information from DOS bootblock is:\n");
+ if (which == -1)
+ for (i = 1; i <= NDOSPART; i++)
+ printf("%d: ", i), print_part(i);
+ else
+ print_part(which);
+}
+
+static struct dos_partition mtpart;
+
+static void
+print_part(int i)
+{
+ struct dos_partition *partp;
+ u_int64_t part_mb;
+
+ partp = ((struct dos_partition *) &mboot.parts) + i - 1;
+
+ if (!bcmp(partp, &mtpart, sizeof (struct dos_partition))) {
+ printf("<UNUSED>\n");
+ return;
+ }
+ /*
+ * Be careful not to overflow.
+ */
+ part_mb = partp->dp_size;
+ part_mb *= secsize;
+ part_mb /= (1024 * 1024);
+ printf("sysid %d (%#04x),(%s)\n", partp->dp_typ, partp->dp_typ,
+ get_type(partp->dp_typ));
+ printf(" start %lu, size %lu (%ju Meg), flag %x%s\n",
+ (u_long)partp->dp_start,
+ (u_long)partp->dp_size,
+ (uintmax_t)part_mb,
+ partp->dp_flag,
+ partp->dp_flag == ACTIVE ? " (active)" : "");
+ printf("\tbeg: cyl %d/ head %d/ sector %d;\n\tend: cyl %d/ head %d/ sector %d\n"
+ ,DPCYL(partp->dp_scyl, partp->dp_ssect)
+ ,partp->dp_shd
+ ,DPSECT(partp->dp_ssect)
+ ,DPCYL(partp->dp_ecyl, partp->dp_esect)
+ ,partp->dp_ehd
+ ,DPSECT(partp->dp_esect));
+}
+
+
+static void
+init_boot(void)
+{
+#ifndef __ia64__
+ const char *fname;
+ int fdesc, n;
+ struct stat sb;
+
+ fname = b_flag ? b_flag : "/boot/mbr";
+ if ((fdesc = open(fname, O_RDONLY)) == -1 ||
+ fstat(fdesc, &sb) == -1)
+ err(1, "%s", fname);
+ if ((mboot.bootinst_size = sb.st_size) % secsize != 0)
+ errx(1, "%s: length must be a multiple of sector size", fname);
+ if (mboot.bootinst != NULL)
+ free(mboot.bootinst);
+ if ((mboot.bootinst = malloc(mboot.bootinst_size = sb.st_size)) == NULL)
+ errx(1, "%s: unable to allocate read buffer", fname);
+ if ((n = read(fdesc, mboot.bootinst, mboot.bootinst_size)) == -1 ||
+ close(fdesc))
+ err(1, "%s", fname);
+ if (n != mboot.bootinst_size)
+ errx(1, "%s: short read", fname);
+#else
+ if (mboot.bootinst != NULL)
+ free(mboot.bootinst);
+ mboot.bootinst_size = secsize;
+ if ((mboot.bootinst = malloc(mboot.bootinst_size)) == NULL)
+ errx(1, "unable to allocate boot block buffer");
+ memset(mboot.bootinst, 0, mboot.bootinst_size);
+ le16enc(&mboot.bootinst[DOSMAGICOFFSET], DOSMAGIC);
+#endif
+}
+
+
+static void
+init_sector0(unsigned long start)
+{
+ struct dos_partition *partp = (struct dos_partition *) (&mboot.parts[0]);
+
+ init_boot();
+
+ partp->dp_typ = DOSPTYP_386BSD;
+ partp->dp_flag = ACTIVE;
+ start = ((start + dos_sectors - 1) / dos_sectors) * dos_sectors;
+ if(start == 0)
+ start = dos_sectors;
+ partp->dp_start = start;
+ partp->dp_size = (disksecs / dos_cylsecs) * dos_cylsecs - start;
+
+ dos(partp);
+}
+
+static void
+change_part(int i)
+{
+ struct dos_partition *partp = ((struct dos_partition *) &mboot.parts) + i - 1;
+
+ printf("The data for partition %d is:\n", i);
+ print_part(i);
+
+ if (u_flag && ok("Do you want to change it?")) {
+ int tmp;
+
+ if (i_flag) {
+ bzero((char *)partp, sizeof (struct dos_partition));
+ if (i == 1) {
+ init_sector0(1);
+ printf("\nThe static data for the slice 1 has been reinitialized to:\n");
+ print_part(i);
+ }
+ }
+
+ do {
+ Decimal("sysid (165=FreeBSD)", partp->dp_typ, tmp);
+ Decimal("start", partp->dp_start, tmp);
+ Decimal("size", partp->dp_size, tmp);
+ if (!sanitize_partition(partp)) {
+ warnx("ERROR: failed to adjust; setting sysid to 0");
+ partp->dp_typ = 0;
+ }
+
+ if (ok("Explicitly specify beg/end address ?"))
+ {
+ int tsec,tcyl,thd;
+ tcyl = DPCYL(partp->dp_scyl,partp->dp_ssect);
+ thd = partp->dp_shd;
+ tsec = DPSECT(partp->dp_ssect);
+ Decimal("beginning cylinder", tcyl, tmp);
+ Decimal("beginning head", thd, tmp);
+ Decimal("beginning sector", tsec, tmp);
+ partp->dp_scyl = DOSCYL(tcyl);
+ partp->dp_ssect = DOSSECT(tsec,tcyl);
+ partp->dp_shd = thd;
+
+ tcyl = DPCYL(partp->dp_ecyl,partp->dp_esect);
+ thd = partp->dp_ehd;
+ tsec = DPSECT(partp->dp_esect);
+ Decimal("ending cylinder", tcyl, tmp);
+ Decimal("ending head", thd, tmp);
+ Decimal("ending sector", tsec, tmp);
+ partp->dp_ecyl = DOSCYL(tcyl);
+ partp->dp_esect = DOSSECT(tsec,tcyl);
+ partp->dp_ehd = thd;
+ } else
+ dos(partp);
+
+ print_part(i);
+ } while (!ok("Are we happy with this entry?"));
+ }
+}
+
+static void
+print_params()
+{
+ printf("parameters extracted from in-core disklabel are:\n");
+ printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
+ ,cyls,heads,sectors,cylsecs);
+ if (dos_cyls > 1023 || dos_heads > 255 || dos_sectors > 63)
+ printf("Figures below won't work with BIOS for partitions not in cyl 1\n");
+ printf("parameters to be used for BIOS calculations are:\n");
+ printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
+ ,dos_cyls,dos_heads,dos_sectors,dos_cylsecs);
+}
+
+static void
+change_active(int which)
+{
+ struct dos_partition *partp = &mboot.parts[0];
+ int active, i, new, tmp;
+
+ active = -1;
+ for (i = 0; i < NDOSPART; i++) {
+ if ((partp[i].dp_flag & ACTIVE) == 0)
+ continue;
+ printf("Partition %d is marked active\n", i + 1);
+ if (active == -1)
+ active = i + 1;
+ }
+ if (a_flag && which != -1)
+ active = which;
+ else if (active == -1)
+ active = 1;
+
+ if (!ok("Do you want to change the active partition?"))
+ return;
+setactive:
+ do {
+ new = active;
+ Decimal("active partition", new, tmp);
+ if (new < 1 || new > 4) {
+ printf("Active partition number must be in range 1-4."
+ " Try again.\n");
+ goto setactive;
+ }
+ active = new;
+ } while (!ok("Are you happy with this choice"));
+ for (i = 0; i < NDOSPART; i++)
+ partp[i].dp_flag = 0;
+ if (active > 0 && active <= NDOSPART)
+ partp[active-1].dp_flag = ACTIVE;
+}
+
+static void
+change_code()
+{
+ if (ok("Do you want to change the boot code?"))
+ init_boot();
+}
+
+void
+get_params_to_use()
+{
+ int tmp;
+ print_params();
+ if (ok("Do you want to change our idea of what BIOS thinks ?"))
+ {
+ do
+ {
+ Decimal("BIOS's idea of #cylinders", dos_cyls, tmp);
+ Decimal("BIOS's idea of #heads", dos_heads, tmp);
+ Decimal("BIOS's idea of #sectors", dos_sectors, tmp);
+ dos_cylsecs = dos_heads * dos_sectors;
+ print_params();
+ }
+ while(!ok("Are you happy with this choice"));
+ }
+}
+
+
+/***********************************************\
+* Change real numbers into strange dos numbers *
+\***********************************************/
+static void
+dos(struct dos_partition *partp)
+{
+ int cy, sec;
+ u_int32_t end;
+
+ if (partp->dp_typ == 0 && partp->dp_start == 0 && partp->dp_size == 0) {
+ memcpy(partp, &mtpart, sizeof(*partp));
+ return;
+ }
+
+ /* Start c/h/s. */
+ partp->dp_shd = partp->dp_start % dos_cylsecs / dos_sectors;
+ cy = partp->dp_start / dos_cylsecs;
+ sec = partp->dp_start % dos_sectors + 1;
+ partp->dp_scyl = DOSCYL(cy);
+ partp->dp_ssect = DOSSECT(sec, cy);
+
+ /* End c/h/s. */
+ end = partp->dp_start + partp->dp_size - 1;
+ partp->dp_ehd = end % dos_cylsecs / dos_sectors;
+ cy = end / dos_cylsecs;
+ sec = end % dos_sectors + 1;
+ partp->dp_ecyl = DOSCYL(cy);
+ partp->dp_esect = DOSSECT(sec, cy);
+}
+
+static int
+open_disk(int flag)
+{
+ struct stat st;
+ int rwmode;
+
+ if (stat(disk, &st) == -1) {
+ if (errno == ENOENT)
+ return -2;
+ warnx("can't get file status of %s", disk);
+ return -1;
+ }
+ if ( !(st.st_mode & S_IFCHR) )
+ warnx("device %s is not character special", disk);
+ rwmode = a_flag || I_flag || B_flag || flag ? O_RDWR : O_RDONLY;
+ fd = open(disk, rwmode);
+ if (fd == -1 && errno == EPERM && rwmode == O_RDWR)
+ fd = open(disk, O_RDONLY);
+ if (fd == -1 && errno == ENXIO)
+ return -2;
+ if (fd == -1) {
+ warnx("can't open device %s", disk);
+ return -1;
+ }
+ if (get_params() == -1) {
+ warnx("can't get disk parameters on %s", disk);
+ return -1;
+ }
+ return fd;
+}
+
+static ssize_t
+read_disk(off_t sector, void *buf)
+{
+
+ lseek(fd, (sector * 512), 0);
+ if (secsize == 0)
+ for (secsize = MIN_SEC_SIZE; secsize <= MAX_SEC_SIZE;
+ secsize *= 2) {
+ /* try the read */
+ int size = read(fd, buf, secsize);
+ if (size == secsize)
+ /* it worked so return */
+ return secsize;
+ }
+ else
+ return read(fd, buf, secsize);
+
+ /* we failed to read at any of the sizes */
+ return -1;
+}
+
+static int
+write_disk(off_t sector, void *buf)
+{
+ int error;
+ struct gctl_req *grq;
+ const char *q;
+ char fbuf[BUFSIZ];
+ int i, fdw;
+
+ grq = gctl_get_handle();
+ gctl_ro_param(grq, "verb", -1, "write MBR");
+ gctl_ro_param(grq, "class", -1, "MBR");
+ q = strrchr(disk, '/');
+ if (q == NULL)
+ q = disk;
+ else
+ q++;
+ gctl_ro_param(grq, "geom", -1, q);
+ gctl_ro_param(grq, "data", secsize, buf);
+ q = gctl_issue(grq);
+ if (q == NULL) {
+ gctl_free(grq);
+ return(0);
+ }
+ warnx("%s", q);
+ gctl_free(grq);
+
+ error = pwrite(fd, buf, secsize, (sector * 512));
+ if (error == secsize)
+ return (0);
+
+ for (i = 1; i < 5; i++) {
+ sprintf(fbuf, "%ss%d", disk, i);
+ fdw = open(fbuf, O_RDWR, 0);
+ if (fdw < 0)
+ continue;
+ error = ioctl(fdw, DIOCSMBR, buf);
+ close(fdw);
+ if (error == 0)
+ return (0);
+ }
+ warnx("Failed to write sector zero");
+ return(EINVAL);
+}
+
+static int
+get_params()
+{
+ int error;
+ u_int u;
+ off_t o;
+
+ error = ioctl(fd, DIOCGFWSECTORS, &u);
+ if (error == 0)
+ sectors = dos_sectors = u;
+ else
+ sectors = dos_sectors = 63;
+
+ error = ioctl(fd, DIOCGFWHEADS, &u);
+ if (error == 0)
+ heads = dos_heads = u;
+ else
+ heads = dos_heads = 255;
+
+ dos_cylsecs = cylsecs = heads * sectors;
+ disksecs = cyls * heads * sectors;
+
+ error = ioctl(fd, DIOCGSECTORSIZE, &u);
+ if (error != 0 || u == 0)
+ u = 512;
+
+ error = ioctl(fd, DIOCGMEDIASIZE, &o);
+ if (error == 0) {
+ disksecs = o / u;
+ cyls = dos_cyls = o / (u * dos_heads * dos_sectors);
+ }
+
+ return (disksecs);
+}
+
+
+static int
+read_s0()
+{
+ int i;
+
+ mboot.bootinst_size = secsize;
+ if (mboot.bootinst != NULL)
+ free(mboot.bootinst);
+ if ((mboot.bootinst = malloc(mboot.bootinst_size)) == NULL) {
+ warnx("unable to allocate buffer to read fdisk "
+ "partition table");
+ return -1;
+ }
+ if (read_disk(0, mboot.bootinst) == -1) {
+ warnx("can't read fdisk partition table");
+ return -1;
+ }
+ if (le16dec(&mboot.bootinst[DOSMAGICOFFSET]) != DOSMAGIC) {
+ warnx("invalid fdisk partition table found");
+ /* So should we initialize things */
+ return -1;
+ }
+ for (i = 0; i < NDOSPART; i++)
+ dos_partition_dec(
+ &mboot.bootinst[DOSPARTOFF + i * DOSPARTSIZE],
+ &mboot.parts[i]);
+ return 0;
+}
+
+static int
+write_s0()
+{
+ int sector, i;
+
+ if (iotest) {
+ print_s0(-1);
+ return 0;
+ }
+ for(i = 0; i < NDOSPART; i++)
+ dos_partition_enc(&mboot.bootinst[DOSPARTOFF + i * DOSPARTSIZE],
+ &mboot.parts[i]);
+ le16enc(&mboot.bootinst[DOSMAGICOFFSET], DOSMAGIC);
+ for(sector = 0; sector < mboot.bootinst_size / secsize; sector++)
+ if (write_disk(sector,
+ &mboot.bootinst[sector * secsize]) == -1) {
+ warn("can't write fdisk partition table");
+ return -1;
+ }
+ return(0);
+}
+
+
+static int
+ok(const char *str)
+{
+ printf("%s [n] ", str);
+ fflush(stdout);
+ if (fgets(lbuf, LBUF, stdin) == NULL)
+ exit(1);
+ lbuf[strlen(lbuf)-1] = 0;
+
+ if (*lbuf &&
+ (!strcmp(lbuf, "yes") || !strcmp(lbuf, "YES") ||
+ !strcmp(lbuf, "y") || !strcmp(lbuf, "Y")))
+ return 1;
+ else
+ return 0;
+}
+
+static int
+decimal(const char *str, int *num, int deflt)
+{
+ int acc = 0, c;
+ char *cp;
+
+ while (1) {
+ printf("Supply a decimal value for \"%s\" [%d] ", str, deflt);
+ fflush(stdout);
+ if (fgets(lbuf, LBUF, stdin) == NULL)
+ exit(1);
+ lbuf[strlen(lbuf)-1] = 0;
+
+ if (!*lbuf)
+ return 0;
+
+ cp = lbuf;
+ while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+ if (!c)
+ return 0;
+ while ((c = *cp++)) {
+ if (c <= '9' && c >= '0')
+ acc = acc * 10 + c - '0';
+ else
+ break;
+ }
+ if (c == ' ' || c == '\t')
+ while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+ if (!c) {
+ *num = acc;
+ return 1;
+ } else
+ printf("%s is an invalid decimal number. Try again.\n",
+ lbuf);
+ }
+
+}
+
+static const char *
+get_type(int type)
+{
+ int numentries = (sizeof(part_types)/sizeof(struct part_type));
+ int counter = 0;
+ struct part_type *ptr = part_types;
+
+
+ while(counter < numentries) {
+ if(ptr->type == type)
+ return(ptr->name);
+ ptr++;
+ counter++;
+ }
+ return("unknown");
+}
+
+
+static void
+parse_config_line(char *line, CMD *command)
+{
+ char *cp, *end;
+
+ cp = line;
+ while (1) {
+ memset(command, 0, sizeof(*command));
+
+ while (isspace(*cp)) ++cp;
+ if (*cp == '\0' || *cp == '#')
+ break;
+ command->cmd = *cp++;
+
+ /*
+ * Parse args
+ */
+ while (1) {
+ while (isspace(*cp)) ++cp;
+ if (*cp == '#')
+ break; /* found comment */
+ if (isalpha(*cp))
+ command->args[command->n_args].argtype = *cp++;
+ if (!isdigit(*cp))
+ break; /* assume end of line */
+ end = NULL;
+ command->args[command->n_args].arg_val = strtol(cp, &end, 0);
+ if (cp == end)
+ break; /* couldn't parse number */
+ cp = end;
+ command->n_args++;
+ }
+ break;
+ }
+}
+
+
+static int
+process_geometry(CMD *command)
+{
+ int status = 1, i;
+
+ while (1) {
+ geom_processed = 1;
+ if (part_processed) {
+ warnx(
+ "ERROR line %d: the geometry specification line must occur before\n\
+ all partition specifications",
+ current_line_number);
+ status = 0;
+ break;
+ }
+ if (command->n_args != 3) {
+ warnx("ERROR line %d: incorrect number of geometry args",
+ current_line_number);
+ status = 0;
+ break;
+ }
+ dos_cyls = 0;
+ dos_heads = 0;
+ dos_sectors = 0;
+ for (i = 0; i < 3; ++i) {
+ switch (command->args[i].argtype) {
+ case 'c':
+ dos_cyls = command->args[i].arg_val;
+ break;
+ case 'h':
+ dos_heads = command->args[i].arg_val;
+ break;
+ case 's':
+ dos_sectors = command->args[i].arg_val;
+ break;
+ default:
+ warnx(
+ "ERROR line %d: unknown geometry arg type: '%c' (0x%02x)",
+ current_line_number, command->args[i].argtype,
+ command->args[i].argtype);
+ status = 0;
+ break;
+ }
+ }
+ if (status == 0)
+ break;
+
+ dos_cylsecs = dos_heads * dos_sectors;
+
+ /*
+ * Do sanity checks on parameter values
+ */
+ if (dos_cyls == 0) {
+ warnx("ERROR line %d: number of cylinders not specified",
+ current_line_number);
+ status = 0;
+ }
+ if (dos_cyls > 1024) {
+ warnx(
+ "WARNING line %d: number of cylinders (%d) may be out-of-range\n\
+ (must be within 1-1024 for normal BIOS operation, unless the entire disk\n\
+ is dedicated to FreeBSD)",
+ current_line_number, dos_cyls);
+ }
+
+ if (dos_heads == 0) {
+ warnx("ERROR line %d: number of heads not specified",
+ current_line_number);
+ status = 0;
+ } else if (dos_heads > 256) {
+ warnx("ERROR line %d: number of heads must be within (1-256)",
+ current_line_number);
+ status = 0;
+ }
+
+ if (dos_sectors == 0) {
+ warnx("ERROR line %d: number of sectors not specified",
+ current_line_number);
+ status = 0;
+ } else if (dos_sectors > 63) {
+ warnx("ERROR line %d: number of sectors must be within (1-63)",
+ current_line_number);
+ status = 0;
+ }
+
+ break;
+ }
+ return (status);
+}
+
+
+static int
+process_partition(CMD *command)
+{
+ int status = 0, partition;
+ u_int32_t prev_head_boundary, prev_cyl_boundary;
+ u_int32_t adj_size, max_end;
+ struct dos_partition *partp;
+
+ while (1) {
+ part_processed = 1;
+ if (command->n_args != 4) {
+ warnx("ERROR line %d: incorrect number of partition args",
+ current_line_number);
+ break;
+ }
+ partition = command->args[0].arg_val;
+ if (partition < 1 || partition > 4) {
+ warnx("ERROR line %d: invalid partition number %d",
+ current_line_number, partition);
+ break;
+ }
+ partp = ((struct dos_partition *) &mboot.parts) + partition - 1;
+ bzero((char *)partp, sizeof (struct dos_partition));
+ partp->dp_typ = command->args[1].arg_val;
+ partp->dp_start = command->args[2].arg_val;
+ partp->dp_size = command->args[3].arg_val;
+ max_end = partp->dp_start + partp->dp_size;
+
+ if (partp->dp_typ == 0) {
+ /*
+ * Get out, the partition is marked as unused.
+ */
+ /*
+ * Insure that it's unused.
+ */
+ bzero((char *)partp, sizeof (struct dos_partition));
+ status = 1;
+ break;
+ }
+
+ /*
+ * Adjust start upwards, if necessary, to fall on a head boundary.
+ */
+ if (partp->dp_start % dos_sectors != 0) {
+ prev_head_boundary = partp->dp_start / dos_sectors * dos_sectors;
+ if (max_end < dos_sectors ||
+ prev_head_boundary > max_end - dos_sectors) {
+ /*
+ * Can't go past end of partition
+ */
+ warnx(
+ "ERROR line %d: unable to adjust start of partition %d to fall on\n\
+ a head boundary",
+ current_line_number, partition);
+ break;
+ }
+ warnx(
+ "WARNING: adjusting start offset of partition %d\n\
+ from %u to %u, to fall on a head boundary",
+ partition, (u_int)partp->dp_start,
+ (u_int)(prev_head_boundary + dos_sectors));
+ partp->dp_start = prev_head_boundary + dos_sectors;
+ }
+
+ /*
+ * Adjust size downwards, if necessary, to fall on a cylinder
+ * boundary.
+ */
+ prev_cyl_boundary =
+ ((partp->dp_start + partp->dp_size) / dos_cylsecs) * dos_cylsecs;
+ if (prev_cyl_boundary > partp->dp_start)
+ adj_size = prev_cyl_boundary - partp->dp_start;
+ else {
+ warnx(
+ "ERROR: could not adjust partition to start on a head boundary\n\
+ and end on a cylinder boundary.");
+ return (0);
+ }
+ if (adj_size != partp->dp_size) {
+ warnx(
+ "WARNING: adjusting size of partition %d from %u to %u\n\
+ to end on a cylinder boundary",
+ partition, (u_int)partp->dp_size, (u_int)adj_size);
+ partp->dp_size = adj_size;
+ }
+ if (partp->dp_size == 0) {
+ warnx("ERROR line %d: size of partition %d is zero",
+ current_line_number, partition);
+ break;
+ }
+
+ dos(partp);
+ status = 1;
+ break;
+ }
+ return (status);
+}
+
+
+static int
+process_active(CMD *command)
+{
+ int status = 0, partition, i;
+ struct dos_partition *partp;
+
+ while (1) {
+ active_processed = 1;
+ if (command->n_args != 1) {
+ warnx("ERROR line %d: incorrect number of active args",
+ current_line_number);
+ status = 0;
+ break;
+ }
+ partition = command->args[0].arg_val;
+ if (partition < 1 || partition > 4) {
+ warnx("ERROR line %d: invalid partition number %d",
+ current_line_number, partition);
+ break;
+ }
+ /*
+ * Reset active partition
+ */
+ partp = ((struct dos_partition *) &mboot.parts);
+ for (i = 0; i < NDOSPART; i++)
+ partp[i].dp_flag = 0;
+ partp[partition-1].dp_flag = ACTIVE;
+
+ status = 1;
+ break;
+ }
+ return (status);
+}
+
+
+static int
+process_line(char *line)
+{
+ CMD command;
+ int status = 1;
+
+ while (1) {
+ parse_config_line(line, &command);
+ switch (command.cmd) {
+ case 0:
+ /*
+ * Comment or blank line
+ */
+ break;
+ case 'g':
+ /*
+ * Set geometry
+ */
+ status = process_geometry(&command);
+ break;
+ case 'p':
+ status = process_partition(&command);
+ break;
+ case 'a':
+ status = process_active(&command);
+ break;
+ default:
+ status = 0;
+ break;
+ }
+ break;
+ }
+ return (status);
+}
+
+
+static int
+read_config(char *config_file)
+{
+ FILE *fp = NULL;
+ int status = 1;
+ char buf[1010];
+
+ while (1) {
+ if (strcmp(config_file, "-") != 0) {
+ /*
+ * We're not reading from stdin
+ */
+ if ((fp = fopen(config_file, "r")) == NULL) {
+ status = 0;
+ break;
+ }
+ } else {
+ fp = stdin;
+ }
+ current_line_number = 0;
+ while (!feof(fp)) {
+ if (fgets(buf, sizeof(buf), fp) == NULL)
+ break;
+ ++current_line_number;
+ status = process_line(buf);
+ if (status == 0)
+ break;
+ }
+ break;
+ }
+ if (fp) {
+ /*
+ * It doesn't matter if we're reading from stdin, as we've reached EOF
+ */
+ fclose(fp);
+ }
+ return (status);
+}
+
+
+static void
+reset_boot(void)
+{
+ int i;
+ struct dos_partition *partp;
+
+ init_boot();
+ for (i = 0; i < 4; ++i) {
+ partp = ((struct dos_partition *) &mboot.parts) + i;
+ bzero((char *)partp, sizeof (struct dos_partition));
+ }
+}
+
+static int
+sanitize_partition(struct dos_partition *partp)
+{
+ u_int32_t prev_head_boundary, prev_cyl_boundary;
+ u_int32_t max_end, size, start;
+
+ start = partp->dp_start;
+ size = partp->dp_size;
+ max_end = start + size;
+ /* Only allow a zero size if the partition is being marked unused. */
+ if (size == 0) {
+ if (start == 0 && partp->dp_typ == 0)
+ return (1);
+ warnx("ERROR: size of partition is zero");
+ return (0);
+ }
+ /* Return if no adjustment is necessary. */
+ if (start % dos_sectors == 0 && (start + size) % dos_sectors == 0)
+ return (1);
+
+ if (start == 0) {
+ warnx("WARNING: partition overlaps with partition table");
+ if (ok("Correct this automatically?"))
+ start = dos_sectors;
+ }
+ if (start % dos_sectors != 0)
+ warnx("WARNING: partition does not start on a head boundary");
+ if ((start +size) % dos_sectors != 0)
+ warnx("WARNING: partition does not end on a cylinder boundary");
+ warnx("WARNING: this may confuse the BIOS or some operating systems");
+ if (!ok("Correct this automatically?"))
+ return (1);
+
+ /*
+ * Adjust start upwards, if necessary, to fall on a head boundary.
+ */
+ if (start % dos_sectors != 0) {
+ prev_head_boundary = start / dos_sectors * dos_sectors;
+ if (max_end < dos_sectors ||
+ prev_head_boundary >= max_end - dos_sectors) {
+ /*
+ * Can't go past end of partition
+ */
+ warnx(
+ "ERROR: unable to adjust start of partition to fall on a head boundary");
+ return (0);
+ }
+ start = prev_head_boundary + dos_sectors;
+ }
+
+ /*
+ * Adjust size downwards, if necessary, to fall on a cylinder
+ * boundary.
+ */
+ prev_cyl_boundary = ((start + size) / dos_cylsecs) * dos_cylsecs;
+ if (prev_cyl_boundary > start)
+ size = prev_cyl_boundary - start;
+ else {
+ warnx("ERROR: could not adjust partition to start on a head boundary\n\
+ and end on a cylinder boundary.");
+ return (0);
+ }
+
+ /* Finally, commit any changes to partp and return. */
+ if (start != partp->dp_start) {
+ warnx("WARNING: adjusting start offset of partition to %u",
+ (u_int)start);
+ partp->dp_start = start;
+ }
+ if (size != partp->dp_size) {
+ warnx("WARNING: adjusting size of partition to %u", (u_int)size);
+ partp->dp_size = size;
+ }
+
+ return (1);
+}
+
+/*
+ * Try figuring out the root device's canonical disk name.
+ * The following choices are considered:
+ * /dev/ad0s1a => /dev/ad0
+ * /dev/da0a => /dev/da0
+ * /dev/vinum/root => /dev/vinum/root
+ */
+static char *
+get_rootdisk(void)
+{
+ struct statfs rootfs;
+ regex_t re;
+#define NMATCHES 2
+ regmatch_t rm[NMATCHES];
+ char *s;
+ int rv;
+
+ if (statfs("/", &rootfs) == -1)
+ err(1, "statfs(\"/\")");
+
+ if ((rv = regcomp(&re, "^(/dev/[a-z]+[0-9]+)([sp][0-9]+)?[a-h]?$",
+ REG_EXTENDED)) != 0)
+ errx(1, "regcomp() failed (%d)", rv);
+ if ((rv = regexec(&re, rootfs.f_mntfromname, NMATCHES, rm, 0)) != 0)
+ errx(1,
+"mounted root fs resource doesn't match expectations (regexec returned %d)",
+ rv);
+ if ((s = malloc(rm[1].rm_eo - rm[1].rm_so + 1)) == NULL)
+ errx(1, "out of memory");
+ memcpy(s, rootfs.f_mntfromname + rm[1].rm_so,
+ rm[1].rm_eo - rm[1].rm_so);
+ s[rm[1].rm_eo - rm[1].rm_so] = 0;
+
+ return s;
+}
diff --git a/sbin/fdisk/runtest.sh b/sbin/fdisk/runtest.sh
new file mode 100644
index 0000000..f25f427
--- /dev/null
+++ b/sbin/fdisk/runtest.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+# $FreeBSD$
+
+set -e
+MD=`mdconfig -a -t malloc -s 4m -x 63 -y 16`
+if [ ! -c /dev/${MD} ] ; then
+ echo "MD device $MD did not materialize" 1>&2
+ exit 2
+fi
+trap "mdconfig -d -u ${MD}" EXIT INT TERM
+
+# Create an empty bootcode file to isolate our checksum from any changes
+# which might happen to the boot code file.
+dd if=/dev/zero of=tmp count=1 > /dev/null 2>&1
+./fdisk -b tmp -I $MD > /dev/null 2>&1
+rm tmp
+
+c=`dd if=/dev/${MD} count=1 2>/dev/null | md5`
+if [ $c != ea4277fcccb6a927a1a497a6b15bfb8c ] ; then
+ echo "FAILED: 'fdisk -I' gives bad checksum ($c)" 1>&2
+ exit 1
+fi
+echo "PASSED: fdisk -I"
+c=`./fdisk $MD | md5`
+if [ $c != 4b126d7ac4c6b2af7ef27ede8ef102ec ] ; then
+ echo "FAILED: 'fdisk' gives bad checksum ($c)" 1>&2
+ exit 1
+fi
+echo "PASSED: fdisk"
+exit 0
diff --git a/sbin/fdisk_pc98/Makefile b/sbin/fdisk_pc98/Makefile
new file mode 100644
index 0000000..e5932f8
--- /dev/null
+++ b/sbin/fdisk_pc98/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+PROG= fdisk
+SRCS= fdisk.c geom_pc98_enc.c
+WARNS?= 4
+MAN= fdisk.8
+
+.PATH: ${.CURDIR}/../../sys/geom
+
+DPADD += ${LIBGEOM}
+LDADD += -lgeom
+
+.include <bsd.prog.mk>
diff --git a/sbin/fdisk_pc98/fdisk.8 b/sbin/fdisk_pc98/fdisk.8
new file mode 100644
index 0000000..c4b25c7
--- /dev/null
+++ b/sbin/fdisk_pc98/fdisk.8
@@ -0,0 +1,483 @@
+.\" $FreeBSD$
+.\"
+.Dd October 4, 1996
+.Dt FDISK 8
+.Os
+.Sh NAME
+.Nm fdisk
+.Nd PC partition table maintenance program
+.Sh SYNOPSIS
+.Nm
+.\" !PC98 .Op Fl BIaistu
+.Op Fl Bastu
+.Op Fl b Ar bootcode
+.Op Fl 1234
+.Op Ar disk
+.Nm
+.Fl f Ar configfile
+.Op Fl itv
+.Op Ar disk
+.Sh PROLOGUE
+In order for the BIOS to boot the kernel,
+certain conventions must be adhered to.
+Sector 0 of the disk must contain boot code,
+a partition table,
+and a magic number.
+BIOS partitions can be used to break the disk up into several pieces.
+The BIOS brings in sector 0 and verifies the magic number.
+The sector
+0 boot code then searches the partition table to determine which
+partition is marked
+.Em active .
+This boot code then brings in the bootstrap from the
+.Em active
+partition and, if marked bootable, runs it.
+Under DOS,
+you can have one or more partitions with one
+.Em active .
+The DOS
+.Nm
+utility can be used to divide space on the disk into partitions and set one
+.Em active .
+.Sh DESCRIPTION
+The
+.Fx
+.Nm
+utility serves a similar purpose to the DOS program.
+The first form is used to
+display partition information or to interactively edit the partition
+table.
+The second is used to write a partition table using a
+.Ar configfile
+and is designed to be used by other scripts/programs.
+.Pp
+Options are:
+.Bl -tag -width time
+.It Fl a
+Change the active partition only.
+Ignored if
+.Fl f
+is given.
+.It Fl b Ar bootcode
+Get the boot code from the file
+.Ar bootcode .
+.It Fl B
+Reinitialize the boot code contained in sector 0 of the disk.
+Ignored
+if
+.Fl f
+is given.
+.It Fl f Ar configfile
+Set partition values using the file
+.Ar configfile .
+The
+.Ar configfile
+always modifies existing partitions, unless
+.Fl i
+is also given, in which case all existing partitions are deleted (marked
+as "unused") before the
+.Ar configfile
+is read.
+The
+.Ar configfile
+can be "-", in which case
+.Ar stdin
+is read.
+See
+.Sx CONFIGURATION FILE ,
+below, for file syntax.
+.Pp
+.Em WARNING :
+when
+.Fl f
+is used, you are not asked if you really want to write the partition
+table (as you are in the interactive mode).
+Use with caution!
+.\" !PC98
+.\" .It Fl i
+.\" Initialize sector 0 of the disk. This implies
+.\" .Fl u ,
+.\" unless
+.\" .Fl f
+.\" is given.
+.\" .It Fl I
+.\" Initialize the contents of sector 0
+.\" with one
+/\" .Fx
+/\" slice covering the entire disk.
+.It Fl s
+Print summary information and exit.
+.It Fl t
+Test mode; do not write partition values.
+Generally used with the
+.Fl f
+option to see what would be written to the partition table.
+Implies
+.Fl v .
+.It Fl u
+Is used for updating (editing) sector 0 of the disk.
+Ignored if
+.Fl f
+is given.
+.It Fl v
+Be verbose.
+When
+.Fl f
+is used,
+.Nm
+prints out the partition table that is written to the disk.
+.It Fl 12345678
+Operate on a single fdisk entry only.
+Ignored if
+.Fl f
+is given.
+.El
+.Pp
+The final disk name can be provided as a
+.Sq bare
+disk name only, e.g.\&
+.Ql da0 ,
+or as a fully qualified device node under
+.Pa /dev .
+If omitted, the disks
+.Ql wd0 ,
+.Ql da0 ,
+and
+.Ql od0
+are being searched in that order, until one is
+being found responding.
+.Pp
+When called with no arguments, it prints the sector 0 partition table.
+An example follows:
+.Bd -literal
+ ******* Working on device /dev/rda0 *******
+ parameters extracted from in-core disklabel are:
+ cylinders=33075 heads=8 sectors/track=32 (256 blks/cyl)
+
+ parameters to be used for BIOS calculations are:
+ cylinders=33075 heads=8 sectors/track=32 (256 blks/cyl)
+
+ Media sector size is 512
+ Warning: BIOS sector numbering starts with sector 1
+ Information from DOS bootblock is:
+ The data for partition 1 is:
+ sysmid 148,(FreeBSD/NetBSD/386BSD)
+ start 256, size 2490112 (1215 Meg), sid 196
+ beg: cyl 1/ sector 0/ head 0;
+ end: cyl 9727/ sector 0/ head 0
+ system Name FreeBSD(98)
+ The data for partition 2 is:
+ sysmid 148,(FreeBSD/NetBSD/386BSD)
+ start 2490368, size 5505024 (2688 Meg), sid 196
+ beg: cyl 9728/ sector 0/ head 0;
+ end: cyl 31231/ sector 0/ head 0
+ system Name FreeBSD(98)
+ The data for partition 3 is:
+ <UNUSED>
+ The data for partition 4 is:
+ <UNUSED>
+ The data for partition 5 is:
+ <UNUSED>
+ The data for partition 6 is:
+ <UNUSED>
+ The data for partition 7 is:
+ <UNUSED>
+ The data for partition 8 is:
+ <UNUSED>
+ The data for partition 9 is:
+ <UNUSED>
+ The data for partition 10 is:
+ <UNUSED>
+ The data for partition 11 is:
+ <UNUSED>
+ The data for partition 12 is:
+ <UNUSED>
+ The data for partition 13 is:
+ <UNUSED>
+ The data for partition 14 is:
+ <UNUSED>
+ The data for partition 15 is:
+ <UNUSED>
+ The data for partition 16 is:
+ <UNUSED>
+.Ed
+.Pp
+The disk is divided into three partitions that happen to fill the disk.
+The second partition overlaps the end of the first.
+(Used for debugging purposes)
+.Bl -tag -width "cyl, sector and head"
+.It Em "sysmid"
+is used to label the partition.
+.Fx
+reserves the
+magic number 148 decimal (94 in hex).
+.It Em start No and Em size
+fields provide the start address
+and size of a partition in sectors.
+.\" !PC98 .It Em "flag 80"
+.\" specifies that this is the active partition.
+.It Em cyl , sector No and Em head
+fields are used to specify the beginning address
+and end address for the partition.
+.It Em "system Name"
+is the name of the partition.
+.It Em Note :
+these numbers are calculated using BIOS's understanding of the disk geometry
+and saved in the bootblock.
+.El
+.Pp
+The flags
+.\" .Fl i
+.\" or
+.Fl u
+are used to indicate that the partition data is to be updated, unless the
+.Fl f
+option is used.
+If the
+.Fl f
+option is not used, the
+.Nm
+utility will enter a conversational mode.
+This mode is designed not to change any data unless you explicitly tell it to.
+The
+.Nm
+utility selects defaults for its questions to guarantee the above behavior.
+.Pp
+It displays each partition
+and ask if you want to edit it.
+If you say yes,
+it will step through each field showing the old value
+and asking for a new one.
+When you are done with a partition,
+.Nm
+will display it and ask if it is correct.
+The
+.Nm
+utility will then proceed to the next entry.
+.Pp
+Getting the
+.Em cyl , sector ,
+and
+.Em head
+fields correct is tricky.
+So by default,
+they will be calculated for you;
+you can specify them if you choose.
+.Pp
+After all the partitions are processed,
+you are given the option to change the
+.Em active
+partition.
+Finally,
+when the all the data for the first sector has been accumulated,
+you are asked if you really want to rewrite sector 0.
+Only if you answer yes,
+will the data be written to disk.
+.Pp
+The difference between the
+.Fl u
+flag and
+.Fl i
+flag is that
+the
+.Fl u
+flag just edits the fields as they appear on the disk.
+While the
+.Fl i
+flag is used to "initialize" sector 0;
+it will setup the last BIOS partition to use the whole disk for
+.Fx ;
+and make it active.
+.Sh NOTES
+The automatic calculation of starting cylinder etc.\& uses
+a set of figures that represent what the BIOS thinks is the
+geometry of the drive.
+These figures are by default taken from the incore disklabel,
+but the program initially gives you an opportunity to change them.
+This allows the user to create a bootblock that can work with drives
+that use geometry translation under the BIOS.
+.Pp
+If you hand craft your disk layout,
+please make sure that the
+.Fx
+partition starts on a cylinder boundary.
+A number of decisions made later may assume this.
+(This might not be necessary later.)
+.Pp
+Editing an existing partition will most likely cause you to
+lose all the data in that partition.
+.Pp
+You should run this program interactively once or twice to see how it
+works.
+This is completely safe as long as you answer the last question
+in the negative.
+There are subtleties that the program detects that are
+not fully explained in this manual page.
+.Sh CONFIGURATION FILE
+When the
+.Fl f
+option is given, a disk's partition table can be written using values
+from a
+.Ar configfile .
+The syntax of this file is very simple.
+Each line is either a comment or
+a specification, and whitespace (except for newlines) are ignored:
+.Bl -tag -width Ds
+.It Xo
+.Ic #
+.Ar comment ...
+.Xc
+Lines beginning with a "#" are comments and are ignored.
+.It Xo
+.Ic g
+.Ar spec1
+.Ar spec2
+.Ar spec3
+.Xc
+Set the BIOS geometry used in partition calculations.
+There must be
+three values specified, with a letter preceding each number:
+.Bl -tag -width Ds
+.Sm off
+.It Cm c Ar num
+.Sm on
+Set the number of cylinders to
+.Ar num .
+.Sm off
+.It Cm h Ar num
+.Sm on
+Set the number of heads to
+.Ar num .
+.Sm off
+.It Cm s Ar num
+.Sm on
+Set the number of sectors/track to
+.Ar num .
+.El
+.Pp
+These specs can occur in any order, as the leading letter determines
+which value is which; however, all three must be specified.
+.Pp
+This line must occur before any lines that specify partition
+information.
+.Pp
+It is an error if the following is not true:
+.Bd -literal -offset indent
+1 <= number of cylinders
+1 <= number of heads <= 256
+1 <= number of sectors/track < 64
+.Ed
+.Pp
+The number of cylinders should be less than or equal to 1024, but this
+is not enforced, although a warning will be output.
+Note that bootable
+.Fx
+partitions (the "/" file system) must lie completely within the
+first 1024 cylinders; if this is not true, booting may fail.
+Non-bootable partitions do not have this restriction.
+.Pp
+Example (all of these are equivalent), for a disk with 1019 cylinders,
+39 heads, and 63 sectors:
+.Bd -literal -offset indent
+g c1019 h39 s63
+g h39 c1019 s63
+g s63 h39 c1019
+.Ed
+.It Xo
+.Ic p
+.Ar partition
+.Ar type
+.Ar start
+.Ar length
+.Xc
+Set the partition given by
+.Ar partition
+(1-4) to type
+.Ar type ,
+starting at sector
+.Ar start
+for
+.Ar length
+sectors.
+.Pp
+Only those partitions explicitly mentioned by these lines are modified;
+any partition not referenced by a "p" line will not be modified.
+However, if an invalid partition table is present, or the
+.Fl i
+option is specified, all existing partition entries will be cleared
+(marked as unused), and these "p" lines will have to be used to
+explicitly set partition information.
+If multiple partitions need to be
+set, multiple "p" lines must be specified; one for each partition.
+.Pp
+These partition lines must occur after any geometry specification lines,
+if one is present.
+.Pp
+The
+.Ar type
+is 165 for
+.Fx
+partitions.
+Specifying a partition type of zero is
+the same as clearing the partition and marking it as unused; however,
+dummy values (such as "0") must still be specified for
+.Ar start
+and
+.Ar length .
+.Pp
+Note: the start offset will be rounded upwards to a head boundary if
+necessary, and the end offset will be rounded downwards to a cylinder
+boundary if necessary.
+.Pp
+Example: to clear partition 4 and mark it as unused:
+.Bd -literal -offset indent
+p 4 0 0 0
+.Ed
+.Pp
+Example: to set partition 1 to a
+.Fx
+partition, starting at sector 1
+for 2503871 sectors (note: these numbers will be rounded upwards and
+downwards to correspond to head and cylinder boundaries):
+.Bd -literal -offset indent
+p 1 165 1 2503871
+.Ed
+.It Xo
+.Ic a
+.Ar partition
+.Xc
+Make
+.Ar partition
+the active partition.
+Can occur anywhere in the config file, but only
+one must be present.
+.Pp
+Example: to make partition 1 the active partition:
+.Bd -literal -offset indent
+a 1
+.Ed
+.El
+.Sh FILES
+.Bl -tag -width /boot/mbr -compact
+.It Pa /boot/mbr
+The default boot code
+.El
+.Sh SEE ALSO
+.Xr disklabel 8
+.Sh BUGS
+The default boot code will not necessarily handle all partition types
+correctly, in particular those introduced since MS-DOS 6.x.
+.Pp
+The entire program should be made more user-friendly.
+.Pp
+Throughout this man page, the term
+.Sq partition
+is used where it should actually be
+.Sq slice ,
+in order to conform with the terms used elsewhere.
+.Pp
+You cannot use this command to completely dedicate a disk to
+.Fx .
+The
+.Xr disklabel 8
+command must be used for this.
diff --git a/sbin/fdisk_pc98/fdisk.c b/sbin/fdisk_pc98/fdisk.c
new file mode 100644
index 0000000..b5d6cc7
--- /dev/null
+++ b/sbin/fdisk_pc98/fdisk.c
@@ -0,0 +1,861 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/diskpc98.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <err.h>
+#include <errno.h>
+#include <libgeom.h>
+#include <paths.h>
+#include <regex.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int iotest;
+
+#define LBUF 100
+static char lbuf[LBUF];
+
+/*
+ *
+ * Ported to 386bsd by Julian Elischer Thu Oct 15 20:26:46 PDT 1992
+ *
+ * 14-Dec-89 Robert Baron (rvb) at Carnegie-Mellon University
+ * Copyright (c) 1989 Robert. V. Baron
+ * Created.
+ */
+
+#define Decimal(str, ans, tmp) if (decimal(str, &tmp, ans)) ans = tmp
+#define String(str, ans, len) {char *z = ans; char **dflt = &z; if (string(str, dflt)) strncpy(ans, *dflt, len); }
+
+#define RoundCyl(x) ((((x) + cylsecs - 1) / cylsecs) * cylsecs)
+
+#define MAX_SEC_SIZE 2048 /* maximum section size that is supported */
+#define MIN_SEC_SIZE 512 /* the sector size to start sensing at */
+static int secsize = 0; /* the sensed sector size */
+
+static char *disk;
+
+static int cyls, sectors, heads, cylsecs, disksecs;
+
+struct mboot {
+ unsigned char padding[2]; /* force the longs to be long aligned */
+ unsigned char bootinst[510];
+ unsigned short int signature;
+ struct pc98_partition parts[8];
+ unsigned char large_sector_overflow[MAX_SEC_SIZE-MIN_SEC_SIZE];
+};
+
+static struct mboot mboot;
+static int fd;
+
+#define ACTIVE 0x80
+
+static uint dos_cyls;
+static uint dos_heads;
+static uint dos_sectors;
+static uint dos_cylsecs;
+
+#define MAX_ARGS 10
+
+typedef struct cmd {
+ char cmd;
+ int n_args;
+ struct arg {
+ char argtype;
+ int arg_val;
+ } args[MAX_ARGS];
+} CMD;
+
+static int B_flag = 0; /* replace boot code */
+static int a_flag = 0; /* set active partition */
+static int i_flag = 0; /* replace partition data */
+static int u_flag = 0; /* update partition data */
+static int s_flag = 0; /* Print a summary and exit */
+static int t_flag = 0; /* test only */
+static char *f_flag = NULL; /* Read config info from file */
+static int v_flag = 0; /* Be verbose */
+
+static struct part_type
+{
+ unsigned char type;
+ const char *name;
+} part_types[] = {
+ {0x00, "unused"}
+ ,{0x01, "Primary DOS with 12 bit FAT"}
+ ,{0x11, "MSDOS"}
+ ,{0x20, "MSDOS"}
+ ,{0x21, "MSDOS"}
+ ,{0x22, "MSDOS"}
+ ,{0x23, "MSDOS"}
+ ,{0x02, "XENIX / file system"}
+ ,{0x03, "XENIX /usr file system"}
+ ,{0x04, "PC-UX"}
+ ,{0x05, "Extended DOS"}
+ ,{0x06, "Primary 'big' DOS (> 32MB)"}
+ ,{0x07, "OS/2 HPFS, QNX or Advanced UNIX"}
+ ,{0x08, "AIX file system"}
+ ,{0x09, "AIX boot partition or Coherent"}
+ ,{0x0A, "OS/2 Boot Manager or OPUS"}
+ ,{0x10, "OPUS"}
+ ,{0x14, "FreeBSD/NetBSD/386BSD"}
+ ,{0x94, "FreeBSD/NetBSD/386BSD"}
+ ,{0x40, "VENIX 286"}
+ ,{0x50, "DM"}
+ ,{0x51, "DM"}
+ ,{0x52, "CP/M or Microport SysV/AT"}
+ ,{0x56, "GB"}
+ ,{0x61, "Speed"}
+ ,{0x63, "ISC UNIX, other System V/386, GNU HURD or Mach"}
+ ,{0x64, "Novell Netware 2.xx"}
+ ,{0x65, "Novell Netware 3.xx"}
+ ,{0x75, "PCIX"}
+ ,{0x40, "Minix"}
+};
+
+static void print_s0(int which);
+static void print_part(int i);
+static void init_sector0(unsigned long start);
+static void init_boot(void);
+static void change_part(int i);
+static void print_params(void);
+static void change_active(int which);
+static void change_code(void);
+static void get_params_to_use(void);
+static char *get_rootdisk(void);
+static void dos(u_int32_t start, u_int32_t size, struct pc98_partition *partp);
+static int open_disk(int flag);
+static ssize_t read_disk(off_t sector, void *buf);
+static int write_disk(off_t sector, void *buf);
+static int get_params(void);
+static int read_s0(void);
+static int write_s0(void);
+static int ok(const char *str);
+static int decimal(const char *str, int *num, int deflt);
+static const char *get_type(int type);
+static void usage(void);
+static int string(const char *str, char **ans);
+
+int
+main(int argc, char *argv[])
+{
+ struct stat sb;
+ int c, i;
+ int partition = -1;
+ struct pc98_partition *partp;
+
+ while ((c = getopt(argc, argv, "Ba:f:istuv12345678")) != -1)
+ switch (c) {
+ case 'B':
+ B_flag = 1;
+ break;
+ case 'a':
+ a_flag = 1;
+ break;
+ case 'f':
+ f_flag = optarg;
+ break;
+ case 'i':
+ i_flag = 1;
+ break;
+ case 's':
+ s_flag = 1;
+ break;
+ case 't':
+ t_flag = 1;
+ break;
+ case 'u':
+ u_flag = 1;
+ break;
+ case 'v':
+ v_flag = 1;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ partition = c - '0';
+ break;
+ default:
+ usage();
+ }
+ if (f_flag || i_flag)
+ u_flag = 1;
+ if (t_flag)
+ v_flag = 1;
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0) {
+ disk = get_rootdisk();
+ } else {
+ if (stat(argv[0], &sb) == 0) {
+ /* OK, full pathname given */
+ disk = argv[0];
+ } else if (errno == ENOENT && argv[0][0] != '/') {
+ /* Try prepending "/dev" */
+ asprintf(&disk, "%s%s", _PATH_DEV, argv[0]);
+ if (disk == NULL)
+ errx(1, "out of memory");
+ } else {
+ /* other stat error, let it fail below */
+ disk = argv[0];
+ }
+ }
+ if (open_disk(u_flag) < 0)
+ err(1, "cannot open disk %s", disk);
+
+ if (s_flag) {
+ if (read_s0())
+ err(1, "read_s0");
+ printf("%s: %d cyl %d hd %d sec\n", disk, dos_cyls, dos_heads,
+ dos_sectors);
+ printf("Part %11s %11s SID\n", "Start", "Size");
+ for (i = 0; i < NDOSPART; i++) {
+ partp = ((struct pc98_partition *) &mboot.parts) + i;
+ if (partp->dp_sid == 0)
+ continue;
+ printf("%4d: %11u %11u 0x%02x\n", i + 1,
+ partp->dp_scyl * cylsecs,
+ (partp->dp_ecyl - partp->dp_scyl + 1) * cylsecs,
+ partp->dp_sid);
+ }
+ exit(0);
+ }
+
+ printf("******* Working on device %s *******\n",disk);
+
+ if (f_flag) {
+ if (v_flag)
+ print_s0(-1);
+ if (!t_flag)
+ write_s0();
+ } else {
+ if(u_flag)
+ get_params_to_use();
+ else
+ print_params();
+
+ if (read_s0())
+ init_sector0(dos_sectors);
+
+ printf("Media sector size is %d\n", secsize);
+ printf("Warning: BIOS sector numbering starts with sector 1\n");
+ printf("Information from DOS bootblock is:\n");
+ if (partition == -1)
+ for (i = 1; i <= NDOSPART; i++)
+ change_part(i);
+ else
+ change_part(partition);
+
+ if (u_flag || a_flag)
+ change_active(partition);
+
+ if (B_flag)
+ change_code();
+
+ if (u_flag || a_flag || B_flag) {
+ if (!t_flag) {
+ printf("\nWe haven't changed the partition table yet. ");
+ printf("This is your last chance.\n");
+ }
+ print_s0(-1);
+ if (!t_flag) {
+ if (ok("Should we write new partition table?"))
+ write_s0();
+ } else {
+ printf("\n-t flag specified -- partition table not written.\n");
+ }
+ }
+ }
+
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s%s",
+ "usage: fdisk [-Baistu] [-12345678] [disk]\n",
+ " fdisk -f configfile [-itv] [disk]\n");
+ exit(1);
+}
+
+static void
+print_s0(int which)
+{
+ int i;
+
+ print_params();
+ printf("Information from DOS bootblock is:\n");
+ if (which == -1)
+ for (i = 1; i <= NDOSPART; i++)
+ printf("%d: ", i), print_part(i);
+ else
+ print_part(which);
+}
+
+static struct pc98_partition mtpart;
+
+static void
+print_part(int i)
+{
+ struct pc98_partition *partp;
+ u_int64_t part_sz, part_mb;
+
+ partp = ((struct pc98_partition *) &mboot.parts) + i - 1;
+
+ if (!bcmp(partp, &mtpart, sizeof (struct pc98_partition))) {
+ printf("<UNUSED>\n");
+ return;
+ }
+ /*
+ * Be careful not to overflow.
+ */
+ part_sz = (partp->dp_ecyl - partp->dp_scyl + 1) * cylsecs;
+ part_mb = part_sz * secsize;
+ part_mb /= (1024 * 1024);
+ printf("sysmid %d (%#04x),(%s)\n", partp->dp_mid, partp->dp_mid,
+ get_type(partp->dp_mid));
+ printf(" start %lu, size %lu (%ju Meg), sid %d\n",
+ (u_long)(partp->dp_scyl * cylsecs), (u_long)part_sz,
+ (uintmax_t)part_mb, partp->dp_sid);
+ printf("\tbeg: cyl %d/ head %d/ sector %d;\n\tend: cyl %d/ head %d/ sector %d\n"
+ ,partp->dp_scyl
+ ,partp->dp_shd
+ ,partp->dp_ssect
+ ,partp->dp_ecyl
+ ,partp->dp_ehd
+ ,partp->dp_esect);
+ printf ("\tsystem Name %.16s\n", partp->dp_name);
+}
+
+
+static void
+init_boot(void)
+{
+
+ mboot.signature = DOSMAGIC;
+}
+
+
+static void
+init_sector0(unsigned long start)
+{
+ struct pc98_partition *partp =
+ (struct pc98_partition *)(&mboot.parts[0]);
+
+ init_boot();
+
+ partp->dp_mid = DOSMID_386BSD;
+ partp->dp_sid = DOSSID_386BSD;
+
+ dos(start, disksecs - start, partp);
+}
+
+static void
+change_part(int i)
+{
+ struct pc98_partition *partp =
+ ((struct pc98_partition *) &mboot.parts) + i - 1;
+
+ printf("The data for partition %d is:\n", i);
+ print_part(i);
+
+ if (u_flag && ok("Do you want to change it?")) {
+ int tmp;
+
+ if (i_flag) {
+ bzero((char *)partp, sizeof (struct pc98_partition));
+ if (i == 1) {
+ init_sector0(1);
+ printf("\nThe static data for the slice 1 has been reinitialized to:\n");
+ print_part(i);
+ }
+ }
+
+ do {
+ int x_start = partp->dp_scyl * cylsecs ;
+ int x_size = (partp->dp_ecyl - partp->dp_scyl + 1) * cylsecs;
+ Decimal("sysmid", partp->dp_mid, tmp);
+ Decimal("syssid", partp->dp_sid, tmp);
+ String ("system name", partp->dp_name, 16);
+ Decimal("start", x_start, tmp);
+ Decimal("size", x_size, tmp);
+
+ if (ok("Explicitly specify beg/end address ?"))
+ {
+ int tsec,tcyl,thd;
+ tcyl = partp->dp_scyl;
+ thd = partp->dp_shd;
+ tsec = partp->dp_ssect;
+ Decimal("beginning cylinder", tcyl, tmp);
+ Decimal("beginning head", thd, tmp);
+ Decimal("beginning sector", tsec, tmp);
+ partp->dp_scyl = tcyl;
+ partp->dp_ssect = tsec;
+ partp->dp_shd = thd;
+ partp->dp_ipl_cyl = partp->dp_scyl;
+ partp->dp_ipl_sct = partp->dp_ssect;
+ partp->dp_ipl_head = partp->dp_shd;
+
+ tcyl = partp->dp_ecyl;
+ thd = partp->dp_ehd;
+ tsec = partp->dp_esect;
+ Decimal("ending cylinder", tcyl, tmp);
+ Decimal("ending head", thd, tmp);
+ Decimal("ending sector", tsec, tmp);
+ partp->dp_ecyl = tcyl;
+ partp->dp_esect = tsec;
+ partp->dp_ehd = thd;
+ } else
+ dos(x_start, x_size, partp);
+
+ print_part(i);
+ } while (!ok("Are we happy with this entry?"));
+ }
+}
+
+static void
+print_params()
+{
+ printf("parameters extracted from in-core disklabel are:\n");
+ printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
+ ,cyls,heads,sectors,cylsecs);
+ if (dos_cyls > 65535 || dos_heads > 255 || dos_sectors > 255)
+ printf("Figures below won't work with BIOS for partitions not in cyl 1\n");
+ printf("parameters to be used for BIOS calculations are:\n");
+ printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
+ ,dos_cyls,dos_heads,dos_sectors,dos_cylsecs);
+}
+
+static void
+change_active(int which)
+{
+ struct pc98_partition *partp = &mboot.parts[0];
+ int active, i, new, tmp;
+
+ active = -1;
+ for (i = 0; i < NDOSPART; i++) {
+ if ((partp[i].dp_sid & ACTIVE) == 0)
+ continue;
+ printf("Partition %d is marked active\n", i + 1);
+ if (active == -1)
+ active = i + 1;
+ }
+ if (a_flag && which != -1)
+ active = which;
+ else if (active == -1)
+ active = 1;
+
+ if (!ok("Do you want to change the active partition?"))
+ return;
+setactive:
+ do {
+ new = active;
+ Decimal("active partition", new, tmp);
+ if (new < 1 || new > 8) {
+ printf("Active partition number must be in range 1-8."
+ " Try again.\n");
+ goto setactive;
+ }
+ active = new;
+ } while (!ok("Are you happy with this choice"));
+ if (active > 0 && active <= 8)
+ partp[active-1].dp_sid |= ACTIVE;
+}
+
+static void
+change_code()
+{
+ if (ok("Do you want to change the boot code?"))
+ init_boot();
+}
+
+void
+get_params_to_use()
+{
+ int tmp;
+ print_params();
+ if (ok("Do you want to change our idea of what BIOS thinks ?"))
+ {
+ do
+ {
+ Decimal("BIOS's idea of #cylinders", dos_cyls, tmp);
+ Decimal("BIOS's idea of #heads", dos_heads, tmp);
+ Decimal("BIOS's idea of #sectors", dos_sectors, tmp);
+ dos_cylsecs = dos_heads * dos_sectors;
+ print_params();
+ }
+ while(!ok("Are you happy with this choice"));
+ }
+}
+
+
+/***********************************************\
+* Change real numbers into strange dos numbers *
+\***********************************************/
+static void
+dos(u_int32_t start, u_int32_t size, struct pc98_partition *partp)
+{
+ u_int32_t end;
+
+ if (partp->dp_mid == 0 && partp->dp_sid == 0 &&
+ start == 0 && size == 0) {
+ memcpy(partp, &mtpart, sizeof(*partp));
+ return;
+ }
+
+ /* Start c/h/s. */
+ partp->dp_scyl = partp->dp_ipl_cyl = start / dos_cylsecs;
+ partp->dp_shd = partp->dp_ipl_head = start % dos_cylsecs / dos_sectors;
+ partp->dp_ssect = partp->dp_ipl_sct = start % dos_sectors;
+
+ /* End c/h/s. */
+ end = start + size - cylsecs;
+ partp->dp_ecyl = end / dos_cylsecs;
+ partp->dp_ehd = end % dos_cylsecs / dos_sectors;
+ partp->dp_esect = end % dos_sectors;
+}
+
+static int
+open_disk(int flag)
+{
+ struct stat st;
+ int rwmode;
+
+ if (stat(disk, &st) == -1) {
+ if (errno == ENOENT)
+ return -2;
+ warnx("can't get file status of %s", disk);
+ return -1;
+ }
+ if ( !(st.st_mode & S_IFCHR) )
+ warnx("device %s is not character special", disk);
+ rwmode = a_flag || B_flag || flag ? O_RDWR : O_RDONLY;
+ fd = open(disk, rwmode);
+ if (fd == -1 && errno == EPERM && rwmode == O_RDWR)
+ fd = open(disk, O_RDONLY);
+ if (fd == -1 && errno == ENXIO)
+ return -2;
+ if (fd == -1) {
+ warnx("can't open device %s", disk);
+ return -1;
+ }
+ if (get_params() == -1) {
+ warnx("can't get disk parameters on %s", disk);
+ return -1;
+ }
+ return fd;
+}
+
+static ssize_t
+read_disk(off_t sector, void *buf)
+{
+
+ lseek(fd, (sector * 512), 0);
+ return read(fd, buf,
+ secsize > MIN_SEC_SIZE ? secsize : MIN_SEC_SIZE * 2);
+}
+
+static int
+write_disk(off_t sector, void *buf)
+{
+ int error;
+ struct gctl_req *grq;
+ const char *q;
+ char fbuf[BUFSIZ];
+ int i, fdw;
+
+ grq = gctl_get_handle();
+ gctl_ro_param(grq, "verb", -1, "write PC98");
+ gctl_ro_param(grq, "class", -1, "PC98");
+ q = strrchr(disk, '/');
+ if (q == NULL)
+ q = disk;
+ else
+ q++;
+ gctl_ro_param(grq, "geom", -1, q);
+ gctl_ro_param(grq, "data", secsize, buf);
+ q = gctl_issue(grq);
+ if (q == NULL) {
+ gctl_free(grq);
+ return(0);
+ }
+ warnx("%s", q);
+ gctl_free(grq);
+
+ error = pwrite(fd, buf, secsize, (sector * 512));
+ if (error == secsize)
+ return (0);
+
+ for (i = 0; i < NDOSPART; i++) {
+ sprintf(fbuf, "%ss%d", disk, i + 1);
+ fdw = open(fbuf, O_RDWR, 0);
+ if (fdw < 0)
+ continue;
+ error = ioctl(fdw, DIOCSPC98, buf);
+ close(fdw);
+ if (error == 0)
+ return (0);
+ }
+ warnx("Failed to write sector zero");
+ return(EINVAL);
+}
+
+static int
+get_params()
+{
+ int error;
+ u_int u;
+ off_t o;
+
+ error = ioctl(fd, DIOCGFWSECTORS, &u);
+ if (error == 0)
+ sectors = dos_sectors = u;
+ else
+ sectors = dos_sectors = 17;
+
+ error = ioctl(fd, DIOCGFWHEADS, &u);
+ if (error == 0)
+ heads = dos_heads = u;
+ else
+ heads = dos_heads = 8;
+
+ dos_cylsecs = cylsecs = heads * sectors;
+ disksecs = cyls * heads * sectors;
+
+ error = ioctl(fd, DIOCGSECTORSIZE, &u);
+ if (error != 0 || u == 0)
+ u = 512;
+ secsize = u;
+
+ error = ioctl(fd, DIOCGMEDIASIZE, &o);
+ if (error == 0) {
+ disksecs = o / u;
+ cyls = dos_cyls = o / (u * dos_heads * dos_sectors);
+ }
+
+ return (disksecs);
+}
+
+
+static int
+read_s0()
+{
+
+ if (read_disk(0, (char *) mboot.bootinst) == -1) {
+ warnx("can't read fdisk partition table");
+ return -1;
+ }
+ if (mboot.signature != DOSMAGIC) {
+ warnx("invalid fdisk partition table found");
+ /* So should we initialize things */
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+write_s0()
+{
+
+ if (iotest) {
+ print_s0(-1);
+ return 0;
+ }
+
+ /*
+ * write enable label sector before write (if necessary),
+ * disable after writing.
+ * needed if the disklabel protected area also protects
+ * sector 0. (e.g. empty disk)
+ */
+ if (write_disk(0, (char *) mboot.bootinst) == -1) {
+ warn("can't write fdisk partition table");
+ return -1;
+ }
+
+ return(0);
+}
+
+
+static int
+ok(const char *str)
+{
+ printf("%s [n] ", str);
+ fflush(stdout);
+ if (fgets(lbuf, LBUF, stdin) == NULL)
+ exit(1);
+ lbuf[strlen(lbuf)-1] = 0;
+
+ if (*lbuf &&
+ (!strcmp(lbuf, "yes") || !strcmp(lbuf, "YES") ||
+ !strcmp(lbuf, "y") || !strcmp(lbuf, "Y")))
+ return 1;
+ else
+ return 0;
+}
+
+static int
+decimal(const char *str, int *num, int deflt)
+{
+ int acc = 0, c;
+ char *cp;
+
+ while (1) {
+ printf("Supply a decimal value for \"%s\" [%d] ", str, deflt);
+ fflush(stdout);
+ if (fgets(lbuf, LBUF, stdin) == NULL)
+ exit(1);
+ lbuf[strlen(lbuf)-1] = 0;
+
+ if (!*lbuf)
+ return 0;
+
+ cp = lbuf;
+ while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+ if (!c)
+ return 0;
+ while ((c = *cp++)) {
+ if (c <= '9' && c >= '0')
+ acc = acc * 10 + c - '0';
+ else
+ break;
+ }
+ if (c == ' ' || c == '\t')
+ while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+ if (!c) {
+ *num = acc;
+ return 1;
+ } else
+ printf("%s is an invalid decimal number. Try again.\n",
+ lbuf);
+ }
+
+}
+
+static int
+string(const char *str, char **ans)
+{
+ int i, c;
+ char *cp = lbuf;
+
+ while (1) {
+ printf("Supply a string value for \"%s\" [%s] ", str, *ans);
+ fgets(lbuf, LBUF, stdin);
+ lbuf[strlen(lbuf)-1] = 0;
+
+ if (!*lbuf)
+ return 0;
+
+ while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+ if (c == '"') {
+ c = *++cp;
+ *ans = cp;
+ while ((c = *cp) && c != '"') cp++;
+ } else {
+ *ans = cp;
+ while ((c = *cp) && c != ' ' && c != '\t') cp++;
+ }
+
+ for (i = strlen(*ans); i < 16; i++)
+ (*ans)[i] = ' ';
+ (*ans)[16] = 0;
+
+ return 1;
+ }
+}
+
+static const char *
+get_type(int type)
+{
+ int numentries = (sizeof(part_types)/sizeof(struct part_type));
+ int counter = 0;
+ struct part_type *ptr = part_types;
+
+
+ while(counter < numentries) {
+ if(ptr->type == (type & 0x7f))
+ return(ptr->name);
+ ptr++;
+ counter++;
+ }
+ return("unknown");
+}
+
+/*
+ * Try figuring out the root device's canonical disk name.
+ * The following choices are considered:
+ * /dev/ad0s1a => /dev/ad0
+ * /dev/da0a => /dev/da0
+ * /dev/vinum/root => /dev/vinum/root
+ */
+static char *
+get_rootdisk(void)
+{
+ struct statfs rootfs;
+ regex_t re;
+#define NMATCHES 2
+ regmatch_t rm[NMATCHES];
+ char *s;
+ int rv;
+
+ if (statfs("/", &rootfs) == -1)
+ err(1, "statfs(\"/\")");
+
+ if ((rv = regcomp(&re, "^(/dev/[a-z]+[0-9]+)([sp][0-9]+)?[a-h]?$",
+ REG_EXTENDED)) != 0)
+ errx(1, "regcomp() failed (%d)", rv);
+ if ((rv = regexec(&re, rootfs.f_mntfromname, NMATCHES, rm, 0)) != 0)
+ errx(1,
+"mounted root fs resource doesn't match expectations (regexec returned %d)",
+ rv);
+ if ((s = malloc(rm[1].rm_eo - rm[1].rm_so + 1)) == NULL)
+ errx(1, "out of memory");
+ memcpy(s, rootfs.f_mntfromname + rm[1].rm_so,
+ rm[1].rm_eo - rm[1].rm_so);
+ s[rm[1].rm_eo - rm[1].rm_so] = 0;
+
+ return s;
+}
diff --git a/sbin/ffsinfo/Makefile b/sbin/ffsinfo/Makefile
new file mode 100644
index 0000000..a1ffef7
--- /dev/null
+++ b/sbin/ffsinfo/Makefile
@@ -0,0 +1,19 @@
+# @(#)Makefile 8.8 (Berkeley) 6/21/2000
+#
+# $TSHeader: src/sbin/ffsinfo/Makefile,v 1.3 2000/12/05 19:45:10 tomsoft Exp $
+# $FreeBSD$
+#
+
+GROWFS= ${.CURDIR}/../growfs
+.PATH: ${GROWFS}
+
+PROG= ffsinfo
+SRCS= ffsinfo.c debug.c
+MAN= ffsinfo.8
+
+WARNS?= 0
+CFLAGS+=-DFS_DEBUG -I${GROWFS}
+DPADD= ${LIBUFS}
+LDADD= -lufs
+
+.include <bsd.prog.mk>
diff --git a/sbin/ffsinfo/ffsinfo.8 b/sbin/ffsinfo/ffsinfo.8
new file mode 100644
index 0000000..ff5afe4
--- /dev/null
+++ b/sbin/ffsinfo/ffsinfo.8
@@ -0,0 +1,145 @@
+.\" Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz
+.\" Copyright (c) 1980, 1989, 1993 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt.
+.\"
+.\" 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 the University of
+.\" California, Berkeley and its contributors, as well as Christoph
+.\" Herrmann and Thomas-Henning von Kamptz.
+.\" 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 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.
+.\"
+.\" $TSHeader: src/sbin/ffsinfo/ffsinfo.8,v 1.3 2000/12/12 19:30:55 tomsoft Exp $
+.\" $FreeBSD$
+.\"
+.Dd September 8, 2000
+.Dt FSINFO 8
+.Os
+.Sh NAME
+.Nm ffsinfo
+.Nd "dump all meta information of an existing ufs file system"
+.Sh SYNOPSIS
+.Nm
+.Op Fl g Ar cylinder_group
+.Op Fl i Ar inode
+.Op Fl l Ar level
+.Op Fl o Ar outfile
+.Ar special | file
+.Sh DESCRIPTION
+The
+.Nm
+utility extends the
+.Xr dumpfs 8
+utility.
+.Pp
+The output is appended to the file
+.Pa outfile .
+Also expect the output file to be rather large.
+Up to 2 percent of the size of the specified file system is not uncommon.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl g Ar cylinder_group
+This restricts the dump to information about this cylinder group only.
+Here
+.Ar 0
+means the first cylinder group and
+.Ar -1
+the last one.
+.It Fl i Ar inode
+This restricts the dump to information about this particular inode only.
+Here the minimum acceptable inode is
+.Ar 2 .
+If this option is omitted but a cylinder group is defined then only inodes
+within that cylinder group are dumped.
+.It Fl l Ar level
+The level of detail which will be dumped.
+This value defaults to
+.Ar 255
+and is the
+.Dq bitwise or
+of the following table:
+.Pp
+.Bl -hang -width indent -compact
+.It Ar 0x001
+initial superblock
+.It Ar 0x002
+superblock copies in each cylinder group
+.It Ar 0x004
+cylinder group summary in initial cylinder group
+.It Ar 0x008
+cylinder group information
+.It Ar 0x010
+inode allocation bitmap
+.It Ar 0x020
+fragment allocation bitmap
+.It Ar 0x040
+cluster maps and summary
+.It Ar 0x100
+inode information
+.It Ar 0x200
+indirect block dump
+.El
+.It Fl o Ar outfile
+This sets the output filename where the dump is written to, and
+must be specified.
+If
+.Fl
+is provided, output will be sent to stdout.
+.El
+.Sh EXAMPLES
+.Dl ffsinfo -o /var/tmp/ffsinfo -l 1023 /dev/vinum/testvol
+.Pp
+will dump
+.Pa /dev/vinum/testvol
+to
+.Pa /var/tmp/ffsinfo
+with all available information.
+.Sh SEE ALSO
+.Xr disklabel 8 ,
+.Xr dumpfs 8 ,
+.Xr fsck 8 ,
+.Xr growfs 8 ,
+.Xr newfs 8 ,
+.Xr tunefs 8 ,
+.Xr vinum 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 4.4 .
+.Sh AUTHORS
+.An Christoph Herrmann Aq chm@FreeBSD.org
+.An Thomas-Henning von Kamptz Aq tomsoft@FreeBSD.org
+.An The GROWFS team Aq growfs@Tomsoft.COM
+.Sh BUGS
+Snapshots are handled like plain files.
+They should get their own level to provide for independent control of the
+amount of what gets dumped.
+It probably also makes sense to some extend to dump the snapshot as a
+file system.
diff --git a/sbin/ffsinfo/ffsinfo.c b/sbin/ffsinfo/ffsinfo.c
new file mode 100644
index 0000000..8df4b41
--- /dev/null
+++ b/sbin/ffsinfo/ffsinfo.c
@@ -0,0 +1,675 @@
+/*
+ * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz
+ * Copyright (c) 1980, 1989, 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt.
+ *
+ * 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 the University of
+ * California, Berkeley and its contributors, as well as Christoph
+ * Herrmann and Thomas-Henning von Kamptz.
+ * 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 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.
+ *
+ * $TSHeader: src/sbin/ffsinfo/ffsinfo.c,v 1.4 2000/12/12 19:30:55 tomsoft Exp $
+ *
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz\n\
+Copyright (c) 1980, 1989, 1993 The Regents of the University of California.\n\
+All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/* ********************************************************** INCLUDES ***** */
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libufs.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+
+/* *********************************************************** GLOBALS ***** */
+#ifdef FS_DEBUG
+int _dbg_lvl_ = (DL_INFO); /* DL_TRC */
+#endif /* FS_DEBUG */
+
+struct uufsd disk;
+
+#define sblock disk.d_fs
+#define acg disk.d_cg
+
+static union {
+ struct fs fs;
+ char pad[SBLOCKSIZE];
+} fsun;
+
+#define osblock fsun.fs
+
+static char i1blk[MAXBSIZE];
+static char i2blk[MAXBSIZE];
+static char i3blk[MAXBSIZE];
+
+static struct csum *fscs;
+
+/* ******************************************************** PROTOTYPES ***** */
+static void usage(void);
+static void dump_whole_ufs1_inode(ino_t, int);
+static void dump_whole_ufs2_inode(ino_t, int);
+
+#define DUMP_WHOLE_INODE(A,B) \
+ ( disk.d_ufs == 1 \
+ ? dump_whole_ufs1_inode((A),(B)) : dump_whole_ufs2_inode((A),(B)) )
+
+/* ************************************************************** main ***** */
+/*
+ * ffsinfo(8) is a tool to dump all metadata of a file system. It helps to find
+ * errors is the file system much easier. You can run ffsinfo before and after
+ * an fsck(8), and compare the two ascii dumps easy with diff, and you see
+ * directly where the problem is. You can control how much detail you want to
+ * see with some command line arguments. You can also easy check the status
+ * of a file system, like is there is enough space for growing a file system,
+ * or how many active snapshots do we have. It provides much more detailed
+ * information then dumpfs. Snapshots, as they are very new, are not really
+ * supported. They are just mentioned currently, but it is planned to run
+ * also over active snapshots, to even get that output.
+ */
+int
+main(int argc, char **argv)
+{
+ DBG_FUNC("main")
+ char *device, *special;
+ int ch;
+ size_t len;
+ struct stat st;
+ struct csum *dbg_csp;
+ int dbg_csc;
+ char dbg_line[80];
+ int cylno,i;
+ int cfg_cg, cfg_in, cfg_lv;
+ int cg_start, cg_stop;
+ ino_t in;
+ char *out_file;
+
+ DBG_ENTER;
+
+ cfg_lv=0xff;
+ cfg_in=-2;
+ cfg_cg=-2;
+ out_file=NULL;
+
+ while ((ch=getopt(argc, argv, "g:i:l:o:")) != -1) {
+ switch(ch) {
+ case 'g':
+ cfg_cg=strtol(optarg, NULL, 0);
+ if(errno == EINVAL||errno == ERANGE)
+ err(1, "%s", optarg);
+ if(cfg_cg < -1) {
+ usage();
+ }
+ break;
+ case 'i':
+ cfg_in=strtol(optarg, NULL, 0);
+ if(errno == EINVAL||errno == ERANGE)
+ err(1, "%s", optarg);
+ if(cfg_in < 0) {
+ usage();
+ }
+ break;
+ case 'l':
+ cfg_lv=strtol(optarg, NULL, 0);
+ if(errno == EINVAL||errno == ERANGE)
+ err(1, "%s", optarg);
+ if(cfg_lv < 0x1||cfg_lv > 0x3ff) {
+ usage();
+ }
+ break;
+ case 'o':
+ free(out_file);
+ out_file=strdup(optarg);
+ if(out_file == NULL) {
+ errx(1, "strdup failed");
+ }
+ break;
+ case '?':
+ /* FALLTHROUGH */
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if(argc != 1) {
+ usage();
+ }
+ device=*argv;
+ if (out_file == NULL)
+ errx(1, "out_file not specified");
+
+ /*
+ * Now we try to guess the (raw)device name.
+ */
+ if (0 == strrchr(device, '/') && (stat(device, &st) == -1)) {
+ /*
+ * No path prefix was given, so try in that order:
+ * /dev/r%s
+ * /dev/%s
+ * /dev/vinum/r%s
+ * /dev/vinum/%s.
+ *
+ * FreeBSD now doesn't distinguish between raw and block
+ * devices any longer, but it should still work this way.
+ */
+ len=strlen(device)+strlen(_PATH_DEV)+2+strlen("vinum/");
+ special=(char *)malloc(len);
+ if(special == NULL) {
+ errx(1, "malloc failed");
+ }
+ snprintf(special, len, "%sr%s", _PATH_DEV, device);
+ if (stat(special, &st) == -1) {
+ snprintf(special, len, "%s%s", _PATH_DEV, device);
+ if (stat(special, &st) == -1) {
+ snprintf(special, len, "%svinum/r%s",
+ _PATH_DEV, device);
+ if (stat(special, &st) == -1) {
+ /*
+ * For now this is the 'last resort'.
+ */
+ snprintf(special, len, "%svinum/%s",
+ _PATH_DEV, device);
+ }
+ }
+ }
+ device = special;
+ }
+
+ if (ufs_disk_fillout(&disk, device) == -1)
+ err(1, "ufs_disk_fillout(%s) failed: %s", device, disk.d_error);
+
+ DBG_OPEN(out_file); /* already here we need a superblock */
+
+ if(cfg_lv & 0x001) {
+ DBG_DUMP_FS(&sblock,
+ "primary sblock");
+ }
+
+ /*
+ * Determine here what cylinder groups to dump.
+ */
+ if(cfg_cg==-2) {
+ cg_start=0;
+ cg_stop=sblock.fs_ncg;
+ } else if (cfg_cg==-1) {
+ cg_start=sblock.fs_ncg-1;
+ cg_stop=sblock.fs_ncg;
+ } else if (cfg_cg<sblock.fs_ncg) {
+ cg_start=cfg_cg;
+ cg_stop=cfg_cg+1;
+ } else {
+ cg_start=sblock.fs_ncg;
+ cg_stop=sblock.fs_ncg;
+ }
+
+ if (cfg_lv & 0x004) {
+ fscs = (struct csum *)calloc((size_t)1,
+ (size_t)sblock.fs_cssize);
+ if(fscs == NULL) {
+ errx(1, "calloc failed");
+ }
+
+ /*
+ * Get the cylinder summary into the memory ...
+ */
+ for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) {
+ if (bread(&disk,
+ fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)),
+ (void *)(((char *)fscs)+i),
+ (size_t)(sblock.fs_cssize-i < sblock.fs_bsize
+ ? sblock.fs_cssize - i
+ : sblock.fs_bsize)) == -1) {
+ err(1, "bread: %s", disk.d_error);
+ }
+ }
+
+ dbg_csp=fscs;
+ /*
+ * ... and dump it.
+ */
+ for(dbg_csc=0; dbg_csc<sblock.fs_ncg; dbg_csc++) {
+ snprintf(dbg_line, sizeof(dbg_line),
+ "%d. csum in fscs", dbg_csc);
+ DBG_DUMP_CSUM(&sblock,
+ dbg_line,
+ dbg_csp++);
+ }
+ }
+
+ /*
+ * For each requested cylinder group ...
+ */
+ for(cylno=cg_start; cylno<cg_stop; cylno++) {
+ snprintf(dbg_line, sizeof(dbg_line), "cgr %d", cylno);
+ if(cfg_lv & 0x002) {
+ /*
+ * ... dump the superblock copies ...
+ */
+ if (bread(&disk, fsbtodb(&sblock, cgsblock(&sblock, cylno)),
+ (void *)&osblock, SBLOCKSIZE) == -1) {
+ err(1, "bread: %s", disk.d_error);
+ }
+ DBG_DUMP_FS(&osblock,
+ dbg_line);
+ }
+ /*
+ * ... read the cylinder group and dump whatever was requested.
+ */
+ if (bread(&disk, fsbtodb(&sblock, cgtod(&sblock, cylno)),
+ (void *)&acg, (size_t)sblock.fs_cgsize) == -1) {
+ err(1, "bread: %s", disk.d_error);
+ }
+ if(cfg_lv & 0x008) {
+ DBG_DUMP_CG(&sblock,
+ dbg_line,
+ &acg);
+ }
+ if(cfg_lv & 0x010) {
+ DBG_DUMP_INMAP(&sblock,
+ dbg_line,
+ &acg);
+ }
+ if(cfg_lv & 0x020) {
+ DBG_DUMP_FRMAP(&sblock,
+ dbg_line,
+ &acg);
+ }
+ if(cfg_lv & 0x040) {
+ DBG_DUMP_CLMAP(&sblock,
+ dbg_line,
+ &acg);
+ DBG_DUMP_CLSUM(&sblock,
+ dbg_line,
+ &acg);
+ }
+#ifdef NOT_CURRENTLY
+ /*
+ * See the comment in sbin/growfs/debug.c for why this
+ * is currently disabled, and what needs to be done to
+ * re-enable it.
+ */
+ if(disk.d_ufs == 1 && cfg_lv & 0x080) {
+ DBG_DUMP_SPTBL(&sblock,
+ dbg_line,
+ &acg);
+ }
+#endif
+ }
+ /*
+ * Dump the requested inode(s).
+ */
+ if(cfg_in != -2) {
+ DUMP_WHOLE_INODE((ino_t)cfg_in, cfg_lv);
+ } else {
+ for(in=cg_start*sblock.fs_ipg; in<(ino_t)cg_stop*sblock.fs_ipg;
+ in++) {
+ DUMP_WHOLE_INODE(in, cfg_lv);
+ }
+ }
+
+ DBG_CLOSE;
+
+ DBG_LEAVE;
+ return 0;
+}
+
+/* ********************************************** dump_whole_ufs1_inode ***** */
+/*
+ * Here we dump a list of all blocks allocated by this inode. We follow
+ * all indirect blocks.
+ */
+void
+dump_whole_ufs1_inode(ino_t inode, int level)
+{
+ DBG_FUNC("dump_whole_ufs1_inode")
+ struct ufs1_dinode *ino;
+ int rb, mode;
+ unsigned int ind2ctr, ind3ctr;
+ ufs1_daddr_t *ind2ptr, *ind3ptr;
+ char comment[80];
+
+ DBG_ENTER;
+
+ /*
+ * Read the inode from disk/cache.
+ */
+ if (getino(&disk, (void **)&ino, inode, &mode) == -1)
+ err(1, "getino: %s", disk.d_error);
+
+ if(ino->di_nlink==0) {
+ DBG_LEAVE;
+ return; /* inode not in use */
+ }
+
+ /*
+ * Dump the main inode structure.
+ */
+ snprintf(comment, sizeof(comment), "Inode 0x%08x", inode);
+ if (level & 0x100) {
+ DBG_DUMP_INO(&sblock,
+ comment,
+ ino);
+ }
+
+ if (!(level & 0x200)) {
+ DBG_LEAVE;
+ return;
+ }
+
+ /*
+ * Ok, now prepare for dumping all direct and indirect pointers.
+ */
+ rb=howmany(ino->di_size, sblock.fs_bsize)-NDADDR;
+ if(rb>0) {
+ /*
+ * Dump single indirect block.
+ */
+ if (bread(&disk, fsbtodb(&sblock, ino->di_ib[0]), (void *)&i1blk,
+ (size_t)sblock.fs_bsize) == -1) {
+ err(1, "bread: %s", disk.d_error);
+ }
+ snprintf(comment, sizeof(comment), "Inode 0x%08x: indirect 0",
+ inode);
+ DBG_DUMP_IBLK(&sblock,
+ comment,
+ i1blk,
+ (size_t)rb);
+ rb-=howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t));
+ }
+ if(rb>0) {
+ /*
+ * Dump double indirect blocks.
+ */
+ if (bread(&disk, fsbtodb(&sblock, ino->di_ib[1]), (void *)&i2blk,
+ (size_t)sblock.fs_bsize) == -1) {
+ err(1, "bread: %s", disk.d_error);
+ }
+ snprintf(comment, sizeof(comment), "Inode 0x%08x: indirect 1",
+ inode);
+ DBG_DUMP_IBLK(&sblock,
+ comment,
+ i2blk,
+ howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))));
+ for(ind2ctr=0; ((ind2ctr < howmany(sblock.fs_bsize,
+ sizeof(ufs1_daddr_t))) && (rb>0)); ind2ctr++) {
+ ind2ptr=&((ufs1_daddr_t *)(void *)&i2blk)[ind2ctr];
+
+ if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk,
+ (size_t)sblock.fs_bsize) == -1) {
+ err(1, "bread: %s", disk.d_error);
+ }
+ snprintf(comment, sizeof(comment),
+ "Inode 0x%08x: indirect 1->%d", inode, ind2ctr);
+ DBG_DUMP_IBLK(&sblock,
+ comment,
+ i1blk,
+ (size_t)rb);
+ rb-=howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t));
+ }
+ }
+ if(rb>0) {
+ /*
+ * Dump triple indirect blocks.
+ */
+ if (bread(&disk, fsbtodb(&sblock, ino->di_ib[2]), (void *)&i3blk,
+ (size_t)sblock.fs_bsize) == -1) {
+ err(1, "bread: %s", disk.d_error);
+ }
+ snprintf(comment, sizeof(comment), "Inode 0x%08x: indirect 2",
+ inode);
+#define SQUARE(a) ((a)*(a))
+ DBG_DUMP_IBLK(&sblock,
+ comment,
+ i3blk,
+ howmany(rb,
+ SQUARE(howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)))));
+#undef SQUARE
+ for(ind3ctr=0; ((ind3ctr<howmany(sblock.fs_bsize,
+ sizeof(ufs1_daddr_t)))&&(rb>0)); ind3ctr++) {
+ ind3ptr=&((ufs1_daddr_t *)(void *)&i3blk)[ind3ctr];
+
+ if (bread(&disk, fsbtodb(&sblock, *ind3ptr), (void *)&i2blk,
+ (size_t)sblock.fs_bsize) == -1) {
+ err(1, "bread: %s", disk.d_error);
+ }
+ snprintf(comment, sizeof(comment),
+ "Inode 0x%08x: indirect 2->%d", inode, ind3ctr);
+ DBG_DUMP_IBLK(&sblock,
+ comment,
+ i2blk,
+ howmany(rb,
+ howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))));
+ for(ind2ctr=0; ((ind2ctr < howmany(sblock.fs_bsize,
+ sizeof(ufs1_daddr_t)))&&(rb>0)); ind2ctr++) {
+ ind2ptr=&((ufs1_daddr_t *)(void *)&i2blk)
+ [ind2ctr];
+ if (bread(&disk, fsbtodb(&sblock, *ind2ptr),
+ (void *)&i1blk, (size_t)sblock.fs_bsize)
+ == -1) {
+ err(1, "bread: %s", disk.d_error);
+ }
+ snprintf(comment, sizeof(comment),
+ "Inode 0x%08x: indirect 2->%d->%d", inode,
+ ind3ctr, ind3ctr);
+ DBG_DUMP_IBLK(&sblock,
+ comment,
+ i1blk,
+ (size_t)rb);
+ rb-=howmany(sblock.fs_bsize,
+ sizeof(ufs1_daddr_t));
+ }
+ }
+ }
+
+ DBG_LEAVE;
+ return;
+}
+
+/* ********************************************** dump_whole_ufs2_inode ***** */
+/*
+ * Here we dump a list of all blocks allocated by this inode. We follow
+ * all indirect blocks.
+ */
+void
+dump_whole_ufs2_inode(ino_t inode, int level)
+{
+ DBG_FUNC("dump_whole_ufs2_inode")
+ struct ufs2_dinode *ino;
+ int rb, mode;
+ unsigned int ind2ctr, ind3ctr;
+ ufs2_daddr_t *ind2ptr, *ind3ptr;
+ char comment[80];
+
+ DBG_ENTER;
+
+ /*
+ * Read the inode from disk/cache.
+ */
+ if (getino(&disk, (void **)&ino, inode, &mode) == -1)
+ err(1, "getino: %s", disk.d_error);
+
+ if (ino->di_nlink == 0) {
+ DBG_LEAVE;
+ return; /* inode not in use */
+ }
+
+ /*
+ * Dump the main inode structure.
+ */
+ snprintf(comment, sizeof(comment), "Inode 0x%08x", inode);
+ if (level & 0x100) {
+ DBG_DUMP_INO(&sblock, comment, ino);
+ }
+
+ if (!(level & 0x200)) {
+ DBG_LEAVE;
+ return;
+ }
+
+ /*
+ * Ok, now prepare for dumping all direct and indirect pointers.
+ */
+ rb = howmany(ino->di_size, sblock.fs_bsize) - NDADDR;
+ if (rb > 0) {
+ /*
+ * Dump single indirect block.
+ */
+ if (bread(&disk, fsbtodb(&sblock, ino->di_ib[0]), (void *)&i1blk,
+ (size_t)sblock.fs_bsize) == -1) {
+ err(1, "bread: %s", disk.d_error);
+ }
+ snprintf(comment, sizeof(comment), "Inode 0x%08x: indirect 0", inode);
+ DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb);
+ rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t));
+ }
+ if (rb > 0) {
+ /*
+ * Dump double indirect blocks.
+ */
+ if (bread(&disk, fsbtodb(&sblock, ino->di_ib[1]), (void *)&i2blk,
+ (size_t)sblock.fs_bsize) == -1) {
+ err(1, "bread: %s", disk.d_error);
+ }
+ snprintf(comment, sizeof(comment), "Inode 0x%08x: indirect 1", inode);
+ DBG_DUMP_IBLK(&sblock,
+ comment,
+ i2blk,
+ howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))));
+ for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize,
+ sizeof(ufs2_daddr_t))) && (rb>0)); ind2ctr++) {
+ ind2ptr = &((ufs2_daddr_t *)(void *)&i2blk)[ind2ctr];
+
+ if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk,
+ (size_t)sblock.fs_bsize) == -1) {
+ err(1, "bread: %s", disk.d_error);
+ }
+ snprintf(comment, sizeof(comment),
+ "Inode 0x%08x: indirect 1->%d", inode, ind2ctr);
+ DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb);
+ rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t));
+ }
+ }
+ if (rb > 0) {
+ /*
+ * Dump triple indirect blocks.
+ */
+ if (bread(&disk, fsbtodb(&sblock, ino->di_ib[2]), (void *)&i3blk,
+ (size_t)sblock.fs_bsize) == -1) {
+ err(1, "bread: %s", disk.d_error);
+ }
+ snprintf(comment, sizeof(comment), "Inode 0x%08x: indirect 2", inode);
+#define SQUARE(a) ((a)*(a))
+ DBG_DUMP_IBLK(&sblock,
+ comment,
+ i3blk,
+ howmany(rb,
+ SQUARE(howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)))));
+#undef SQUARE
+ for (ind3ctr = 0; ((ind3ctr < howmany(sblock.fs_bsize,
+ sizeof(ufs2_daddr_t))) && (rb > 0)); ind3ctr++) {
+ ind3ptr = &((ufs2_daddr_t *)(void *)&i3blk)[ind3ctr];
+
+ if (bread(&disk, fsbtodb(&sblock, *ind3ptr), (void *)&i2blk,
+ (size_t)sblock.fs_bsize) == -1) {
+ err(1, "bread: %s", disk.d_error);
+ }
+ snprintf(comment, sizeof(comment),
+ "Inode 0x%08x: indirect 2->%d", inode, ind3ctr);
+ DBG_DUMP_IBLK(&sblock,
+ comment,
+ i2blk,
+ howmany(rb,
+ howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))));
+ for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize,
+ sizeof(ufs2_daddr_t))) && (rb > 0)); ind2ctr++) {
+ ind2ptr = &((ufs2_daddr_t *)(void *)&i2blk) [ind2ctr];
+ if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk,
+ (size_t)sblock.fs_bsize) == -1) {
+ err(1, "bread: %s", disk.d_error);
+ }
+ snprintf(comment, sizeof(comment),
+ "Inode 0x%08x: indirect 2->%d->%d", inode,
+ ind3ctr, ind3ctr);
+ DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb);
+ rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t));
+ }
+ }
+ }
+
+ DBG_LEAVE;
+ return;
+}
+
+/* ************************************************************* usage ***** */
+/*
+ * Dump a line of usage.
+ */
+void
+usage(void)
+{
+ DBG_FUNC("usage")
+
+ DBG_ENTER;
+
+ fprintf(stderr,
+ "usage: ffsinfo [-g cylinder_group] [-i inode] [-l level] "
+ "[-o outfile]\n"
+ " special | file\n");
+
+ DBG_LEAVE;
+ exit(1);
+}
diff --git a/sbin/fsck/Makefile b/sbin/fsck/Makefile
new file mode 100644
index 0000000..bc445fd
--- /dev/null
+++ b/sbin/fsck/Makefile
@@ -0,0 +1,9 @@
+# $NetBSD: Makefile,v 1.14 1996/09/27 22:38:37 christos Exp $
+# $FreeBSD$
+
+PROG= fsck
+SRCS= fsck.c fsutil.c preen.c
+WARNS?= 2
+MAN= fsck.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/fsck/fsck.8 b/sbin/fsck/fsck.8
new file mode 100644
index 0000000..b7360d9
--- /dev/null
+++ b/sbin/fsck/fsck.8
@@ -0,0 +1,198 @@
+.\" $NetBSD: fsck.8,v 1.19 1999/03/10 00:08:33 erh Exp $
+.\"
+.\" Copyright (c) 1996 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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Christos Zoulas.
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 25, 2001
+.Dt FSCK 8
+.Os
+.Sh NAME
+.Nm fsck
+.Nd file system consistency check and interactive repair
+.Sh SYNOPSIS
+.Nm
+.Op Fl dfnpvy
+.Op Fl B | F
+.Op Fl T Ar fstype : Ns Ar fsoptions
+.Op Fl t Ar fstype
+.Oo Ar special | node Oc ...
+.Sh DESCRIPTION
+The
+.Nm
+utility invokes file system-specific programs to check
+the special devices listed in the
+.Xr fstab 5
+file or in the command line for consistency.
+.Pp
+It is normally used in the script
+.Pa /etc/rc
+during automatic reboot.
+Traditionally,
+.Nm
+is invoked before the file systems are mounted
+and all checks are done to completion at that time.
+If background checking is available,
+.Nm
+is invoked twice.
+It is first invoked at the traditional time,
+before the file systems are mounted, with the
+.Fl F
+flag to do checking on all the file systems
+that cannot do background checking.
+It is then invoked a second time,
+after the system has completed going multiuser, with the
+.Fl B
+flag to do checking on all the file systems
+that can do background checking.
+Unlike the foreground checking,
+the background checking is started asynchronously
+so that other system activity can proceed
+even on the file systems that are being checked.
+.Pp
+If no file systems are specified,
+.Nm
+reads the table
+.Pa /etc/fstab
+to determine which file systems to check.
+Only partitions in
+.Pa /etc/fstab
+that are mounted
+.Dq rw ,
+.Dq rq
+or
+.Dq ro
+and that have non-zero pass number are checked.
+File systems with pass number 1 (normally just the root file system)
+are always checked one at a time.
+.Pp
+If not in preen mode, the remaining entries are checked in order of
+increasing pass number one at a time.
+This is needed when interaction with
+.Nm
+is required.
+.Pp
+In preen mode, after pass 1 completes, all remaining file systems are checked,
+in pass number order running one process per disk drive in parallel for each
+pass number in increasing order.
+.Pp
+In other words: In preen mode all pass 1 partitions are checked sequentially.
+Next all pass 2 partitions are checked in parallel, one process per disk drive.
+Next all pass 3 partitions are checked in parallel, one process per disk drive.
+etc.
+.Pp
+The disk drive containing each file system is inferred from the shortest prefix
+of the device name that ends in a digit; the remaining characters are assumed
+to be the partition and slice designators.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl d
+Debugging mode.
+Just print the commands without executing them.
+Available
+only if
+.Nm
+is compiled to support it.
+.It Fl f
+Force checking of file systems, even when they are marked clean (for file systems
+that support this).
+.It Fl n
+Causes
+.Nm
+to assume no as the answer to all operator questions, except "CONTINUE?".
+.It Fl p
+Enter preen mode.
+In preen mode, only a restricted class of innocuous
+file system inconsistencies will be corrected.
+If unexpected inconsistencies caused by hardware or
+software failures are encountered, the check program
+will exit with a failure.
+See the manual pages for the individual check programs
+for a list of the sorts of failures that they correct
+when running in preen mode.
+.It Fl F
+Run in foreground mode.
+The check program for each file system is invoked with the
+.Fl F
+flag to determine whether it wishes to run as part of
+the boot up sequence,
+or if it is able to do its job in background after the
+system is up and running.
+A non-zero exit code indicates that it wants to run in foreground
+and the check program is invoked.
+A zero exit code indicates that it is able to run later in background
+and just a deferred message is printed.
+.It Fl B
+Run in background mode.
+The check program for each file system is invoked with the
+.Fl F
+flag to determine whether it wishes to run as part of
+the boot up sequence,
+or if it is able to do its job in background after the
+system is up and running.
+A non-zero exit code indicates that it wanted to run in foreground
+which is assumed to have been done, so the file system is skipped.
+A zero exit code indicates that it is able to run in background
+so the check program is invoked with the
+.Fl B
+flag to indicate that a check on the active file system should be done.
+When running in background mode,
+only one file system at a time will be checked.
+.It Fl t Ar fstype
+Invoke
+.Nm
+only for the comma separated list of file system types.
+If the
+list starts with
+.Dq no
+then invoke
+.Nm
+for the file system types that are not specified in the list.
+.It Fl v
+Print the commands before executing them.
+.It Fl y
+Causes
+.Nm
+to assume yes
+as the answer to all operator questions.
+.It Fl T Ar fstype : Ns Ar fsoptions
+List of comma separated file system specific options for the specified
+file system type, in the same format as
+.Xr mount 8 .
+.El
+.Sh FILES
+.Bl -tag -width /etc/fstab -compact
+.It Pa /etc/fstab
+file system table
+.El
+.Sh SEE ALSO
+.Xr fstab 5 ,
+.Xr fsck_ffs 8 ,
+.Xr fsck_msdosfs 8 ,
+.Xr mount 8
diff --git a/sbin/fsck/fsck.c b/sbin/fsck/fsck.c
new file mode 100644
index 0000000..490a29a
--- /dev/null
+++ b/sbin/fsck/fsck.c
@@ -0,0 +1,574 @@
+/* $NetBSD: fsck.c,v 1.21 1999/04/22 04:20:53 abs Exp $ */
+
+/*
+ * Copyright (c) 1996 Christos Zoulas. All rights reserved.
+ * Copyright (c) 1980, 1989, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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 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: @(#)mount.c 8.19 (Berkeley) 4/19/94
+ * From: $NetBSD: mount.c,v 1.24 1995/11/18 03:34:29 cgd Exp
+ * $NetBSD: fsck.c,v 1.21 1999/04/22 04:20:53 abs Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/queue.h>
+#include <sys/wait.h>
+#define FSTYPENAMES
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fstab.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "fsutil.h"
+
+static enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST;
+
+TAILQ_HEAD(fstypelist, entry) opthead, selhead;
+
+struct entry {
+ char *type;
+ char *options;
+ TAILQ_ENTRY(entry) entries;
+};
+
+static char *options = NULL;
+static int flags = 0;
+static int forceflag = 0;
+
+static int checkfs(const char *, const char *, const char *, char *, pid_t *);
+static int selected(const char *);
+static void addoption(char *);
+static const char *getoptions(const char *);
+static void addentry(struct fstypelist *, const char *, const char *);
+static void maketypelist(char *);
+static void catopt(char **, const char *);
+static void mangle(char *, int *, const char ***, int *);
+static const char *getfslab(const char *);
+static void usage(void) __dead2;
+static int isok(struct fstab *);
+
+int
+main(int argc, char *argv[])
+{
+ struct fstab *fs;
+ int i, rval = 0;
+ const char *vfstype = NULL;
+ char globopt[3];
+
+ globopt[0] = '-';
+ globopt[2] = '\0';
+
+ TAILQ_INIT(&selhead);
+ TAILQ_INIT(&opthead);
+
+ while ((i = getopt(argc, argv, "BdvpfFnyl:t:T:")) != -1)
+ switch (i) {
+ case 'B':
+ if (flags & CHECK_BACKGRD)
+ errx(1, "Cannot specify -B and -F.");
+ flags |= DO_BACKGRD;
+ break;
+
+ case 'd':
+ flags |= CHECK_DEBUG;
+ break;
+
+ case 'v':
+ flags |= CHECK_VERBOSE;
+ break;
+
+ case 'F':
+ if (flags & DO_BACKGRD)
+ errx(1, "Cannot specify -B and -F.");
+ flags |= CHECK_BACKGRD;
+ break;
+
+ case 'p':
+ flags |= CHECK_PREEN;
+ /*FALLTHROUGH*/
+ case 'n':
+ case 'y':
+ globopt[1] = i;
+ catopt(&options, globopt);
+ break;
+
+ case 'f':
+ forceflag = 1;
+ globopt[1] = i;
+ catopt(&options, globopt);
+ break;
+
+ case 'l':
+ warnx("Ignoring obsolete -l option\n");
+ break;
+
+ case 'T':
+ if (*optarg)
+ addoption(optarg);
+ break;
+
+ case 't':
+ if (!TAILQ_EMPTY(&selhead))
+ errx(1, "only one -t option may be specified.");
+
+ maketypelist(optarg);
+ vfstype = optarg;
+ break;
+
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ return checkfstab(flags, isok, checkfs);
+
+#define BADTYPE(type) \
+ (strcmp(type, FSTAB_RO) && \
+ strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
+
+
+ for (; argc--; argv++) {
+ const char *spec, *mntpt, *type, *cp;
+ char device[MAXPATHLEN];
+ struct statfs *mntp;
+
+ spec = *argv;
+ cp = strrchr(spec, '/');
+ if (cp == 0) {
+ (void)snprintf(device, sizeof(device), "%s%s",
+ _PATH_DEV, spec);
+ spec = device;
+ }
+ mntp = getmntpt(spec);
+ if (mntp != NULL) {
+ spec = mntp->f_mntfromname;
+ mntpt = mntp->f_mntonname;
+ }
+ if ((fs = getfsfile(spec)) == NULL &&
+ (fs = getfsspec(spec)) == NULL) {
+ if (vfstype == NULL)
+ vfstype = getfslab(spec);
+ if (vfstype == NULL)
+ errx(1, "Could not determine filesystem type");
+ type = vfstype;
+ devcheck(spec);
+ } else {
+ spec = fs->fs_spec;
+ type = fs->fs_vfstype;
+ mntpt = fs->fs_file;
+ if (BADTYPE(fs->fs_type))
+ errx(1, "%s has unknown file system type.",
+ spec);
+ }
+ if ((flags & CHECK_BACKGRD) &&
+ checkfs(type, spec, mntpt, "-F", NULL) == 0) {
+ printf("%s: DEFER FOR BACKGROUND CHECKING\n", *argv);
+ continue;
+ }
+ if ((flags & DO_BACKGRD) && forceflag == 0 &&
+ checkfs(type, spec, mntpt, "-F", NULL) != 0)
+ continue;
+
+ rval |= checkfs(type, spec, mntpt, NULL, NULL);
+ }
+
+ return rval;
+}
+
+
+static int
+isok(struct fstab *fs)
+{
+ int i;
+
+ if (fs->fs_passno == 0)
+ return (0);
+ if (BADTYPE(fs->fs_type))
+ return (0);
+ if (!selected(fs->fs_vfstype))
+ return (0);
+ /*
+ * If the -B flag has been given, then process the needed
+ * background checks. Background checks cannot be run on
+ * file systems that will be mounted read-only or that were
+ * not mounted at boot time (typically those marked `noauto').
+ * If these basic tests are passed, check with the file system
+ * itself to see if it is willing to do background checking
+ * by invoking its check program with the -F flag.
+ */
+ if (flags & DO_BACKGRD) {
+ if (!strcmp(fs->fs_type, FSTAB_RO))
+ return (0);
+ if (getmntpt(fs->fs_spec) == NULL)
+ return (0);
+ if (checkfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file, "-F", 0))
+ return (0);
+ return (1);
+ }
+ /*
+ * If the -F flag has been given, then consider deferring the
+ * check to background. Background checks cannot be run on
+ * file systems that will be mounted read-only or that will
+ * not be mounted at boot time (e.g., marked `noauto'). If
+ * these basic tests are passed, check with the file system
+ * itself to see if it is willing to defer to background
+ * checking by invoking its check program with the -F flag.
+ */
+ if ((flags & CHECK_BACKGRD) == 0 || !strcmp(fs->fs_type, FSTAB_RO))
+ return (1);
+ for (i = strlen(fs->fs_mntops) - 6; i >= 0; i--)
+ if (!strncmp(&fs->fs_mntops[i], "noauto", 6))
+ break;
+ if (i >= 0)
+ return (1);
+ if (checkfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file, "-F", NULL) != 0)
+ return (1);
+ printf("%s: DEFER FOR BACKGROUND CHECKING\n", fs->fs_spec);
+ return (0);
+}
+
+
+static int
+checkfs(const char *pvfstype, const char *spec, const char *mntpt,
+ char *auxopt, pid_t *pidp)
+{
+ const char **argv;
+ pid_t pid;
+ int argc, i, status, maxargc;
+ char *optbuf, execbase[MAXPATHLEN];
+ char *vfstype = NULL;
+ const char *extra = NULL;
+
+#ifdef __GNUC__
+ /* Avoid vfork clobbering */
+ (void) &optbuf;
+ (void) &vfstype;
+#endif
+ /*
+ * We convert the vfstype to lowercase and any spaces to underscores
+ * to not confuse the issue
+ *
+ * XXX This is a kludge to make automatic filesystem type guessing
+ * from the disklabel work for "4.2BSD" filesystems. It does a
+ * very limited subset of transliteration to a normalised form of
+ * filesystem name, and we do not seem to enforce a filesystem
+ * name character set.
+ */
+ vfstype = strdup(pvfstype);
+ if (vfstype == NULL)
+ perror("strdup(pvfstype)");
+ for (i = 0; i < strlen(vfstype); i++) {
+ vfstype[i] = tolower(vfstype[i]);
+ if (vfstype[i] == ' ')
+ vfstype[i] = '_';
+ }
+
+ extra = getoptions(vfstype);
+ optbuf = NULL;
+ if (options)
+ catopt(&optbuf, options);
+ if (extra)
+ catopt(&optbuf, extra);
+ if (auxopt)
+ catopt(&optbuf, auxopt);
+ else if (flags & DO_BACKGRD)
+ catopt(&optbuf, "-B");
+
+ maxargc = 64;
+ argv = emalloc(sizeof(char *) * maxargc);
+
+ (void) snprintf(execbase, sizeof(execbase), "fsck_%s", vfstype);
+ argc = 0;
+ argv[argc++] = execbase;
+ if (optbuf)
+ mangle(optbuf, &argc, &argv, &maxargc);
+ argv[argc++] = spec;
+ argv[argc] = NULL;
+
+ if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) {
+ (void)printf("start %s %swait", mntpt,
+ pidp ? "no" : "");
+ for (i = 0; i < argc; i++)
+ (void)printf(" %s", argv[i]);
+ (void)printf("\n");
+ }
+
+ switch (pid = vfork()) {
+ case -1: /* Error. */
+ warn("vfork");
+ if (optbuf)
+ free(optbuf);
+ free(vfstype);
+ return (1);
+
+ case 0: /* Child. */
+ if ((flags & CHECK_DEBUG) && auxopt == NULL)
+ _exit(0);
+
+ /* Go find an executable. */
+ execvP(execbase, _PATH_SYSPATH, (char * const *)argv);
+ if (spec)
+ warn("exec %s for %s in %s", execbase, spec, _PATH_SYSPATH);
+ else
+ warn("exec %s in %s", execbase, _PATH_SYSPATH);
+ _exit(1);
+ /* NOTREACHED */
+
+ default: /* Parent. */
+ if (optbuf)
+ free(optbuf);
+
+ free(vfstype);
+
+ if (pidp) {
+ *pidp = pid;
+ return 0;
+ }
+
+ if (waitpid(pid, &status, 0) < 0) {
+ warn("waitpid");
+ return (1);
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0)
+ return (WEXITSTATUS(status));
+ }
+ else if (WIFSIGNALED(status)) {
+ warnx("%s: %s", spec, strsignal(WTERMSIG(status)));
+ return (1);
+ }
+ break;
+ }
+
+ return (0);
+}
+
+
+static int
+selected(const char *type)
+{
+ struct entry *e;
+
+ /* If no type specified, it's always selected. */
+ TAILQ_FOREACH(e, &selhead, entries)
+ if (!strncmp(e->type, type, MFSNAMELEN))
+ return which == IN_LIST ? 1 : 0;
+
+ return which == IN_LIST ? 0 : 1;
+}
+
+
+static const char *
+getoptions(const char *type)
+{
+ struct entry *e;
+
+ TAILQ_FOREACH(e, &opthead, entries)
+ if (!strncmp(e->type, type, MFSNAMELEN))
+ return e->options;
+ return "";
+}
+
+
+static void
+addoption(char *optstr)
+{
+ char *newoptions;
+ struct entry *e;
+
+ if ((newoptions = strchr(optstr, ':')) == NULL)
+ errx(1, "Invalid option string");
+
+ *newoptions++ = '\0';
+
+ TAILQ_FOREACH(e, &opthead, entries)
+ if (!strncmp(e->type, optstr, MFSNAMELEN)) {
+ catopt(&e->options, newoptions);
+ return;
+ }
+ addentry(&opthead, optstr, newoptions);
+}
+
+
+static void
+addentry(struct fstypelist *list, const char *type, const char *opts)
+{
+ struct entry *e;
+
+ e = emalloc(sizeof(struct entry));
+ e->type = estrdup(type);
+ e->options = estrdup(opts);
+ TAILQ_INSERT_TAIL(list, e, entries);
+}
+
+
+static void
+maketypelist(char *fslist)
+{
+ char *ptr;
+
+ if ((fslist == NULL) || (fslist[0] == '\0'))
+ errx(1, "empty type list");
+
+ if (fslist[0] == 'n' && fslist[1] == 'o') {
+ fslist += 2;
+ which = NOT_IN_LIST;
+ }
+ else
+ which = IN_LIST;
+
+ while ((ptr = strsep(&fslist, ",")) != NULL)
+ addentry(&selhead, ptr, "");
+
+}
+
+
+static void
+catopt(char **sp, const char *o)
+{
+ char *s;
+ size_t i, j;
+
+ s = *sp;
+ if (s) {
+ i = strlen(s);
+ j = i + 1 + strlen(o) + 1;
+ s = erealloc(s, j);
+ (void)snprintf(s + i, j, ",%s", o);
+ } else
+ s = estrdup(o);
+ *sp = s;
+}
+
+
+static void
+mangle(char *options, int *argcp, const char ***argvp, int *maxargcp)
+{
+ char *p, *s;
+ int argc, maxargc;
+ const char **argv;
+
+ argc = *argcp;
+ argv = *argvp;
+ maxargc = *maxargcp;
+
+ for (s = options; (p = strsep(&s, ",")) != NULL;) {
+ /* Always leave space for one more argument and the NULL. */
+ if (argc >= maxargc - 3) {
+ maxargc <<= 1;
+ argv = erealloc(argv, maxargc * sizeof(char *));
+ }
+ if (*p != '\0') {
+ if (*p == '-') {
+ argv[argc++] = p;
+ p = strchr(p, '=');
+ if (p) {
+ *p = '\0';
+ argv[argc++] = p+1;
+ }
+ } else {
+ argv[argc++] = "-o";
+ argv[argc++] = p;
+ }
+ }
+ }
+
+ *argcp = argc;
+ *argvp = argv;
+ *maxargcp = maxargc;
+}
+
+
+const static char *
+getfslab(const char *str)
+{
+ struct disklabel dl;
+ int fd;
+ char p;
+ const char *vfstype;
+ u_char t;
+
+ /* deduce the file system type from the disk label */
+ if ((fd = open(str, O_RDONLY)) == -1)
+ err(1, "cannot open `%s'", str);
+
+ if (ioctl(fd, DIOCGDINFO, &dl) == -1)
+ return(NULL);
+
+ (void) close(fd);
+
+ p = str[strlen(str) - 1];
+
+ if ((p - 'a') >= dl.d_npartitions)
+ errx(1, "partition `%s' is not defined on disk", str);
+
+ if ((t = dl.d_partitions[p - 'a'].p_fstype) >= FSMAXTYPES)
+ errx(1, "partition `%s' is not of a legal vfstype",
+ str);
+
+ if ((vfstype = fstypenames[t]) == NULL)
+ errx(1, "vfstype `%s' on partition `%s' is not supported",
+ fstypenames[t], str);
+
+ return vfstype;
+}
+
+
+static void
+usage(void)
+{
+ static const char common[] =
+ "[-dfnpvy] [-B | -F] [-T fstype:fsoptions] [-t fstype]";
+
+ (void)fprintf(stderr, "usage: %s %s [special | node] ...\n",
+ getprogname(), common);
+ exit(1);
+}
diff --git a/sbin/fsck/fsutil.c b/sbin/fsck/fsutil.c
new file mode 100644
index 0000000..a4578bd
--- /dev/null
+++ b/sbin/fsck/fsutil.c
@@ -0,0 +1,225 @@
+/* $NetBSD: fsutil.c,v 1.7 1998/07/30 17:41:03 thorpej Exp $ */
+
+/*
+ * Copyright (c) 1990, 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.
+ * 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 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/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: fsutil.c,v 1.7 1998/07/30 17:41:03 thorpej Exp $");
+#endif /* not lint */
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fstab.h>
+#include <paths.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "fsutil.h"
+
+static const char *dev = NULL;
+static int preen = 0;
+
+static void vmsg(int, const char *, va_list) __printflike(2, 0);
+
+void
+setcdevname(const char *cd, int pr)
+{
+ dev = cd;
+ preen = pr;
+}
+
+const char *
+cdevname(void)
+{
+ return dev;
+}
+
+static void
+vmsg(int fatal, const char *fmt, va_list ap)
+{
+ if (!fatal && preen)
+ (void) printf("%s: ", dev);
+
+ (void) vprintf(fmt, ap);
+
+ if (fatal && preen)
+ (void) printf("\n");
+
+ if (fatal && preen) {
+ (void) printf(
+ "%s: UNEXPECTED INCONSISTENCY; RUN %s MANUALLY.\n",
+ dev, getprogname());
+ exit(8);
+ }
+}
+
+/*VARARGS*/
+void
+pfatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vmsg(1, fmt, ap);
+ va_end(ap);
+}
+
+/*VARARGS*/
+void
+pwarn(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vmsg(0, fmt, ap);
+ va_end(ap);
+}
+
+void
+perror(const char *s)
+{
+ pfatal("%s (%s)", s, strerror(errno));
+}
+
+void
+panic(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vmsg(1, fmt, ap);
+ va_end(ap);
+ exit(8);
+}
+
+const char *
+devcheck(const char *origname)
+{
+ struct stat stslash, stchar;
+
+ if (stat("/", &stslash) < 0) {
+ perror("/");
+ printf("Can't stat root\n");
+ return (origname);
+ }
+ if (stat(origname, &stchar) < 0) {
+ perror(origname);
+ printf("Can't stat %s\n", origname);
+ return (origname);
+ }
+ if (!S_ISCHR(stchar.st_mode)) {
+ perror(origname);
+ printf("%s is not a char device\n", origname);
+ }
+ return (origname);
+}
+
+/*
+ * Get the mount point information for name.
+ */
+struct statfs *
+getmntpt(const char *name)
+{
+ struct stat devstat, mntdevstat;
+ char device[sizeof(_PATH_DEV) - 1 + MNAMELEN];
+ char *devname;
+ struct statfs *mntbuf, *statfsp;
+ int i, mntsize, isdev;
+
+ if (stat(name, &devstat) != 0)
+ return (NULL);
+ if (S_ISCHR(devstat.st_mode) || S_ISBLK(devstat.st_mode))
+ isdev = 1;
+ else
+ isdev = 0;
+ mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
+ for (i = 0; i < mntsize; i++) {
+ statfsp = &mntbuf[i];
+ devname = statfsp->f_mntfromname;
+ if (*devname != '/') {
+ strcpy(device, _PATH_DEV);
+ strcat(device, devname);
+ strcpy(statfsp->f_mntfromname, device);
+ }
+ if (isdev == 0) {
+ if (strcmp(name, statfsp->f_mntonname))
+ continue;
+ return (statfsp);
+ }
+ if (stat(devname, &mntdevstat) == 0 &&
+ mntdevstat.st_rdev == devstat.st_rdev)
+ return (statfsp);
+ }
+ statfsp = NULL;
+ return (statfsp);
+}
+
+
+void *
+emalloc(size_t s)
+{
+ void *p;
+
+ p = malloc(s);
+ if (p == NULL)
+ err(1, "malloc failed");
+ return (p);
+}
+
+
+void *
+erealloc(void *p, size_t s)
+{
+ void *q;
+
+ q = realloc(p, s);
+ if (q == NULL)
+ err(1, "realloc failed");
+ return (q);
+}
+
+
+char *
+estrdup(const char *s)
+{
+ char *p;
+
+ p = strdup(s);
+ if (p == NULL)
+ err(1, "strdup failed");
+ return (p);
+}
diff --git a/sbin/fsck/fsutil.h b/sbin/fsck/fsutil.h
new file mode 100644
index 0000000..84e7290
--- /dev/null
+++ b/sbin/fsck/fsutil.h
@@ -0,0 +1,54 @@
+/* $NetBSD: fsutil.h,v 1.4 1998/07/26 20:02:36 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1996 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christos Zoulas.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+void perror(const char *);
+void pfatal(const char *, ...) __printflike(1, 2);
+void pwarn(const char *, ...) __printflike(1, 2);
+void panic(const char *, ...) __dead2 __printflike(1, 2);
+const char *devcheck(const char *);
+const char *cdevname(void);
+void setcdevname(const char *, int);
+struct statfs *getmntpt(const char *);
+void *emalloc(size_t);
+void *erealloc(void *, size_t);
+char *estrdup(const char *);
+
+#define CHECK_PREEN 0x0001
+#define CHECK_VERBOSE 0x0002
+#define CHECK_DEBUG 0x0004
+#define CHECK_BACKGRD 0x0008
+#define DO_BACKGRD 0x0010
+
+struct fstab;
+int checkfstab(int, int (*)(struct fstab *),
+ int (*) (const char *, const char *, const char *, char *, pid_t *));
diff --git a/sbin/fsck/preen.c b/sbin/fsck/preen.c
new file mode 100644
index 0000000..6204685
--- /dev/null
+++ b/sbin/fsck/preen.c
@@ -0,0 +1,333 @@
+/* $NetBSD: preen.c,v 1.18 1998/07/26 20:02:36 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1990, 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.
+ * 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)preen.c 8.5 (Berkeley) 4/28/95";
+#else
+__RCSID("$NetBSD: preen.c,v 1.18 1998/07/26 20:02:36 mycroft Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/queue.h>
+
+#include <err.h>
+#include <ctype.h>
+#include <fstab.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "fsutil.h"
+
+struct partentry {
+ TAILQ_ENTRY(partentry) p_entries;
+ char *p_devname; /* device name */
+ char *p_mntpt; /* mount point */
+ char *p_type; /* file system type */
+};
+
+TAILQ_HEAD(part, partentry) badh;
+
+struct diskentry {
+ TAILQ_ENTRY(diskentry) d_entries;
+ char *d_name; /* disk base name */
+ TAILQ_HEAD(prt, partentry) d_part; /* list of partitions on disk */
+ int d_pid; /* 0 or pid of fsck proc */
+};
+
+TAILQ_HEAD(disk, diskentry) diskh;
+
+static int nrun = 0, ndisks = 0;
+
+static struct diskentry *finddisk(const char *);
+static void addpart(const char *, const char *, const char *);
+static int startdisk(struct diskentry *,
+ int (*)(const char *, const char *, const char *, char *, pid_t *));
+static void printpart(void);
+
+int
+checkfstab(int flags, int (*docheck)(struct fstab *),
+ int (*checkit)(const char *, const char *, const char *, char *, pid_t *))
+{
+ struct fstab *fs;
+ struct diskentry *d, *nextdisk;
+ struct partentry *p;
+ int ret, pid, retcode, passno, sumstatus, status, nextpass;
+ const char *name;
+
+ TAILQ_INIT(&badh);
+ TAILQ_INIT(&diskh);
+
+ sumstatus = 0;
+
+ nextpass = 0;
+ for (passno = 1; nextpass != INT_MAX; passno = nextpass) {
+ if (flags & CHECK_DEBUG)
+ printf("pass %d\n", passno);
+
+ nextpass = INT_MAX;
+ if (setfsent() == 0) {
+ warnx("Can't open checklist file: %s\n", _PATH_FSTAB);
+ return (8);
+ }
+ while ((fs = getfsent()) != 0) {
+ name = fs->fs_spec;
+ if (fs->fs_passno > passno && fs->fs_passno < nextpass)
+ nextpass = fs->fs_passno;
+
+ if (passno != fs->fs_passno)
+ continue;
+
+ if ((*docheck)(fs) == 0)
+ continue;
+
+ if (flags & CHECK_DEBUG)
+ printf("pass %d, name %s\n", passno, name);
+
+ if ((flags & CHECK_PREEN) == 0 || passno == 1 ||
+ (flags & DO_BACKGRD) != 0) {
+ if (name == NULL) {
+ if (flags & CHECK_PREEN)
+ return 8;
+ else
+ continue;
+ }
+ sumstatus = (*checkit)(fs->fs_vfstype,
+ name, fs->fs_file, NULL, NULL);
+
+ if (sumstatus)
+ return (sumstatus);
+ continue;
+ }
+ if (name == NULL) {
+ (void) fprintf(stderr,
+ "BAD DISK NAME %s\n", fs->fs_spec);
+ sumstatus |= 8;
+ continue;
+ }
+ addpart(fs->fs_vfstype, name, fs->fs_file);
+ }
+
+ if ((flags & CHECK_PREEN) == 0 || passno == 1 ||
+ (flags & DO_BACKGRD) != 0)
+ continue;
+
+ if (flags & CHECK_DEBUG) {
+ printf("Parallel start\n");
+ printpart();
+ }
+
+ TAILQ_FOREACH(nextdisk, &diskh, d_entries) {
+ if ((ret = startdisk(nextdisk, checkit)) != 0)
+ return ret;
+ }
+
+ if (flags & CHECK_DEBUG)
+ printf("Parallel wait\n");
+ while ((pid = wait(&status)) != -1) {
+ TAILQ_FOREACH(d, &diskh, d_entries)
+ if (d->d_pid == pid)
+ break;
+
+ if (d == NULL) {
+ warnx("Unknown pid %d\n", pid);
+ continue;
+ }
+
+ if (WIFEXITED(status))
+ retcode = WEXITSTATUS(status);
+ else
+ retcode = 0;
+
+ p = TAILQ_FIRST(&d->d_part);
+
+ if (flags & (CHECK_DEBUG|CHECK_VERBOSE))
+ (void) printf("done %s: %s (%s) = 0x%x\n",
+ p->p_type, p->p_devname, p->p_mntpt,
+ status);
+
+ if (WIFSIGNALED(status)) {
+ (void) fprintf(stderr,
+ "%s: %s (%s): EXITED WITH SIGNAL %d\n",
+ p->p_type, p->p_devname, p->p_mntpt,
+ WTERMSIG(status));
+ retcode = 8;
+ }
+
+ TAILQ_REMOVE(&d->d_part, p, p_entries);
+
+ if (retcode != 0) {
+ TAILQ_INSERT_TAIL(&badh, p, p_entries);
+ sumstatus |= retcode;
+ } else {
+ free(p->p_type);
+ free(p->p_devname);
+ free(p);
+ }
+ d->d_pid = 0;
+ nrun--;
+
+ if (TAILQ_EMPTY(&d->d_part)) {
+ TAILQ_REMOVE(&diskh, d, d_entries);
+ ndisks--;
+ } else {
+ if ((ret = startdisk(d, checkit)) != 0)
+ return ret;
+ }
+ }
+ if (flags & CHECK_DEBUG) {
+ printf("Parallel end\n");
+ printpart();
+ }
+ }
+
+ if (!(flags & CHECK_PREEN))
+ return 0;
+
+ if (sumstatus) {
+ p = TAILQ_FIRST(&badh);
+ if (p == NULL)
+ return (sumstatus);
+
+ (void) fprintf(stderr,
+ "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
+ TAILQ_NEXT(p, p_entries) ? "S" : "",
+ "UNEXPECTED INCONSISTENCY:");
+
+ for (; p; p = TAILQ_NEXT(p, p_entries))
+ (void) fprintf(stderr,
+ "%s: %s (%s)%s", p->p_type, p->p_devname,
+ p->p_mntpt, TAILQ_NEXT(p, p_entries) ? ", " : "\n");
+
+ return sumstatus;
+ }
+ (void) endfsent();
+ return (0);
+}
+
+
+static struct diskentry *
+finddisk(const char *name)
+{
+ const char *p;
+ size_t len = 0;
+ struct diskentry *d;
+
+ p = strrchr(name, '/');
+ if (p == NULL)
+ p = name;
+ else
+ p++;
+ for (; *p && !isdigit(*p); p++)
+ continue;
+ for (; *p && isdigit(*p); p++)
+ continue;
+ len = p - name;
+ if (len == 0)
+ len = strlen(name);
+
+ TAILQ_FOREACH(d, &diskh, d_entries)
+ if (strncmp(d->d_name, name, len) == 0 && d->d_name[len] == 0)
+ return d;
+
+ d = emalloc(sizeof(*d));
+ d->d_name = estrdup(name);
+ d->d_name[len] = '\0';
+ TAILQ_INIT(&d->d_part);
+ d->d_pid = 0;
+
+ TAILQ_INSERT_TAIL(&diskh, d, d_entries);
+ ndisks++;
+
+ return d;
+}
+
+
+static void
+printpart(void)
+{
+ struct diskentry *d;
+ struct partentry *p;
+
+ TAILQ_FOREACH(d, &diskh, d_entries) {
+ (void) printf("disk %s: ", d->d_name);
+ TAILQ_FOREACH(p, &d->d_part, p_entries)
+ (void) printf("%s ", p->p_devname);
+ (void) printf("\n");
+ }
+}
+
+
+static void
+addpart(const char *type, const char *devname, const char *mntpt)
+{
+ struct diskentry *d = finddisk(devname);
+ struct partentry *p;
+
+ TAILQ_FOREACH(p, &d->d_part, p_entries)
+ if (strcmp(p->p_devname, devname) == 0) {
+ warnx("%s in fstab more than once!\n", devname);
+ return;
+ }
+
+ p = emalloc(sizeof(*p));
+ p->p_devname = estrdup(devname);
+ p->p_mntpt = estrdup(mntpt);
+ p->p_type = estrdup(type);
+
+ TAILQ_INSERT_TAIL(&d->d_part, p, p_entries);
+}
+
+
+static int
+startdisk(struct diskentry *d, int (*checkit)(const char *, const char *,
+ const char *, char *, pid_t *))
+{
+ struct partentry *p = TAILQ_FIRST(&d->d_part);
+ int rv;
+
+ while ((rv = (*checkit)(p->p_type, p->p_devname, p->p_mntpt,
+ NULL, &d->d_pid)) != 0 && nrun > 0)
+ sleep(10);
+
+ if (rv == 0)
+ nrun++;
+
+ return rv;
+}
diff --git a/sbin/fsck_ffs/Makefile b/sbin/fsck_ffs/Makefile
new file mode 100644
index 0000000..5b7474f
--- /dev/null
+++ b/sbin/fsck_ffs/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+# @(#)Makefile 8.2 (Berkeley) 4/27/95
+
+PROG= fsck_ffs
+LINKS+= ${BINDIR}/fsck_ffs ${BINDIR}/fsck_ufs
+LINKS+= ${BINDIR}/fsck_ffs ${BINDIR}/fsck_4.2bsd
+MAN= fsck_ffs.8
+MLINKS= fsck_ffs.8 fsck_ufs.8 fsck_ffs.8 fsck_4.2bsd.8
+SRCS= dir.c ea.c fsutil.c inode.c main.c pass1.c pass1b.c pass2.c pass3.c \
+ pass4.c pass5.c setup.c utilities.c ffs_subr.c ffs_tables.c
+WARNS?= 2
+CFLAGS+= -I${.CURDIR}
+
+.PATH: ${.CURDIR}/../../sys/ufs/ffs
+
+.include <bsd.prog.mk>
diff --git a/sbin/fsck_ffs/SMM.doc/0.t b/sbin/fsck_ffs/SMM.doc/0.t
new file mode 100644
index 0000000..5fe189d
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/0.t
@@ -0,0 +1,147 @@
+.\" Copyright (c) 1986, 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.
+.\" 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 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.
+.\"
+.\" $FreeBSD$
+.\" @(#)0.t 8.1 (Berkeley) 6/8/93
+.\"
+.if n .ND
+.TL
+Fsck_ffs \- The UNIX\(dg File System Check Program
+.EH 'SMM:3-%''The \s-2UNIX\s+2 File System Check Program'
+.OH 'The \s-2UNIX\s+2 File System Check Program''SMM:3-%'
+.AU
+Marshall Kirk McKusick
+.AI
+Computer Systems Research Group
+Computer Science Division
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, CA 94720
+.AU
+T. J. Kowalski
+.AI
+Bell Laboratories
+Murray Hill, New Jersey 07974
+.AB
+.FS
+\(dgUNIX is a trademark of Bell Laboratories.
+.FE
+.FS
+This work was done under grants from
+the National Science Foundation under grant MCS80-05144,
+and the Defense Advance Research Projects Agency (DoD) under
+Arpa Order No. 4031 monitored by Naval Electronic System Command under
+Contract No. N00039-82-C-0235.
+.FE
+This document reflects the use of
+.I fsck_ffs
+with the 4.2BSD and 4.3BSD file system organization. This
+is a revision of the
+original paper written by
+T. J. Kowalski.
+.PP
+File System Check Program (\fIfsck_ffs\fR)
+is an interactive file system check and repair program.
+.I Fsck_ffs
+uses the redundant structural information in the
+UNIX file system to perform several consistency checks.
+If an inconsistency is detected, it is reported
+to the operator, who may elect to fix or ignore
+each inconsistency.
+These inconsistencies result from the permanent interruption
+of the file system updates, which are performed every
+time a file is modified.
+Unless there has been a hardware failure,
+.I fsck_ffs
+is able to repair corrupted file systems
+using procedures based upon the order in which UNIX honors
+these file system update requests.
+.PP
+The purpose of this document is to describe the normal updating
+of the file system,
+to discuss the possible causes of file system corruption,
+and to present the corrective actions implemented
+by
+.I fsck_ffs.
+Both the program and the interaction between the
+program and the operator are described.
+.sp 2
+.LP
+Revised October 7, 1996
+.AE
+.LP
+.bp
+.ce
+.B "TABLE OF CONTENTS"
+.LP
+.sp 1
+.nf
+.B "1. Introduction"
+.LP
+.sp .5v
+.nf
+.B "2. Overview of the file system
+2.1. Superblock
+2.2. Summary Information
+2.3. Cylinder groups
+2.4. Fragments
+2.5. Updates to the file system
+.LP
+.sp .5v
+.nf
+.B "3. Fixing corrupted file systems
+3.1. Detecting and correcting corruption
+3.2. Super block checking
+3.3. Free block checking
+3.4. Checking the inode state
+3.5. Inode links
+3.6. Inode data size
+3.7. Checking the data associated with an inode
+3.8. File system connectivity
+.LP
+.sp .5v
+.nf
+.B Acknowledgements
+.LP
+.sp .5v
+.nf
+.B References
+.LP
+.sp .5v
+.nf
+.B "4. Appendix A
+4.1. Conventions
+4.2. Initialization
+4.3. Phase 1 - Check Blocks and Sizes
+4.4. Phase 1b - Rescan for more Dups
+4.5. Phase 2 - Check Pathnames
+4.6. Phase 3 - Check Connectivity
+4.7. Phase 4 - Check Reference Counts
+4.8. Phase 5 - Check Cyl groups
+4.9. Cleanup
+.ds RH Introduction
+.bp
diff --git a/sbin/fsck_ffs/SMM.doc/1.t b/sbin/fsck_ffs/SMM.doc/1.t
new file mode 100644
index 0000000..3098b49
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/1.t
@@ -0,0 +1,80 @@
+.\" Copyright (c) 1982, 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.
+.\" 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 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.
+.\"
+.\" $FreeBSD$
+.\" @(#)1.t 8.1 (Berkeley) 6/5/93
+.\"
+.ds RH Introduction
+.NH
+Introduction
+.PP
+This document reflects the use of
+.I fsck_ffs
+with the 4.2BSD and 4.3BSD file system organization. This
+is a revision of the
+original paper written by
+T. J. Kowalski.
+.PP
+When a UNIX
+operating system is brought up, a consistency
+check of the file systems should always be performed.
+This precautionary measure helps to insure
+a reliable environment for file storage on disk.
+If an inconsistency is discovered,
+corrective action must be taken.
+.I Fsck_ffs
+runs in two modes.
+Normally it is run non-interactively by the system after
+a normal boot.
+When running in this mode,
+it will only make changes to the file system that are known
+to always be correct.
+If an unexpected inconsistency is found
+.I fsck_ffs
+will exit with a non-zero exit status,
+leaving the system running single-user.
+Typically the operator then runs
+.I fsck_ffs
+interactively.
+When running in this mode,
+each problem is listed followed by a suggested corrective action.
+The operator must decide whether or not the suggested correction
+should be made.
+.PP
+The purpose of this memo is to dispel the
+mystique surrounding
+file system inconsistencies.
+It first describes the updating of the file system
+(the calm before the storm) and
+then describes file system corruption (the storm).
+Finally,
+the set of deterministic corrective actions
+used by
+.I fsck_ffs
+(the Coast Guard
+to the rescue) is presented.
+.ds RH Overview of the File System
diff --git a/sbin/fsck_ffs/SMM.doc/2.t b/sbin/fsck_ffs/SMM.doc/2.t
new file mode 100644
index 0000000..b294a6a
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/2.t
@@ -0,0 +1,262 @@
+.\" Copyright (c) 1982, 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.
+.\" 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 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.
+.\"
+.\" $FreeBSD$
+.\" @(#)2.t 8.1 (Berkeley) 6/5/93
+.\"
+.ds RH Overview of the file system
+.NH
+Overview of the file system
+.PP
+The file system is discussed in detail in [Mckusick84];
+this section gives a brief overview.
+.NH 2
+Superblock
+.PP
+A file system is described by its
+.I "super-block" .
+The super-block is built when the file system is created (\c
+.I newfs (8))
+and never changes.
+The super-block
+contains the basic parameters of the file system,
+such as the number of data blocks it contains
+and a count of the maximum number of files.
+Because the super-block contains critical data,
+.I newfs
+replicates it to protect against catastrophic loss.
+The
+.I "default super block"
+always resides at a fixed offset from the beginning
+of the file system's disk partition.
+The
+.I "redundant super blocks"
+are not referenced unless a head crash
+or other hard disk error causes the default super-block
+to be unusable.
+The redundant blocks are sprinkled throughout the disk partition.
+.PP
+Within the file system are files.
+Certain files are distinguished as directories and contain collections
+of pointers to files that may themselves be directories.
+Every file has a descriptor associated with it called an
+.I "inode".
+The inode contains information describing ownership of the file,
+time stamps indicating modification and access times for the file,
+and an array of indices pointing to the data blocks for the file.
+In this section,
+we assume that the first 12 blocks
+of the file are directly referenced by values stored
+in the inode structure itself\(dg.
+.FS
+\(dgThe actual number may vary from system to system, but is usually in
+the range 5-13.
+.FE
+The inode structure may also contain references to indirect blocks
+containing further data block indices.
+In a file system with a 4096 byte block size, a singly indirect
+block contains 1024 further block addresses,
+a doubly indirect block contains 1024 addresses of further single indirect
+blocks,
+and a triply indirect block contains 1024 addresses of further doubly indirect
+blocks (the triple indirect block is never needed in practice).
+.PP
+In order to create files with up to
+2\(ua32 bytes,
+using only two levels of indirection,
+the minimum size of a file system block is 4096 bytes.
+The size of file system blocks can be any power of two
+greater than or equal to 4096.
+The block size of the file system is maintained in the super-block,
+so it is possible for file systems of different block sizes
+to be accessible simultaneously on the same system.
+The block size must be decided when
+.I newfs
+creates the file system;
+the block size cannot be subsequently
+changed without rebuilding the file system.
+.NH 2
+Summary information
+.PP
+Associated with the super block is non replicated
+.I "summary information" .
+The summary information changes
+as the file system is modified.
+The summary information contains
+the number of blocks, fragments, inodes and directories in the file system.
+.NH 2
+Cylinder groups
+.PP
+The file system partitions the disk into one or more areas called
+.I "cylinder groups".
+A cylinder group is comprised of one or more consecutive
+cylinders on a disk.
+Each cylinder group includes inode slots for files, a
+.I "block map"
+describing available blocks in the cylinder group,
+and summary information describing the usage of data blocks
+within the cylinder group.
+A fixed number of inodes is allocated for each cylinder group
+when the file system is created.
+The current policy is to allocate one inode for each 2048
+bytes of disk space;
+this is expected to be far more inodes than will ever be needed.
+.PP
+All the cylinder group bookkeeping information could be
+placed at the beginning of each cylinder group.
+However if this approach were used,
+all the redundant information would be on the top platter.
+A single hardware failure that destroyed the top platter
+could cause the loss of all copies of the redundant super-blocks.
+Thus the cylinder group bookkeeping information
+begins at a floating offset from the beginning of the cylinder group.
+The offset for
+the
+.I "i+1" st
+cylinder group is about one track further
+from the beginning of the cylinder group
+than it was for the
+.I "i" th
+cylinder group.
+In this way,
+the redundant
+information spirals down into the pack;
+any single track, cylinder,
+or platter can be lost without losing all copies of the super-blocks.
+Except for the first cylinder group,
+the space between the beginning of the cylinder group
+and the beginning of the cylinder group information stores data.
+.NH 2
+Fragments
+.PP
+To avoid waste in storing small files,
+the file system space allocator divides a single
+file system block into one or more
+.I "fragments".
+The fragmentation of the file system is specified
+when the file system is created;
+each file system block can be optionally broken into
+2, 4, or 8 addressable fragments.
+The lower bound on the size of these fragments is constrained
+by the disk sector size;
+typically 512 bytes is the lower bound on fragment size.
+The block map associated with each cylinder group
+records the space availability at the fragment level.
+Aligned fragments are examined
+to determine block availability.
+.PP
+On a file system with a block size of 4096 bytes
+and a fragment size of 1024 bytes,
+a file is represented by zero or more 4096 byte blocks of data,
+and possibly a single fragmented block.
+If a file system block must be fragmented to obtain
+space for a small amount of data,
+the remainder of the block is made available for allocation
+to other files.
+For example,
+consider an 11000 byte file stored on
+a 4096/1024 byte file system.
+This file uses two full size blocks and a 3072 byte fragment.
+If no fragments with at least 3072 bytes
+are available when the file is created,
+a full size block is split yielding the necessary 3072 byte
+fragment and an unused 1024 byte fragment.
+This remaining fragment can be allocated to another file, as needed.
+.NH 2
+Updates to the file system
+.PP
+Every working day hundreds of files
+are created, modified, and removed.
+Every time a file is modified,
+the operating system performs a
+series of file system updates.
+These updates, when written on disk, yield a consistent file system.
+The file system stages
+all modifications of critical information;
+modification can
+either be completed or cleanly backed out after a crash.
+Knowing the information that is first written to the file system,
+deterministic procedures can be developed to
+repair a corrupted file system.
+To understand this process,
+the order that the update
+requests were being honored must first be understood.
+.PP
+When a user program does an operation to change the file system,
+such as a
+.I write ,
+the data to be written is copied into an internal
+.I "in-core"
+buffer in the kernel.
+Normally, the disk update is handled asynchronously;
+the user process is allowed to proceed even though
+the data has not yet been written to the disk.
+The data,
+along with the inode information reflecting the change,
+is eventually written out to disk.
+The real disk write may not happen until long after the
+.I write
+system call has returned.
+Thus at any given time, the file system,
+as it resides on the disk,
+lags the state of the file system represented by the in-core information.
+.PP
+The disk information is updated to reflect the in-core information
+when the buffer is required for another use,
+when a
+.I sync (2)
+is done (at 30 second intervals) by
+.I "/etc/update" "(8),"
+or by manual operator intervention with the
+.I sync (8)
+command.
+If the system is halted without writing out the in-core information,
+the file system on the disk will be in an inconsistent state.
+.PP
+If all updates are done asynchronously, several serious
+inconsistencies can arise.
+One inconsistency is that a block may be claimed by two inodes.
+Such an inconsistency can occur when the system is halted before
+the pointer to the block in the old inode has been cleared
+in the copy of the old inode on the disk,
+and after the pointer to the block in the new inode has been written out
+to the copy of the new inode on the disk.
+Here,
+there is no deterministic method for deciding
+which inode should really claim the block.
+A similar problem can arise with a multiply claimed inode.
+.PP
+The problem with asynchronous inode updates
+can be avoided by doing all inode deallocations synchronously.
+Consequently,
+inodes and indirect blocks are written to the disk synchronously
+(\fIi.e.\fP the process blocks until the information is
+really written to disk)
+when they are being deallocated.
+Similarly inodes are kept consistent by synchronously
+deleting, adding, or changing directory entries.
+.ds RH Fixing corrupted file systems
diff --git a/sbin/fsck_ffs/SMM.doc/3.t b/sbin/fsck_ffs/SMM.doc/3.t
new file mode 100644
index 0000000..522a222
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/3.t
@@ -0,0 +1,449 @@
+.\" Copyright (c) 1982, 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.
+.\" 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 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.
+.\"
+.\" $FreeBSD$
+.\" @(#)3.t 8.1 (Berkeley) 6/5/93
+.\"
+.ds RH Fixing corrupted file systems
+.NH
+Fixing corrupted file systems
+.PP
+A file system
+can become corrupted in several ways.
+The most common of these ways are
+improper shutdown procedures
+and hardware failures.
+.PP
+File systems may become corrupted during an
+.I "unclean halt" .
+This happens when proper shutdown
+procedures are not observed,
+physically write-protecting a mounted file system,
+or a mounted file system is taken off-line.
+The most common operator procedural failure is forgetting to
+.I sync
+the system before halting the CPU.
+.PP
+File systems may become further corrupted if proper startup
+procedures are not observed, e.g.,
+not checking a file system for inconsistencies,
+and not repairing inconsistencies.
+Allowing a corrupted file system to be used (and, thus, to be modified
+further) can be disastrous.
+.PP
+Any piece of hardware can fail at any time.
+Failures
+can be as subtle as a bad block
+on a disk pack, or as blatant as a non-functional disk-controller.
+.NH 2
+Detecting and correcting corruption
+.PP
+Normally
+.I fsck_ffs
+is run non-interactively.
+In this mode it will only fix
+corruptions that are expected to occur from an unclean halt.
+These actions are a proper subset of the actions that
+.I fsck_ffs
+will take when it is running interactively.
+Throughout this paper we assume that
+.I fsck_ffs
+is being run interactively,
+and all possible errors can be encountered.
+When an inconsistency is discovered in this mode,
+.I fsck_ffs
+reports the inconsistency for the operator to
+chose a corrective action.
+.PP
+A quiescent\(dd
+.FS
+\(dd I.e., unmounted and not being written on.
+.FE
+file system may be checked for structural integrity
+by performing consistency checks on the
+redundant data intrinsic to a file system.
+The redundant data is either read from
+the file system,
+or computed from other known values.
+The file system
+.B must
+be in a quiescent state when
+.I fsck_ffs
+is run,
+since
+.I fsck_ffs
+is a multi-pass program.
+.PP
+In the following sections,
+we discuss methods to discover inconsistencies
+and possible corrective actions
+for the cylinder group blocks, the inodes, the indirect blocks, and
+the data blocks containing directory entries.
+.NH 2
+Super-block checking
+.PP
+The most commonly corrupted item in a file system
+is the summary information
+associated with the super-block.
+The summary information is prone to corruption
+because it is modified with every change to the file
+system's blocks or inodes,
+and is usually corrupted
+after an unclean halt.
+.PP
+The super-block is checked for inconsistencies
+involving file-system size, number of inodes,
+free-block count, and the free-inode count.
+The file-system size must be larger than the
+number of blocks used by the super-block
+and the number of blocks used by the list of inodes.
+The file-system size and layout information
+are the most critical pieces of information for
+.I fsck_ffs .
+While there is no way to actually check these sizes,
+since they are statically determined by
+.I newfs ,
+.I fsck_ffs
+can check that these sizes are within reasonable bounds.
+All other file system checks require that these sizes be correct.
+If
+.I fsck_ffs
+detects corruption in the static parameters of the default super-block,
+.I fsck_ffs
+requests the operator to specify the location of an alternate super-block.
+.NH 2
+Free block checking
+.PP
+.I Fsck_ffs
+checks that all the blocks
+marked as free in the cylinder group block maps
+are not claimed by any files.
+When all the blocks have been initially accounted for,
+.I fsck_ffs
+checks that
+the number of free blocks
+plus the number of blocks claimed by the inodes
+equals the total number of blocks in the file system.
+.PP
+If anything is wrong with the block allocation maps,
+.I fsck_ffs
+will rebuild them,
+based on the list it has computed of allocated blocks.
+.PP
+The summary information associated with the super-block
+counts the total number of free blocks within the file system.
+.I Fsck_ffs
+compares this count to the
+number of free blocks it found within the file system.
+If the two counts do not agree, then
+.I fsck_ffs
+replaces the incorrect count in the summary information
+by the actual free-block count.
+.PP
+The summary information
+counts the total number of free inodes within the file system.
+.I Fsck_ffs
+compares this count to the number
+of free inodes it found within the file system.
+If the two counts do not agree, then
+.I fsck_ffs
+replaces the incorrect count in the
+summary information by the actual free-inode count.
+.NH 2
+Checking the inode state
+.PP
+An individual inode is not as likely to be corrupted as
+the allocation information.
+However, because of the great number of active inodes,
+a few of the inodes are usually corrupted.
+.PP
+The list of inodes in the file system
+is checked sequentially starting with inode 2
+(inode 0 marks unused inodes;
+inode 1 is saved for future generations)
+and progressing through the last inode in the file system.
+The state of each inode is checked for
+inconsistencies involving format and type,
+link count,
+duplicate blocks,
+bad blocks,
+and inode size.
+.PP
+Each inode contains a mode word.
+This mode word describes the type and state of the inode.
+Inodes must be one of six types:
+regular inode, directory inode, symbolic link inode,
+special block inode, special character inode, or socket inode.
+Inodes may be found in one of three allocation states:
+unallocated, allocated, and neither unallocated nor allocated.
+This last state suggests an incorrectly formated inode.
+An inode can get in this state if
+bad data is written into the inode list.
+The only possible corrective action is for
+.I fsck_ffs
+is to clear the inode.
+.NH 2
+Inode links
+.PP
+Each inode counts the
+total number of directory entries
+linked to the inode.
+.I Fsck_ffs
+verifies the link count of each inode
+by starting at the root of the file system,
+and descending through the directory structure.
+The actual link count for each inode
+is calculated during the descent.
+.PP
+If the stored link count is non-zero and the actual
+link count is zero,
+then no directory entry appears for the inode.
+If this happens,
+.I fsck_ffs
+will place the disconnected file in the
+.I lost+found
+directory.
+If the stored and actual link counts are non-zero and unequal,
+a directory entry may have been added or removed without the inode being
+updated.
+If this happens,
+.I fsck_ffs
+replaces the incorrect stored link count by the actual link count.
+.PP
+Each inode contains a list,
+or pointers to
+lists (indirect blocks),
+of all the blocks claimed by the inode.
+Since indirect blocks are owned by an inode,
+inconsistencies in indirect blocks directly
+affect the inode that owns it.
+.PP
+.I Fsck_ffs
+compares each block number claimed by an inode
+against a list of already allocated blocks.
+If another inode already claims a block number,
+then the block number is added to a list of
+.I "duplicate blocks" .
+Otherwise, the list of allocated blocks
+is updated to include the block number.
+.PP
+If there are any duplicate blocks,
+.I fsck_ffs
+will perform a partial second
+pass over the inode list
+to find the inode of the duplicated block.
+The second pass is needed,
+since without examining the files associated with
+these inodes for correct content,
+not enough information is available
+to determine which inode is corrupted and should be cleared.
+If this condition does arise
+(only hardware failure will cause it),
+then the inode with the earliest
+modify time is usually incorrect,
+and should be cleared.
+If this happens,
+.I fsck_ffs
+prompts the operator to clear both inodes.
+The operator must decide which one should be kept
+and which one should be cleared.
+.PP
+.I Fsck_ffs
+checks the range of each block number claimed by an inode.
+If the block number is
+lower than the first data block in the file system,
+or greater than the last data block,
+then the block number is a
+.I "bad block number" .
+Many bad blocks in an inode are usually caused by
+an indirect block that was not written to the file system,
+a condition which can only occur if there has been a hardware failure.
+If an inode contains bad block numbers,
+.I fsck_ffs
+prompts the operator to clear it.
+.NH 2
+Inode data size
+.PP
+Each inode contains a count of the number of data blocks
+that it contains.
+The number of actual data blocks
+is the sum of the allocated data blocks
+and the indirect blocks.
+.I Fsck_ffs
+computes the actual number of data blocks
+and compares that block count against
+the actual number of blocks the inode claims.
+If an inode contains an incorrect count
+.I fsck_ffs
+prompts the operator to fix it.
+.PP
+Each inode contains a thirty-two bit size field.
+The size is the number of data bytes
+in the file associated with the inode.
+The consistency of the byte size field is roughly checked
+by computing from the size field the maximum number of blocks
+that should be associated with the inode,
+and comparing that expected block count against
+the actual number of blocks the inode claims.
+.NH 2
+Checking the data associated with an inode
+.PP
+An inode can directly or indirectly
+reference three kinds of data blocks.
+All referenced blocks must be the same kind.
+The three types of data blocks are:
+plain data blocks, symbolic link data blocks, and directory data blocks.
+Plain data blocks
+contain the information stored in a file;
+symbolic link data blocks
+contain the path name stored in a link.
+Directory data blocks contain directory entries.
+.I Fsck_ffs
+can only check the validity of directory data blocks.
+.PP
+Each directory data block is checked for
+several types of inconsistencies.
+These inconsistencies include
+directory inode numbers pointing to unallocated inodes,
+directory inode numbers that are greater than
+the number of inodes in the file system,
+incorrect directory inode numbers for ``\fB.\fP'' and ``\fB..\fP'',
+and directories that are not attached to the file system.
+If the inode number in a directory data block
+references an unallocated inode,
+then
+.I fsck_ffs
+will remove that directory entry.
+Again,
+this condition can only arise when there has been a hardware failure.
+.PP
+.I Fsck_ffs
+also checks for directories with unallocated blocks (holes).
+Such directories should never be created.
+When found,
+.I fsck_ffs
+will prompt the user to adjust the length of the offending directory
+which is done by shortening the size of the directory to the end of the
+last allocated block preceding the hole.
+Unfortunately, this means that another Phase 1 run has to be done.
+.I Fsck_ffs
+will remind the user to rerun fsck_ffs after repairing a
+directory containing an unallocated block.
+.PP
+If a directory entry inode number references
+outside the inode list, then
+.I fsck_ffs
+will remove that directory entry.
+This condition occurs if bad data is written into a directory data block.
+.PP
+The directory inode number entry for ``\fB.\fP''
+must be the first entry in the directory data block.
+The inode number for ``\fB.\fP''
+must reference itself;
+e.g., it must equal the inode number
+for the directory data block.
+The directory inode number entry
+for ``\fB..\fP'' must be
+the second entry in the directory data block.
+Its value must equal the inode number for the
+parent of the directory entry
+(or the inode number of the directory
+data block if the directory is the
+root directory).
+If the directory inode numbers are
+incorrect,
+.I fsck_ffs
+will replace them with the correct values.
+If there are multiple hard links to a directory,
+the first one encountered is considered the real parent
+to which ``\fB..\fP'' should point;
+\fIfsck_ffs\fP recommends deletion for the subsequently discovered names.
+.NH 2
+File system connectivity
+.PP
+.I Fsck_ffs
+checks the general connectivity of the file system.
+If directories are not linked into the file system, then
+.I fsck_ffs
+links the directory back into the file system in the
+.I lost+found
+directory.
+This condition only occurs when there has been a hardware failure.
+.ds RH "References"
+.SH
+\s+2Acknowledgements\s0
+.PP
+I thank Bill Joy, Sam Leffler, Robert Elz and Dennis Ritchie
+for their suggestions and help in implementing the new file system.
+Thanks also to Robert Henry for his editorial input to
+get this document together.
+Finally we thank our sponsors,
+the National Science Foundation under grant MCS80-05144,
+and the Defense Advance Research Projects Agency (DoD) under
+Arpa Order No. 4031 monitored by Naval Electronic System Command under
+Contract No. N00039-82-C-0235. (Kirk McKusick, July 1983)
+.PP
+I would like to thank Larry A. Wehr for advice that lead
+to the first version of
+.I fsck_ffs
+and Rick B. Brandt for adapting
+.I fsck_ffs
+to
+UNIX/TS. (T. Kowalski, July 1979)
+.sp 2
+.SH
+\s+2References\s0
+.LP
+.IP [Dolotta78] 20
+Dolotta, T. A., and Olsson, S. B. eds.,
+.I "UNIX User's Manual, Edition 1.1\^" ,
+January 1978.
+.IP [Joy83] 20
+Joy, W., Cooper, E., Fabry, R., Leffler, S., McKusick, M., and Mosher, D.
+4.2BSD System Manual,
+.I "University of California at Berkeley" ,
+.I "Computer Systems Research Group Technical Report"
+#4, 1982.
+.IP [McKusick84] 20
+McKusick, M., Joy, W., Leffler, S., and Fabry, R.
+A Fast File System for UNIX,
+\fIACM Transactions on Computer Systems 2\fP, 3.
+pp. 181-197, August 1984.
+.IP [Ritchie78] 20
+Ritchie, D. M., and Thompson, K.,
+The UNIX Time-Sharing System,
+.I "The Bell System Technical Journal"
+.B 57 ,
+6 (July-August 1978, Part 2), pp. 1905-29.
+.IP [Thompson78] 20
+Thompson, K.,
+UNIX Implementation,
+.I "The Bell System Technical Journal\^"
+.B 57 ,
+6 (July-August 1978, Part 2), pp. 1931-46.
+.ds RH Appendix A \- Fsck_ffs Error Conditions
+.bp
diff --git a/sbin/fsck_ffs/SMM.doc/4.t b/sbin/fsck_ffs/SMM.doc/4.t
new file mode 100644
index 0000000..353fab3
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/4.t
@@ -0,0 +1,1421 @@
+.\" Copyright (c) 1982, 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.
+.\" 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 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.
+.\"
+.\" $FreeBSD$
+.\" @(#)4.t 8.1 (Berkeley) 6/5/93
+.\"
+.ds RH Appendix A \- Fsck_ffs Error Conditions
+.NH
+Appendix A \- Fsck_ffs Error Conditions
+.NH 2
+Conventions
+.PP
+.I Fsck_ffs
+is
+a multi-pass file system check program.
+Each file system pass invokes a different Phase of the
+.I fsck_ffs
+program.
+After the initial setup,
+.I fsck_ffs
+performs successive Phases over each file system,
+checking blocks and sizes,
+path-names,
+connectivity,
+reference counts,
+and the map of free blocks,
+(possibly rebuilding it),
+and performs some cleanup.
+.LP
+Normally
+.I fsck_ffs
+is run non-interactively to
+.I preen
+the file systems after an unclean halt.
+While preen'ing a file system,
+it will only fix corruptions that are expected
+to occur from an unclean halt.
+These actions are a proper subset of the actions that
+.I fsck_ffs
+will take when it is running interactively.
+Throughout this appendix many errors have several options
+that the operator can take.
+When an inconsistency is detected,
+.I fsck_ffs
+reports the error condition to the operator.
+If a response is required,
+.I fsck_ffs
+prints a prompt message and
+waits for a response.
+When preen'ing most errors are fatal.
+For those that are expected,
+the response taken is noted.
+This appendix explains the meaning of each error condition,
+the possible responses, and the related error conditions.
+.LP
+The error conditions are organized by the
+.I Phase
+of the
+.I fsck_ffs
+program in which they can occur.
+The error conditions that may occur
+in more than one Phase
+will be discussed in initialization.
+.NH 2
+Initialization
+.PP
+Before a file system check can be performed, certain
+tables have to be set up and certain files opened.
+This section concerns itself with the opening of files and
+the initialization of tables.
+This section lists error conditions resulting from
+command line options,
+memory requests,
+opening of files,
+status of files,
+file system size checks,
+and creation of the scratch file.
+All the initialization errors are fatal
+when the file system is being preen'ed.
+.sp
+.LP
+.B "\fIC\fP option?"
+.br
+\fIC\fP is not a legal option to
+.I fsck_ffs ;
+legal options are \-b, \-c, \-y, \-n, and \-p.
+.I Fsck_ffs
+terminates on this error condition.
+See the
+.I fsck_ffs (8)
+manual entry for further detail.
+.sp
+.LP
+.B "cannot alloc NNN bytes for blockmap"
+.br
+.B "cannot alloc NNN bytes for freemap"
+.br
+.B "cannot alloc NNN bytes for statemap"
+.br
+.B "cannot alloc NNN bytes for lncntp"
+.br
+.I Fsck_ffs 's
+request for memory for its virtual
+memory tables failed.
+This should never happen.
+.I Fsck_ffs
+terminates on this error condition.
+See a guru.
+.sp
+.LP
+.B "Can't open checklist file: \fIF\fP"
+.br
+The file system checklist file
+\fIF\fP (usually
+.I /etc/fstab )
+can not be opened for reading.
+.I Fsck_ffs
+terminates on this error condition.
+Check access modes of \fIF\fP.
+.sp
+.LP
+.B "Can't stat root"
+.br
+.I Fsck_ffs 's
+request for statistics about the root directory ``/'' failed.
+This should never happen.
+.I Fsck_ffs
+terminates on this error condition.
+See a guru.
+.sp
+.LP
+.B "Can't stat \fIF\fP"
+.br
+.B "Can't make sense out of name \fIF\fP"
+.br
+.I Fsck_ffs 's
+request for statistics about the file system \fIF\fP failed.
+When running manually,
+it ignores this file system
+and continues checking the next file system given.
+Check access modes of \fIF\fP.
+.sp
+.LP
+.B "Can't open \fIF\fP"
+.br
+.I Fsck_ffs 's
+request attempt to open the file system \fIF\fP failed.
+When running manually, it ignores this file system
+and continues checking the next file system given.
+Check access modes of \fIF\fP.
+.sp
+.LP
+.B "\fIF\fP: (NO WRITE)"
+.br
+Either the \-n flag was specified or
+.I fsck_ffs 's
+attempt to open the file system \fIF\fP for writing failed.
+When running manually,
+all the diagnostics are printed out,
+but no modifications are attempted to fix them.
+.sp
+.LP
+.B "file is not a block or character device; OK"
+.br
+You have given
+.I fsck_ffs
+a regular file name by mistake.
+Check the type of the file specified.
+.LP
+Possible responses to the OK prompt are:
+.IP YES
+ignore this error condition.
+.IP NO
+ignore this file system and continues checking
+the next file system given.
+.sp
+.LP
+.B "UNDEFINED OPTIMIZATION IN SUPERBLOCK (SET TO DEFAULT)"
+.br
+The superblock optimization parameter is neither OPT_TIME
+nor OPT_SPACE.
+.LP
+Possible responses to the SET TO DEFAULT prompt are:
+.IP YES
+The superblock is set to request optimization to minimize
+running time of the system.
+(If optimization to minimize disk space utilization is
+desired, it can be set using \fItunefs\fP(8).)
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "IMPOSSIBLE MINFREE=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)"
+.br
+The superblock minimum space percentage is greater than 99%
+or less then 0%.
+.LP
+Possible responses to the SET TO DEFAULT prompt are:
+.IP YES
+The minfree parameter is set to 10%.
+(If some other percentage is desired,
+it can be set using \fItunefs\fP(8).)
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "IMPOSSIBLE INTERLEAVE=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)"
+.br
+The file system interleave is less than or equal to zero.
+.LP
+Possible responses to the SET TO DEFAULT prompt are:
+.IP YES
+The interleave parameter is set to 1.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "IMPOSSIBLE NPSECT=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)"
+.br
+The number of physical sectors per track is less than the number
+of usable sectors per track.
+.LP
+Possible responses to the SET TO DEFAULT prompt are:
+.IP YES
+The npsect parameter is set to the number of usable sectors per track.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+One of the following messages will appear:
+.br
+.B "MAGIC NUMBER WRONG"
+.br
+.B "NCG OUT OF RANGE"
+.br
+.B "CPG OUT OF RANGE"
+.br
+.B "NCYL DOES NOT JIVE WITH NCG*CPG"
+.br
+.B "SIZE PREPOSTEROUSLY LARGE"
+.br
+.B "TRASHED VALUES IN SUPER BLOCK"
+.br
+and will be followed by the message:
+.br
+.B "\fIF\fP: BAD SUPER BLOCK: \fIB\fP"
+.br
+.B "USE -b OPTION TO FSCK_FFS TO SPECIFY LOCATION OF AN ALTERNATE"
+.br
+.B "SUPER-BLOCK TO SUPPLY NEEDED INFORMATION; SEE fsck_ffs(8)."
+.br
+The super block has been corrupted.
+An alternative super block must be selected from among those
+listed by
+.I newfs
+(8) when the file system was created.
+For file systems with a blocksize less than 32K,
+specifying \-b 32 is a good first choice.
+.sp
+.LP
+.B "INTERNAL INCONSISTENCY: \fIM\fP"
+.br
+.I Fsck_ffs 's
+has had an internal panic, whose message is specified as \fIM\fP.
+This should never happen.
+See a guru.
+.sp
+.LP
+.B "CAN NOT SEEK: BLK \fIB\fP (CONTINUE)"
+.br
+.I Fsck_ffs 's
+request for moving to a specified block number \fIB\fP in
+the file system failed.
+This should never happen.
+See a guru.
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+attempt to continue to run the file system check.
+Often,
+however the problem will persist.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck_ffs
+should be made to re-check this file system.
+If the block was part of the virtual memory buffer
+cache,
+.I fsck_ffs
+will terminate with the message ``Fatal I/O error''.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "CAN NOT READ: BLK \fIB\fP (CONTINUE)"
+.br
+.I Fsck_ffs 's
+request for reading a specified block number \fIB\fP in
+the file system failed.
+This should never happen.
+See a guru.
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+attempt to continue to run the file system check.
+It will retry the read and print out the message:
+.br
+.B "THE FOLLOWING SECTORS COULD NOT BE READ: \fIN\fP"
+.br
+where \fIN\fP indicates the sectors that could not be read.
+If
+.I fsck_ffs
+ever tries to write back one of the blocks on which the read failed
+it will print the message:
+.br
+.B "WRITING ZERO'ED BLOCK \fIN\fP TO DISK"
+.br
+where \fIN\fP indicates the sector that was written with zero's.
+If the disk is experiencing hardware problems, the problem will persist.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck_ffs
+should be made to re-check this file system.
+If the block was part of the virtual memory buffer
+cache,
+.I fsck_ffs
+will terminate with the message ``Fatal I/O error''.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "CAN NOT WRITE: BLK \fIB\fP (CONTINUE)"
+.br
+.I Fsck_ffs 's
+request for writing a specified block number \fIB\fP
+in the file system failed.
+The disk is write-protected;
+check the write protect lock on the drive.
+If that is not the problem, see a guru.
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+attempt to continue to run the file system check.
+The write operation will be retried with the failed blocks
+indicated by the message:
+.br
+.B "THE FOLLOWING SECTORS COULD NOT BE WRITTEN: \fIN\fP"
+.br
+where \fIN\fP indicates the sectors that could not be written.
+If the disk is experiencing hardware problems, the problem will persist.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck_ffs
+should be made to re-check this file system.
+If the block was part of the virtual memory buffer
+cache,
+.I fsck_ffs
+will terminate with the message ``Fatal I/O error''.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "bad inode number DDD to ginode"
+.br
+An internal error has attempted to read non-existent inode \fIDDD\fP.
+This error causes
+.I fsck_ffs
+to exit.
+See a guru.
+.NH 2
+Phase 1 \- Check Blocks and Sizes
+.PP
+This phase concerns itself with
+the inode list.
+This section lists error conditions resulting from
+checking inode types,
+setting up the zero-link-count table,
+examining inode block numbers for bad or duplicate blocks,
+checking inode size,
+and checking inode format.
+All errors in this phase except
+.B "INCORRECT BLOCK COUNT"
+and
+.B "PARTIALLY TRUNCATED INODE"
+are fatal if the file system is being preen'ed.
+.sp
+.LP
+.B "UNKNOWN FILE TYPE I=\fII\fP (CLEAR)"
+.br
+The mode word of the inode \fII\fP indicates that the inode is not a
+special block inode, special character inode, socket inode, regular inode,
+symbolic link, or directory inode.
+.LP
+Possible responses to the CLEAR prompt are:
+.IP YES
+de-allocate inode \fII\fP by zeroing its contents.
+This will always invoke the UNALLOCATED error condition in Phase 2
+for each directory entry pointing to this inode.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "PARTIALLY TRUNCATED INODE I=\fII\fP (SALVAGE)"
+.br
+.I Fsck_ffs
+has found inode \fII\fP whose size is shorter than the number of
+blocks allocated to it.
+This condition should only occur if the system crashes while in the
+midst of truncating a file.
+When preen'ing the file system,
+.I fsck_ffs
+completes the truncation to the specified size.
+.LP
+Possible responses to SALVAGE are:
+.IP YES
+complete the truncation to the size specified in the inode.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "LINK COUNT TABLE OVERFLOW (CONTINUE)"
+.br
+An internal table for
+.I fsck_ffs
+containing allocated inodes with a link count of
+zero cannot allocate more memory.
+Increase the virtual memory for
+.I fsck_ffs .
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+continue with the program.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck_ffs
+should be made to re-check this file system.
+If another allocated inode with a zero link count is found,
+this error condition is repeated.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "\fIB\fP BAD I=\fII\fP"
+.br
+Inode \fII\fP contains block number \fIB\fP with a number
+lower than the number of the first data block in the file system or
+greater than the number of the last block
+in the file system.
+This error condition may invoke the
+.B "EXCESSIVE BAD BLKS"
+error condition in Phase 1 (see next paragraph) if
+inode \fII\fP has too many block numbers outside the file system range.
+This error condition will always invoke the
+.B "BAD/DUP"
+error condition in Phase 2 and Phase 4.
+.sp
+.LP
+.B "EXCESSIVE BAD BLKS I=\fII\fP (CONTINUE)"
+.br
+There is more than a tolerable number (usually 10) of blocks with a number
+lower than the number of the first data block in the file system or greater than
+the number of last block in the file system associated with inode \fII\fP.
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+ignore the rest of the blocks in this inode
+and continue checking with the next inode in the file system.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck_ffs
+should be made to re-check this file system.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "BAD STATE DDD TO BLKERR"
+.br
+An internal error has scrambled
+.I fsck_ffs 's
+state map to have the impossible value \fIDDD\fP.
+.I Fsck_ffs
+exits immediately.
+See a guru.
+.sp
+.LP
+.B "\fIB\fP DUP I=\fII\fP"
+.br
+Inode \fII\fP contains block number \fIB\fP that is already claimed by
+another inode.
+This error condition may invoke the
+.B "EXCESSIVE DUP BLKS"
+error condition in Phase 1 if
+inode \fII\fP has too many block numbers claimed by other inodes.
+This error condition will always invoke Phase 1b and the
+.B "BAD/DUP"
+error condition in Phase 2 and Phase 4.
+.sp
+.LP
+.B "EXCESSIVE DUP BLKS I=\fII\fP (CONTINUE)"
+.br
+There is more than a tolerable number (usually 10) of blocks claimed by other
+inodes.
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+ignore the rest of the blocks in this inode
+and continue checking with the next inode in the file system.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck_ffs
+should be made to re-check this file system.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "DUP TABLE OVERFLOW (CONTINUE)"
+.br
+An internal table in
+.I fsck_ffs
+containing duplicate block numbers cannot allocate any more space.
+Increase the amount of virtual memory available to
+.I fsck_ffs .
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+continue with the program.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck_ffs
+should be made to re-check this file system.
+If another duplicate block is found, this error condition will repeat.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "PARTIALLY ALLOCATED INODE I=\fII\fP (CLEAR)"
+.br
+Inode \fII\fP is neither allocated nor unallocated.
+.LP
+Possible responses to the CLEAR prompt are:
+.IP YES
+de-allocate inode \fII\fP by zeroing its contents.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "INCORRECT BLOCK COUNT I=\fII\fP (\fIX\fP should be \fIY\fP) (CORRECT)"
+.br
+The block count for inode \fII\fP is \fIX\fP blocks,
+but should be \fIY\fP blocks.
+When preen'ing the count is corrected.
+.LP
+Possible responses to the CORRECT prompt are:
+.IP YES
+replace the block count of inode \fII\fP with \fIY\fP.
+.IP NO
+ignore this error condition.
+.NH 2
+Phase 1B: Rescan for More Dups
+.PP
+When a duplicate block is found in the file system, the file system is
+rescanned to find the inode that previously claimed that block.
+This section lists the error condition when the duplicate block is found.
+.sp
+.LP
+.B "\fIB\fP DUP I=\fII\fP"
+.br
+Inode \fII\fP contains block number \fIB\fP that
+is already claimed by another inode.
+This error condition will always invoke the
+.B "BAD/DUP"
+error condition in Phase 2.
+You can determine which inodes have overlapping blocks by examining
+this error condition and the DUP error condition in Phase 1.
+.NH 2
+Phase 2 \- Check Pathnames
+.PP
+This phase concerns itself with removing directory entries
+pointing to
+error conditioned inodes
+from Phase 1 and Phase 1b.
+This section lists error conditions resulting from
+root inode mode and status,
+directory inode pointers in range,
+and directory entries pointing to bad inodes,
+and directory integrity checks.
+All errors in this phase are fatal if the file system is being preen'ed,
+except for directories not being a multiple of the blocks size
+and extraneous hard links.
+.sp
+.LP
+.B "ROOT INODE UNALLOCATED (ALLOCATE)"
+.br
+The root inode (usually inode number 2) has no allocate mode bits.
+This should never happen.
+.LP
+Possible responses to the ALLOCATE prompt are:
+.IP YES
+allocate inode 2 as the root inode.
+The files and directories usually found in the root will be recovered
+in Phase 3 and put into
+.I lost+found .
+If the attempt to allocate the root fails,
+.I fsck_ffs
+will exit with the message:
+.br
+.B "CANNOT ALLOCATE ROOT INODE" .
+.IP NO
+.I fsck_ffs
+will exit.
+.sp
+.LP
+.B "ROOT INODE NOT DIRECTORY (REALLOCATE)"
+.br
+The root inode (usually inode number 2)
+is not directory inode type.
+.LP
+Possible responses to the REALLOCATE prompt are:
+.IP YES
+clear the existing contents of the root inode
+and reallocate it.
+The files and directories usually found in the root will be recovered
+in Phase 3 and put into
+.I lost+found .
+If the attempt to allocate the root fails,
+.I fsck_ffs
+will exit with the message:
+.br
+.B "CANNOT ALLOCATE ROOT INODE" .
+.IP NO
+.I fsck_ffs
+will then prompt with
+.B "FIX"
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+replace the root inode's type to be a directory.
+If the root inode's data blocks are not directory blocks,
+many error conditions will be produced.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "DUPS/BAD IN ROOT INODE (REALLOCATE)"
+.br
+Phase 1 or Phase 1b have found duplicate blocks
+or bad blocks in the root inode (usually inode number 2) for the file system.
+.LP
+Possible responses to the REALLOCATE prompt are:
+.IP YES
+clear the existing contents of the root inode
+and reallocate it.
+The files and directories usually found in the root will be recovered
+in Phase 3 and put into
+.I lost+found .
+If the attempt to allocate the root fails,
+.I fsck_ffs
+will exit with the message:
+.br
+.B "CANNOT ALLOCATE ROOT INODE" .
+.IP NO
+.I fsck_ffs
+will then prompt with
+.B "CONTINUE" .
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+ignore the
+.B "DUPS/BAD"
+error condition in the root inode and
+attempt to continue to run the file system check.
+If the root inode is not correct,
+then this may result in many other error conditions.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "NAME TOO LONG \fIF\fP"
+.br
+An excessively long path name has been found.
+This usually indicates loops in the file system name space.
+This can occur if the super user has made circular links to directories.
+The offending links must be removed (by a guru).
+.sp
+.LP
+.B "I OUT OF RANGE I=\fII\fP NAME=\fIF\fP (REMOVE)"
+.br
+A directory entry \fIF\fP has an inode number \fII\fP that is greater than
+the end of the inode list.
+.LP
+Possible responses to the REMOVE prompt are:
+.IP YES
+the directory entry \fIF\fP is removed.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "UNALLOCATED I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP \fItype\fP=\fIF\fP (REMOVE)"
+.br
+A directory or file entry \fIF\fP points to an unallocated inode \fII\fP.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP,
+and name \fIF\fP are printed.
+.LP
+Possible responses to the REMOVE prompt are:
+.IP YES
+the directory entry \fIF\fP is removed.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "DUP/BAD I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP \fItype\fP=\fIF\fP (REMOVE)"
+.br
+Phase 1 or Phase 1b have found duplicate blocks or bad blocks
+associated with directory or file entry \fIF\fP, inode \fII\fP.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP,
+and directory name \fIF\fP are printed.
+.LP
+Possible responses to the REMOVE prompt are:
+.IP YES
+the directory entry \fIF\fP is removed.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "ZERO LENGTH DIRECTORY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (REMOVE)"
+.br
+A directory entry \fIF\fP has a size \fIS\fP that is zero.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP,
+and directory name \fIF\fP are printed.
+.LP
+Possible responses to the REMOVE prompt are:
+.IP YES
+the directory entry \fIF\fP is removed;
+this will always invoke the BAD/DUP error condition in Phase 4.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "DIRECTORY TOO SHORT I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fIF\fP has been found whose size \fIS\fP
+is less than the minimum size directory.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP,
+and directory name \fIF\fP are printed.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+increase the size of the directory to the minimum directory size.
+.IP NO
+ignore this directory.
+.sp
+.LP
+.B "DIRECTORY \fIF\fP LENGTH \fIS\fP NOT MULTIPLE OF \fIB\fP (ADJUST)
+.br
+A directory \fIF\fP has been found with size \fIS\fP that is not
+a multiple of the directory blocksize \fIB\fP.
+.LP
+Possible responses to the ADJUST prompt are:
+.IP YES
+the length is rounded up to the appropriate block size.
+This error can occur on 4.2BSD file systems.
+Thus when preen'ing the file system only a warning is printed
+and the directory is adjusted.
+.IP NO
+ignore the error condition.
+.sp
+.LP
+.B "DIRECTORY CORRUPTED I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (SALVAGE)"
+.br
+A directory with an inconsistent internal state has been found.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+throw away all entries up to the next directory boundary (usually 512-byte)
+boundary.
+This drastic action can throw away up to 42 entries,
+and should be taken only after other recovery efforts have failed.
+.IP NO
+skip up to the next directory boundary and resume reading,
+but do not modify the directory.
+.sp
+.LP
+.B "BAD INODE NUMBER FOR `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found whose inode number for `.' does
+does not equal \fII\fP.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+change the inode number for `.' to be equal to \fII\fP.
+.IP NO
+leave the inode number for `.' unchanged.
+.sp
+.LP
+.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found whose first entry is unallocated.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+build an entry for `.' with inode number equal to \fII\fP.
+.IP NO
+leave the directory unchanged.
+.sp
+.LP
+.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP"
+.br
+.B "CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS \fIF\fP"
+.br
+A directory \fII\fP has been found whose first entry is \fIF\fP.
+.I Fsck_ffs
+cannot resolve this problem.
+The file system should be mounted and the offending entry \fIF\fP
+moved elsewhere.
+The file system should then be unmounted and
+.I fsck_ffs
+should be run again.
+.sp
+.LP
+.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP"
+.br
+.B "CANNOT FIX, INSUFFICIENT SPACE TO ADD `.'"
+.br
+A directory \fII\fP has been found whose first entry is not `.'.
+.I Fsck_ffs
+cannot resolve this problem as it should never happen.
+See a guru.
+.sp
+.LP
+.B "EXTRA `.' ENTRY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found that has more than one entry for `.'.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+remove the extra entry for `.'.
+.IP NO
+leave the directory unchanged.
+.sp
+.LP
+.B "BAD INODE NUMBER FOR `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found whose inode number for `..' does
+does not equal the parent of \fII\fP.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+change the inode number for `..' to be equal to the parent of \fII\fP
+(``\fB..\fP'' in the root inode points to itself).
+.IP NO
+leave the inode number for `..' unchanged.
+.sp
+.LP
+.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found whose second entry is unallocated.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+build an entry for `..' with inode number equal to the parent of \fII\fP
+(``\fB..\fP'' in the root inode points to itself).
+.IP NO
+leave the directory unchanged.
+.sp
+.LP
+.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP"
+.br
+.B "CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS \fIF\fP"
+.br
+A directory \fII\fP has been found whose second entry is \fIF\fP.
+.I Fsck_ffs
+cannot resolve this problem.
+The file system should be mounted and the offending entry \fIF\fP
+moved elsewhere.
+The file system should then be unmounted and
+.I fsck_ffs
+should be run again.
+.sp
+.LP
+.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP"
+.br
+.B "CANNOT FIX, INSUFFICIENT SPACE TO ADD `..'"
+.br
+A directory \fII\fP has been found whose second entry is not `..'.
+.I Fsck_ffs
+cannot resolve this problem.
+The file system should be mounted and the second entry in the directory
+moved elsewhere.
+The file system should then be unmounted and
+.I fsck_ffs
+should be run again.
+.sp
+.LP
+.B "EXTRA `..' ENTRY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found that has more than one entry for `..'.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+remove the extra entry for `..'.
+.IP NO
+leave the directory unchanged.
+.sp
+.LP
+.B "\fIN\fP IS AN EXTRANEOUS HARD LINK TO A DIRECTORY \fID\fP (REMOVE)
+.br
+.I Fsck_ffs
+has found a hard link, \fIN\fP, to a directory, \fID\fP.
+When preen'ing the extraneous links are ignored.
+.LP
+Possible responses to the REMOVE prompt are:
+.IP YES
+delete the extraneous entry, \fIN\fP.
+.IP NO
+ignore the error condition.
+.sp
+.LP
+.B "BAD INODE \fIS\fP TO DESCEND"
+.br
+An internal error has caused an impossible state \fIS\fP to be passed to the
+routine that descends the file system directory structure.
+.I Fsck_ffs
+exits.
+See a guru.
+.sp
+.LP
+.B "BAD RETURN STATE \fIS\fP FROM DESCEND"
+.br
+An internal error has caused an impossible state \fIS\fP to be returned
+from the routine that descends the file system directory structure.
+.I Fsck_ffs
+exits.
+See a guru.
+.sp
+.LP
+.B "BAD STATE \fIS\fP FOR ROOT INODE"
+.br
+An internal error has caused an impossible state \fIS\fP to be assigned
+to the root inode.
+.I Fsck_ffs
+exits.
+See a guru.
+.NH 2
+Phase 3 \- Check Connectivity
+.PP
+This phase concerns itself with the directory connectivity seen in
+Phase 2.
+This section lists error conditions resulting from
+unreferenced directories,
+and missing or full
+.I lost+found
+directories.
+.sp
+.LP
+.B "UNREF DIR I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (RECONNECT)"
+.br
+The directory inode \fII\fP was not connected to a directory entry
+when the file system was traversed.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and
+modify time \fIT\fP of directory inode \fII\fP are printed.
+When preen'ing, the directory is reconnected if its size is non-zero,
+otherwise it is cleared.
+.LP
+Possible responses to the RECONNECT prompt are:
+.IP YES
+reconnect directory inode \fII\fP to the file system in the
+directory for lost files (usually \fIlost+found\fP).
+This may invoke the
+.I lost+found
+error condition in Phase 3
+if there are problems connecting directory inode \fII\fP to \fIlost+found\fP.
+This may also invoke the CONNECTED error condition in Phase 3 if the link
+was successful.
+.IP NO
+ignore this error condition.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "NO lost+found DIRECTORY (CREATE)"
+.br
+There is no
+.I lost+found
+directory in the root directory of the file system;
+When preen'ing
+.I fsck_ffs
+tries to create a \fIlost+found\fP directory.
+.LP
+Possible responses to the CREATE prompt are:
+.IP YES
+create a \fIlost+found\fP directory in the root of the file system.
+This may raise the message:
+.br
+.B "NO SPACE LEFT IN / (EXPAND)"
+.br
+See below for the possible responses.
+Inability to create a \fIlost+found\fP directory generates the message:
+.br
+.B "SORRY. CANNOT CREATE lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "lost+found IS NOT A DIRECTORY (REALLOCATE)"
+.br
+The entry for
+.I lost+found
+is not a directory.
+.LP
+Possible responses to the REALLOCATE prompt are:
+.IP YES
+allocate a directory inode, and change \fIlost+found\fP to reference it.
+The previous inode reference by the \fIlost+found\fP name is not cleared.
+Thus it will either be reclaimed as an UNREF'ed inode or have its
+link count ADJUST'ed later in this Phase.
+Inability to create a \fIlost+found\fP directory generates the message:
+.br
+.B "SORRY. CANNOT CREATE lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "NO SPACE LEFT IN /lost+found (EXPAND)"
+.br
+There is no space to add another entry to the
+.I lost+found
+directory in the root directory
+of the file system.
+When preen'ing the
+.I lost+found
+directory is expanded.
+.LP
+Possible responses to the EXPAND prompt are:
+.IP YES
+the
+.I lost+found
+directory is expanded to make room for the new entry.
+If the attempted expansion fails
+.I fsck_ffs
+prints the message:
+.br
+.B "SORRY. NO SPACE IN lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+Clean out unnecessary entries in
+.I lost+found .
+This error is fatal if the file system is being preen'ed.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "DIR I=\fII1\fP CONNECTED. PARENT WAS I=\fII2\fP"
+.br
+This is an advisory message indicating a directory inode \fII1\fP was
+successfully connected to the
+.I lost+found
+directory.
+The parent inode \fII2\fP of the directory inode \fII1\fP is
+replaced by the inode number of the
+.I lost+found
+directory.
+.sp
+.LP
+.B "DIRECTORY \fIF\fP LENGTH \fIS\fP NOT MULTIPLE OF \fIB\fP (ADJUST)
+.br
+A directory \fIF\fP has been found with size \fIS\fP that is not
+a multiple of the directory blocksize \fIB\fP
+(this can reoccur in Phase 3 if it is not adjusted in Phase 2).
+.LP
+Possible responses to the ADJUST prompt are:
+.IP YES
+the length is rounded up to the appropriate block size.
+This error can occur on 4.2BSD file systems.
+Thus when preen'ing the file system only a warning is printed
+and the directory is adjusted.
+.IP NO
+ignore the error condition.
+.sp
+.LP
+.B "BAD INODE \fIS\fP TO DESCEND"
+.br
+An internal error has caused an impossible state \fIS\fP to be passed to the
+routine that descends the file system directory structure.
+.I Fsck_ffs
+exits.
+See a guru.
+.NH 2
+Phase 4 \- Check Reference Counts
+.PP
+This phase concerns itself with the link count information
+seen in Phase 2 and Phase 3.
+This section lists error conditions resulting from
+unreferenced files,
+missing or full
+.I lost+found
+directory,
+incorrect link counts for files, directories, symbolic links, or special files,
+unreferenced files, symbolic links, and directories,
+and bad or duplicate blocks in files, symbolic links, and directories.
+All errors in this phase are correctable if the file system is being preen'ed
+except running out of space in the \fIlost+found\fP directory.
+.sp
+.LP
+.B "UNREF FILE I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (RECONNECT)"
+.br
+Inode \fII\fP was not connected to a directory entry
+when the file system was traversed.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and
+modify time \fIT\fP of inode \fII\fP are printed.
+When preen'ing the file is cleared if either its size or its
+link count is zero,
+otherwise it is reconnected.
+.LP
+Possible responses to the RECONNECT prompt are:
+.IP YES
+reconnect inode \fII\fP to the file system in the directory for
+lost files (usually \fIlost+found\fP).
+This may invoke the
+.I lost+found
+error condition in Phase 4
+if there are problems connecting inode \fII\fP to
+.I lost+found .
+.IP NO
+ignore this error condition.
+This will always invoke the CLEAR error condition in Phase 4.
+.sp
+.LP
+.B "(CLEAR)"
+.br
+The inode mentioned in the immediately previous error condition can not be
+reconnected.
+This cannot occur if the file system is being preen'ed,
+since lack of space to reconnect files is a fatal error.
+.LP
+Possible responses to the CLEAR prompt are:
+.IP YES
+de-allocate the inode mentioned in the immediately previous error condition by zeroing its contents.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "NO lost+found DIRECTORY (CREATE)"
+.br
+There is no
+.I lost+found
+directory in the root directory of the file system;
+When preen'ing
+.I fsck_ffs
+tries to create a \fIlost+found\fP directory.
+.LP
+Possible responses to the CREATE prompt are:
+.IP YES
+create a \fIlost+found\fP directory in the root of the file system.
+This may raise the message:
+.br
+.B "NO SPACE LEFT IN / (EXPAND)"
+.br
+See below for the possible responses.
+Inability to create a \fIlost+found\fP directory generates the message:
+.br
+.B "SORRY. CANNOT CREATE lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "lost+found IS NOT A DIRECTORY (REALLOCATE)"
+.br
+The entry for
+.I lost+found
+is not a directory.
+.LP
+Possible responses to the REALLOCATE prompt are:
+.IP YES
+allocate a directory inode, and change \fIlost+found\fP to reference it.
+The previous inode reference by the \fIlost+found\fP name is not cleared.
+Thus it will either be reclaimed as an UNREF'ed inode or have its
+link count ADJUST'ed later in this Phase.
+Inability to create a \fIlost+found\fP directory generates the message:
+.br
+.B "SORRY. CANNOT CREATE lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "NO SPACE LEFT IN /lost+found (EXPAND)"
+.br
+There is no space to add another entry to the
+.I lost+found
+directory in the root directory
+of the file system.
+When preen'ing the
+.I lost+found
+directory is expanded.
+.LP
+Possible responses to the EXPAND prompt are:
+.IP YES
+the
+.I lost+found
+directory is expanded to make room for the new entry.
+If the attempted expansion fails
+.I fsck_ffs
+prints the message:
+.br
+.B "SORRY. NO SPACE IN lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+Clean out unnecessary entries in
+.I lost+found .
+This error is fatal if the file system is being preen'ed.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "LINK COUNT \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP COUNT=\fIX\fP SHOULD BE \fIY\fP (ADJUST)"
+.br
+The link count for inode \fII\fP,
+is \fIX\fP but should be \fIY\fP.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and modify time \fIT\fP
+are printed.
+When preen'ing the link count is adjusted unless the number of references
+is increasing, a condition that should never occur unless precipitated
+by a hardware failure.
+When the number of references is increasing under preen mode,
+.I fsck_ffs
+exits with the message:
+.br
+.B "LINK COUNT INCREASING"
+.LP
+Possible responses to the ADJUST prompt are:
+.IP YES
+replace the link count of file inode \fII\fP with \fIY\fP.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "UNREF \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (CLEAR)"
+.br
+Inode \fII\fP, was not connected to a directory entry when the
+file system was traversed.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP,
+and modify time \fIT\fP of inode \fII\fP
+are printed.
+When preen'ing,
+this is a file that was not connected because its size or link count was zero,
+hence it is cleared.
+.LP
+Possible responses to the CLEAR prompt are:
+.IP YES
+de-allocate inode \fII\fP by zeroing its contents.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "BAD/DUP \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (CLEAR)"
+.br
+Phase 1 or Phase 1b have found duplicate blocks
+or bad blocks associated with
+inode \fII\fP.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP,
+and modify time \fIT\fP of inode \fII\fP
+are printed.
+This error cannot arise when the file system is being preen'ed,
+as it would have caused a fatal error earlier.
+.LP
+Possible responses to the CLEAR prompt are:
+.IP YES
+de-allocate inode \fII\fP by zeroing its contents.
+.IP NO
+ignore this error condition.
+.NH 2
+Phase 5 - Check Cyl groups
+.PP
+This phase concerns itself with the free-block and used-inode maps.
+This section lists error conditions resulting from
+allocated blocks in the free-block maps,
+free blocks missing from free-block maps,
+and the total free-block count incorrect.
+It also lists error conditions resulting from
+free inodes in the used-inode maps,
+allocated inodes missing from used-inode maps,
+and the total used-inode count incorrect.
+.sp
+.LP
+.B "CG \fIC\fP: BAD MAGIC NUMBER"
+.br
+The magic number of cylinder group \fIC\fP is wrong.
+This usually indicates that the cylinder group maps have been destroyed.
+When running manually the cylinder group is marked as needing
+to be reconstructed.
+This error is fatal if the file system is being preen'ed.
+.sp
+.LP
+.B "BLK(S) MISSING IN BIT MAPS (SALVAGE)"
+.br
+A cylinder group block map is missing some free blocks.
+During preen'ing the maps are reconstructed.
+.LP
+Possible responses to the SALVAGE prompt are:
+.IP YES
+reconstruct the free block map.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "SUMMARY INFORMATION BAD (SALVAGE)"
+.br
+The summary information was found to be incorrect.
+When preen'ing,
+the summary information is recomputed.
+.LP
+Possible responses to the SALVAGE prompt are:
+.IP YES
+reconstruct the summary information.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "FREE BLK COUNT(S) WRONG IN SUPERBLOCK (SALVAGE)"
+.br
+The superblock free block information was found to be incorrect.
+When preen'ing,
+the superblock free block information is recomputed.
+.LP
+Possible responses to the SALVAGE prompt are:
+.IP YES
+reconstruct the superblock free block information.
+.IP NO
+ignore this error condition.
+.NH 2
+Cleanup
+.PP
+Once a file system has been checked, a few cleanup functions are performed.
+This section lists advisory messages about
+the file system
+and modify status of the file system.
+.sp
+.LP
+.B "\fIV\fP files, \fIW\fP used, \fIX\fP free (\fIY\fP frags, \fIZ\fP blocks)"
+.br
+This is an advisory message indicating that
+the file system checked contained
+\fIV\fP files using
+\fIW\fP fragment sized blocks leaving
+\fIX\fP fragment sized blocks free in the file system.
+The numbers in parenthesis breaks the free count down into
+\fIY\fP free fragments and
+\fIZ\fP free full sized blocks.
+.sp
+.LP
+.B "***** REBOOT UNIX *****"
+.br
+This is an advisory message indicating that
+the root file system has been modified by
+.I fsck_ffs.
+If UNIX is not rebooted immediately,
+the work done by
+.I fsck_ffs
+may be undone by the in-core copies of tables
+UNIX keeps.
+When preen'ing,
+.I fsck_ffs
+will exit with a code of 4.
+The standard auto-reboot script distributed with 4.3BSD
+interprets an exit code of 4 by issuing a reboot system call.
+.sp
+.LP
+.B "***** FILE SYSTEM WAS MODIFIED *****"
+.br
+This is an advisory message indicating that
+the current file system was modified by
+.I fsck_ffs.
+If this file system is mounted or is the current root file system,
+.I fsck_ffs
+should be halted and UNIX rebooted.
+If UNIX is not rebooted immediately,
+the work done by
+.I fsck_ffs
+may be undone by the in-core copies of tables
+UNIX keeps.
diff --git a/sbin/fsck_ffs/SMM.doc/Makefile b/sbin/fsck_ffs/SMM.doc/Makefile
new file mode 100644
index 0000000..9ddc06f
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+DIR= smm/03.fsck_ffs
+SRCS= 0.t 1.t 2.t 3.t 4.t
+MACROS= -ms
+
+.include <bsd.doc.mk>
diff --git a/sbin/fsck_ffs/dir.c b/sbin/fsck_ffs/dir.c
new file mode 100644
index 0000000..da838b0
--- /dev/null
+++ b/sbin/fsck_ffs/dir.c
@@ -0,0 +1,713 @@
+/*
+ * Copyright (c) 1980, 1986, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char sccsid[] = "@(#)dir.c 8.8 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <string.h>
+
+#include "fsck.h"
+
+const char *lfname = "lost+found";
+int lfmode = 0700;
+struct dirtemplate emptydir = {
+ 0, DIRBLKSIZ, DT_UNKNOWN, 0, "",
+ 0, 0, DT_UNKNOWN, 0, ""
+};
+struct dirtemplate dirhead = {
+ 0, 12, DT_DIR, 1, ".",
+ 0, DIRBLKSIZ - 12, DT_DIR, 2, ".."
+};
+struct odirtemplate odirhead = {
+ 0, 12, 1, ".",
+ 0, DIRBLKSIZ - 12, 2, ".."
+};
+
+static int chgino(struct inodesc *);
+static int dircheck(struct inodesc *, struct direct *);
+static int expanddir(union dinode *dp, char *name);
+static void freedir(ino_t ino, ino_t parent);
+static struct direct *fsck_readdir(struct inodesc *);
+static struct bufarea *getdirblk(ufs2_daddr_t blkno, long size);
+static int lftempname(char *bufp, ino_t ino);
+static int mkentry(struct inodesc *);
+
+/*
+ * Propagate connected state through the tree.
+ */
+void
+propagate(void)
+{
+ struct inoinfo **inpp, *inp;
+ struct inoinfo **inpend;
+ long change;
+
+ inpend = &inpsort[inplast];
+ do {
+ change = 0;
+ for (inpp = inpsort; inpp < inpend; inpp++) {
+ inp = *inpp;
+ if (inp->i_parent == 0)
+ continue;
+ if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
+ INO_IS_DUNFOUND(inp->i_number)) {
+ inoinfo(inp->i_number)->ino_state = DFOUND;
+ change++;
+ }
+ }
+ } while (change > 0);
+}
+
+/*
+ * Scan each entry in a directory block.
+ */
+int
+dirscan(struct inodesc *idesc)
+{
+ struct direct *dp;
+ struct bufarea *bp;
+ u_int dsize, n;
+ long blksiz;
+ char dbuf[DIRBLKSIZ];
+
+ if (idesc->id_type != DATA)
+ errx(EEXIT, "wrong type to dirscan %d", idesc->id_type);
+ if (idesc->id_entryno == 0 &&
+ (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
+ idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
+ blksiz = idesc->id_numfrags * sblock.fs_fsize;
+ if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
+ idesc->id_filesize -= blksiz;
+ return (SKIP);
+ }
+ idesc->id_loc = 0;
+ for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
+ dsize = dp->d_reclen;
+ if (dsize > sizeof(dbuf))
+ dsize = sizeof(dbuf);
+ memmove(dbuf, dp, (size_t)dsize);
+ idesc->id_dirp = (struct direct *)dbuf;
+ if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
+ (size_t)dsize);
+ dirty(bp);
+ sbdirty();
+ }
+ if (n & STOP)
+ return (n);
+ }
+ return (idesc->id_filesize > 0 ? KEEPON : STOP);
+}
+
+/*
+ * get next entry in a directory.
+ */
+static struct direct *
+fsck_readdir(struct inodesc *idesc)
+{
+ struct direct *dp, *ndp;
+ struct bufarea *bp;
+ long size, blksiz, fix, dploc;
+
+ blksiz = idesc->id_numfrags * sblock.fs_fsize;
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 &&
+ idesc->id_loc < blksiz) {
+ dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
+ if (dircheck(idesc, dp))
+ goto dpok;
+ if (idesc->id_fix == IGNORE)
+ return (0);
+ fix = dofix(idesc, "DIRECTORY CORRUPTED");
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
+ dp->d_reclen = DIRBLKSIZ;
+ dp->d_ino = 0;
+ dp->d_type = 0;
+ dp->d_namlen = 0;
+ dp->d_name[0] = '\0';
+ if (fix)
+ dirty(bp);
+ idesc->id_loc += DIRBLKSIZ;
+ idesc->id_filesize -= DIRBLKSIZ;
+ return (dp);
+ }
+dpok:
+ if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
+ return NULL;
+ dploc = idesc->id_loc;
+ dp = (struct direct *)(bp->b_un.b_buf + dploc);
+ idesc->id_loc += dp->d_reclen;
+ idesc->id_filesize -= dp->d_reclen;
+ if ((idesc->id_loc % DIRBLKSIZ) == 0)
+ return (dp);
+ ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
+ if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
+ dircheck(idesc, ndp) == 0) {
+ size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
+ idesc->id_loc += size;
+ idesc->id_filesize -= size;
+ if (idesc->id_fix == IGNORE)
+ return (0);
+ fix = dofix(idesc, "DIRECTORY CORRUPTED");
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ dp = (struct direct *)(bp->b_un.b_buf + dploc);
+ dp->d_reclen += size;
+ if (fix)
+ dirty(bp);
+ }
+ return (dp);
+}
+
+/*
+ * Verify that a directory entry is valid.
+ * This is a superset of the checks made in the kernel.
+ */
+static int
+dircheck(struct inodesc *idesc, struct direct *dp)
+{
+ size_t size;
+ char *cp;
+ u_char type;
+ u_int namlen;
+ int spaceleft;
+
+ spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
+ if (dp->d_reclen == 0 ||
+ dp->d_reclen > spaceleft ||
+ (dp->d_reclen & 0x3) != 0)
+ goto bad;
+ if (dp->d_ino == 0)
+ return (1);
+ size = DIRSIZ(0, dp);
+ namlen = dp->d_namlen;
+ type = dp->d_type;
+ if (dp->d_reclen < size ||
+ idesc->id_filesize < size ||
+ namlen > MAXNAMLEN ||
+ type > 15)
+ goto bad;
+ for (cp = dp->d_name, size = 0; size < namlen; size++)
+ if (*cp == '\0' || (*cp++ == '/'))
+ goto bad;
+ if (*cp != '\0')
+ goto bad;
+ return (1);
+bad:
+ if (debug)
+ printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n",
+ dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_type,
+ dp->d_name);
+ return (0);
+}
+
+void
+direrror(ino_t ino, const char *errmesg)
+{
+
+ fileerror(ino, ino, errmesg);
+}
+
+void
+fileerror(ino_t cwd, ino_t ino, const char *errmesg)
+{
+ union dinode *dp;
+ char pathbuf[MAXPATHLEN + 1];
+
+ pwarn("%s ", errmesg);
+ pinode(ino);
+ printf("\n");
+ getpathname(pathbuf, cwd, ino);
+ if (ino < ROOTINO || ino > maxino) {
+ pfatal("NAME=%s\n", pathbuf);
+ return;
+ }
+ dp = ginode(ino);
+ if (ftypeok(dp))
+ pfatal("%s=%s\n",
+ (DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE",
+ pathbuf);
+ else
+ pfatal("NAME=%s\n", pathbuf);
+}
+
+void
+adjust(struct inodesc *idesc, int lcnt)
+{
+ union dinode *dp;
+ int saveresolved;
+
+ dp = ginode(idesc->id_number);
+ if (DIP(dp, di_nlink) == lcnt) {
+ /*
+ * If we have not hit any unresolved problems, are running
+ * in preen mode, and are on a file system using soft updates,
+ * then just toss any partially allocated files.
+ */
+ if (resolved && (preen || bkgrdflag) && usedsoftdep) {
+ clri(idesc, "UNREF", 1);
+ return;
+ } else {
+ /*
+ * The file system can be marked clean even if
+ * a file is not linked up, but is cleared.
+ * Hence, resolved should not be cleared when
+ * linkup is answered no, but clri is answered yes.
+ */
+ saveresolved = resolved;
+ if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) {
+ resolved = saveresolved;
+ clri(idesc, "UNREF", 0);
+ return;
+ }
+ /*
+ * Account for the new reference created by linkup().
+ */
+ dp = ginode(idesc->id_number);
+ lcnt--;
+ }
+ }
+ if (lcnt != 0) {
+ pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
+ ((DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE"));
+ pinode(idesc->id_number);
+ printf(" COUNT %d SHOULD BE %d",
+ DIP(dp, di_nlink), DIP(dp, di_nlink) - lcnt);
+ if (preen || usedsoftdep) {
+ if (lcnt < 0) {
+ printf("\n");
+ pfatal("LINK COUNT INCREASING");
+ }
+ if (preen)
+ printf(" (ADJUSTED)\n");
+ }
+ if (preen || reply("ADJUST") == 1) {
+ if (bkgrdflag == 0) {
+ DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - lcnt);
+ inodirty();
+ } else {
+ cmd.value = idesc->id_number;
+ cmd.size = -lcnt;
+ if (debug)
+ printf("adjrefcnt ino %ld amt %lld\n",
+ (long)cmd.value,
+ (long long)cmd.size);
+ if (sysctl(adjrefcnt, MIBSIZE, 0, 0,
+ &cmd, sizeof cmd) == -1)
+ rwerror("ADJUST INODE", cmd.value);
+ }
+ }
+ }
+}
+
+static int
+mkentry(struct inodesc *idesc)
+{
+ struct direct *dirp = idesc->id_dirp;
+ struct direct newent;
+ int newlen, oldlen;
+
+ newent.d_namlen = strlen(idesc->id_name);
+ newlen = DIRSIZ(0, &newent);
+ if (dirp->d_ino != 0)
+ oldlen = DIRSIZ(0, dirp);
+ else
+ oldlen = 0;
+ if (dirp->d_reclen - oldlen < newlen)
+ return (KEEPON);
+ newent.d_reclen = dirp->d_reclen - oldlen;
+ dirp->d_reclen = oldlen;
+ dirp = (struct direct *)(((char *)dirp) + oldlen);
+ dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */
+ dirp->d_reclen = newent.d_reclen;
+ dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
+ dirp->d_namlen = newent.d_namlen;
+ memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1);
+ return (ALTERED|STOP);
+}
+
+static int
+chgino(struct inodesc *idesc)
+{
+ struct direct *dirp = idesc->id_dirp;
+
+ if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
+ return (KEEPON);
+ dirp->d_ino = idesc->id_parent;
+ dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
+ return (ALTERED|STOP);
+}
+
+int
+linkup(ino_t orphan, ino_t parentdir, char *name)
+{
+ union dinode *dp;
+ int lostdir;
+ ino_t oldlfdir;
+ struct inodesc idesc;
+ char tempname[BUFSIZ];
+
+ memset(&idesc, 0, sizeof(struct inodesc));
+ dp = ginode(orphan);
+ lostdir = (DIP(dp, di_mode) & IFMT) == IFDIR;
+ pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
+ pinode(orphan);
+ if (preen && DIP(dp, di_size) == 0)
+ return (0);
+ if (cursnapshot != 0) {
+ pfatal("FILE LINKUP IN SNAPSHOT");
+ return (0);
+ }
+ if (preen)
+ printf(" (RECONNECTED)\n");
+ else
+ if (reply("RECONNECT") == 0)
+ return (0);
+ if (lfdir == 0) {
+ dp = ginode(ROOTINO);
+ idesc.id_name = strdup(lfname);
+ idesc.id_type = DATA;
+ idesc.id_func = findino;
+ idesc.id_number = ROOTINO;
+ if ((ckinode(dp, &idesc) & FOUND) != 0) {
+ lfdir = idesc.id_parent;
+ } else {
+ pwarn("NO lost+found DIRECTORY");
+ if (preen || reply("CREATE")) {
+ lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
+ if (lfdir != 0) {
+ if (makeentry(ROOTINO, lfdir, lfname) != 0) {
+ numdirs++;
+ if (preen)
+ printf(" (CREATED)\n");
+ } else {
+ freedir(lfdir, ROOTINO);
+ lfdir = 0;
+ if (preen)
+ printf("\n");
+ }
+ }
+ }
+ }
+ if (lfdir == 0) {
+ pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
+ printf("\n\n");
+ return (0);
+ }
+ }
+ dp = ginode(lfdir);
+ if ((DIP(dp, di_mode) & IFMT) != IFDIR) {
+ pfatal("lost+found IS NOT A DIRECTORY");
+ if (reply("REALLOCATE") == 0)
+ return (0);
+ oldlfdir = lfdir;
+ if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) {
+ pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
+ return (0);
+ }
+ if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) {
+ pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
+ return (0);
+ }
+ inodirty();
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ idesc.id_number = oldlfdir;
+ adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1);
+ inoinfo(oldlfdir)->ino_linkcnt = 0;
+ dp = ginode(lfdir);
+ }
+ if (inoinfo(lfdir)->ino_state != DFOUND) {
+ pfatal("SORRY. NO lost+found DIRECTORY\n\n");
+ return (0);
+ }
+ (void)lftempname(tempname, orphan);
+ if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) {
+ pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
+ printf("\n\n");
+ return (0);
+ }
+ inoinfo(orphan)->ino_linkcnt--;
+ if (lostdir) {
+ if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
+ parentdir != (ino_t)-1)
+ (void)makeentry(orphan, lfdir, "..");
+ dp = ginode(lfdir);
+ DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1);
+ inodirty();
+ inoinfo(lfdir)->ino_linkcnt++;
+ pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan);
+ if (parentdir != (ino_t)-1) {
+ printf("PARENT WAS I=%lu\n", (u_long)parentdir);
+ /*
+ * The parent directory, because of the ordering
+ * guarantees, has had the link count incremented
+ * for the child, but no entry was made. This
+ * fixes the parent link count so that fsck does
+ * not need to be rerun.
+ */
+ inoinfo(parentdir)->ino_linkcnt++;
+ }
+ if (preen == 0)
+ printf("\n");
+ }
+ return (1);
+}
+
+/*
+ * fix an entry in a directory.
+ */
+int
+changeino(ino_t dir, const char *name, ino_t newnum)
+{
+ struct inodesc idesc;
+
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = DATA;
+ idesc.id_func = chgino;
+ idesc.id_number = dir;
+ idesc.id_fix = DONTKNOW;
+ idesc.id_name = strdup(name);
+ idesc.id_parent = newnum; /* new value for name */
+ return (ckinode(ginode(dir), &idesc));
+}
+
+/*
+ * make an entry in a directory
+ */
+int
+makeentry(ino_t parent, ino_t ino, const char *name)
+{
+ union dinode *dp;
+ struct inodesc idesc;
+ char pathbuf[MAXPATHLEN + 1];
+
+ if (parent < ROOTINO || parent >= maxino ||
+ ino < ROOTINO || ino >= maxino)
+ return (0);
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = DATA;
+ idesc.id_func = mkentry;
+ idesc.id_number = parent;
+ idesc.id_parent = ino; /* this is the inode to enter */
+ idesc.id_fix = DONTKNOW;
+ idesc.id_name = strdup(name);
+ dp = ginode(parent);
+ if (DIP(dp, di_size) % DIRBLKSIZ) {
+ DIP_SET(dp, di_size, roundup(DIP(dp, di_size), DIRBLKSIZ));
+ inodirty();
+ }
+ if ((ckinode(dp, &idesc) & ALTERED) != 0)
+ return (1);
+ getpathname(pathbuf, parent, parent);
+ dp = ginode(parent);
+ if (expanddir(dp, pathbuf) == 0)
+ return (0);
+ return (ckinode(dp, &idesc) & ALTERED);
+}
+
+/*
+ * Attempt to expand the size of a directory
+ */
+static int
+expanddir(union dinode *dp, char *name)
+{
+ ufs2_daddr_t lastbn, newblk;
+ struct bufarea *bp;
+ char *cp, firstblk[DIRBLKSIZ];
+
+ lastbn = lblkno(&sblock, DIP(dp, di_size));
+ if (lastbn >= NDADDR - 1 || DIP(dp, di_db[lastbn]) == 0 ||
+ DIP(dp, di_size) == 0)
+ return (0);
+ if ((newblk = allocblk(sblock.fs_frag)) == 0)
+ return (0);
+ DIP_SET(dp, di_db[lastbn + 1], DIP(dp, di_db[lastbn]));
+ DIP_SET(dp, di_db[lastbn], newblk);
+ DIP_SET(dp, di_size, DIP(dp, di_size) + sblock.fs_bsize);
+ DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize));
+ bp = getdirblk(DIP(dp, di_db[lastbn + 1]),
+ sblksize(&sblock, DIP(dp, di_size), lastbn + 1));
+ if (bp->b_errs)
+ goto bad;
+ memmove(firstblk, bp->b_un.b_buf, DIRBLKSIZ);
+ bp = getdirblk(newblk, sblock.fs_bsize);
+ if (bp->b_errs)
+ goto bad;
+ memmove(bp->b_un.b_buf, firstblk, DIRBLKSIZ);
+ for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
+ cp < &bp->b_un.b_buf[sblock.fs_bsize];
+ cp += DIRBLKSIZ)
+ memmove(cp, &emptydir, sizeof emptydir);
+ dirty(bp);
+ bp = getdirblk(DIP(dp, di_db[lastbn + 1]),
+ sblksize(&sblock, DIP(dp, di_size), lastbn + 1));
+ if (bp->b_errs)
+ goto bad;
+ memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir);
+ pwarn("NO SPACE LEFT IN %s", name);
+ if (preen)
+ printf(" (EXPANDED)\n");
+ else if (reply("EXPAND") == 0)
+ goto bad;
+ dirty(bp);
+ inodirty();
+ return (1);
+bad:
+ DIP_SET(dp, di_db[lastbn], DIP(dp, di_db[lastbn + 1]));
+ DIP_SET(dp, di_db[lastbn + 1], 0);
+ DIP_SET(dp, di_size, DIP(dp, di_size) - sblock.fs_bsize);
+ DIP_SET(dp, di_blocks, DIP(dp, di_blocks) - btodb(sblock.fs_bsize));
+ freeblk(newblk, sblock.fs_frag);
+ return (0);
+}
+
+/*
+ * allocate a new directory
+ */
+ino_t
+allocdir(ino_t parent, ino_t request, int mode)
+{
+ ino_t ino;
+ char *cp;
+ union dinode *dp;
+ struct bufarea *bp;
+ struct inoinfo *inp;
+ struct dirtemplate *dirp;
+
+ ino = allocino(request, IFDIR|mode);
+ dirp = &dirhead;
+ dirp->dot_ino = ino;
+ dirp->dotdot_ino = parent;
+ dp = ginode(ino);
+ bp = getdirblk(DIP(dp, di_db[0]), sblock.fs_fsize);
+ if (bp->b_errs) {
+ freeino(ino);
+ return (0);
+ }
+ memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
+ for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
+ cp < &bp->b_un.b_buf[sblock.fs_fsize];
+ cp += DIRBLKSIZ)
+ memmove(cp, &emptydir, sizeof emptydir);
+ dirty(bp);
+ DIP_SET(dp, di_nlink, 2);
+ inodirty();
+ if (ino == ROOTINO) {
+ inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
+ cacheino(dp, ino);
+ return(ino);
+ }
+ if (!INO_IS_DVALID(parent)) {
+ freeino(ino);
+ return (0);
+ }
+ cacheino(dp, ino);
+ inp = getinoinfo(ino);
+ inp->i_parent = parent;
+ inp->i_dotdot = parent;
+ inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;
+ if (inoinfo(ino)->ino_state == DSTATE) {
+ inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
+ inoinfo(parent)->ino_linkcnt++;
+ }
+ dp = ginode(parent);
+ DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1);
+ inodirty();
+ return (ino);
+}
+
+/*
+ * free a directory inode
+ */
+static void
+freedir(ino_t ino, ino_t parent)
+{
+ union dinode *dp;
+
+ if (ino != parent) {
+ dp = ginode(parent);
+ DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - 1);
+ inodirty();
+ }
+ freeino(ino);
+}
+
+/*
+ * generate a temporary name for the lost+found directory.
+ */
+static int
+lftempname(char *bufp, ino_t ino)
+{
+ ino_t in;
+ char *cp;
+ int namlen;
+
+ cp = bufp + 2;
+ for (in = maxino; in > 0; in /= 10)
+ cp++;
+ *--cp = 0;
+ namlen = cp - bufp;
+ in = ino;
+ while (cp > bufp) {
+ *--cp = (in % 10) + '0';
+ in /= 10;
+ }
+ *cp = '#';
+ return (namlen);
+}
+
+/*
+ * Get a directory block.
+ * Insure that it is held until another is requested.
+ */
+static struct bufarea *
+getdirblk(ufs2_daddr_t blkno, long size)
+{
+
+ if (pdirbp != 0)
+ pdirbp->b_flags &= ~B_INUSE;
+ pdirbp = getdatablk(blkno, size);
+ return (pdirbp);
+}
diff --git a/sbin/fsck_ffs/ea.c b/sbin/fsck_ffs/ea.c
new file mode 100644
index 0000000..2cdf71e
--- /dev/null
+++ b/sbin/fsck_ffs/ea.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2002 Poul-Henning Kamp
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Poul-Henning Kamp
+ * and NAI Labs, the Security Research Division of Network Associates, Inc.
+ * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
+ * DARPA CHATS research program.
+ *
+ * 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 names of the authors may not 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stdint.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <string.h>
+
+#include "fsck.h"
+
+/*
+ * Scan each entry in an ea block.
+ */
+int
+eascan(struct inodesc *idesc, struct ufs2_dinode *dp)
+{
+#if 1
+ return (0);
+#else
+ struct bufarea *bp;
+ u_int dsize, n;
+ u_char *cp;
+ long blksiz;
+ char dbuf[DIRBLKSIZ];
+
+ printf("Inode %ju extsize %ju\n",
+ (intmax_t)idesc->id_number, (intmax_t)dp->di_extsize);
+ if (dp->di_extsize == 0)
+ return 0;
+ if (dp->di_extsize <= sblock.fs_fsize)
+ blksiz = sblock.fs_fsize;
+ else
+ blksiz = sblock.fs_bsize;
+ printf("blksiz = %ju\n", (intmax_t)blksiz);
+ bp = getdatablk(dp->di_extb[0], blksiz);
+ cp = (u_char *)bp->b_un.b_buf;
+ for (n = 0; n < blksiz; n++) {
+ printf("%02x", cp[n]);
+ if ((n & 31) == 31)
+ printf("\n");
+ }
+ return (STOP);
+#endif
+}
+
diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h
new file mode 100644
index 0000000..32f9cfa
--- /dev/null
+++ b/sbin/fsck_ffs/fsck.h
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and Network Associates Laboratories, the Security
+ * Research Division of Network Associates, Inc. under DARPA/SPAWAR
+ * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+ * research program.
+ *
+ * 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.
+ *
+ * Copyright (c) 1980, 1986, 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.
+ * 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 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.
+ *
+ * @(#)fsck.h 8.4 (Berkeley) 5/9/95
+ * $FreeBSD$
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define MAXDUP 10 /* limit on dup blks (per inode) */
+#define MAXBAD 10 /* limit on bad blks (per inode) */
+#define MAXBUFSPACE 40*1024 /* maximum space to allocate to buffers */
+#define INOBUFSIZE 56*1024 /* size of buffer to read inodes in pass1 */
+
+union dinode {
+ struct ufs1_dinode dp1;
+ struct ufs2_dinode dp2;
+};
+#define DIP(dp, field) \
+ ((sblock.fs_magic == FS_UFS1_MAGIC) ? \
+ (dp)->dp1.field : (dp)->dp2.field)
+
+#define DIP_SET(dp, field, val) do { \
+ if (sblock.fs_magic == FS_UFS1_MAGIC) \
+ (dp)->dp1.field = (val); \
+ else \
+ (dp)->dp2.field = (val); \
+ } while (0)
+
+/*
+ * Each inode on the file system is described by the following structure.
+ * The linkcnt is initially set to the value in the inode. Each time it
+ * is found during the descent in passes 2, 3, and 4 the count is
+ * decremented. Any inodes whose count is non-zero after pass 4 needs to
+ * have its link count adjusted by the value remaining in ino_linkcnt.
+ */
+struct inostat {
+ char ino_state; /* state of inode, see below */
+ char ino_type; /* type of inode */
+ short ino_linkcnt; /* number of links not found */
+};
+/*
+ * Inode states.
+ */
+#define USTATE 0x1 /* inode not allocated */
+#define FSTATE 0x2 /* inode is file */
+#define FZLINK 0x3 /* inode is file with a link count of zero */
+#define DSTATE 0x4 /* inode is directory */
+#define DZLINK 0x5 /* inode is directory with a zero link count */
+#define DFOUND 0x6 /* directory found during descent */
+/* 0x7 UNUSED - see S_IS_DVALID() definition */
+#define DCLEAR 0x8 /* directory is to be cleared */
+#define FCLEAR 0x9 /* file is to be cleared */
+/* DUNFOUND === (state == DSTATE || state == DZLINK) */
+#define S_IS_DUNFOUND(state) (((state) & ~0x1) == DSTATE)
+/* DVALID === (state == DSTATE || state == DZLINK || state == DFOUND) */
+#define S_IS_DVALID(state) (((state) & ~0x3) == DSTATE)
+#define INO_IS_DUNFOUND(ino) S_IS_DUNFOUND(inoinfo(ino)->ino_state)
+#define INO_IS_DVALID(ino) S_IS_DVALID(inoinfo(ino)->ino_state)
+/*
+ * Inode state information is contained on per cylinder group lists
+ * which are described by the following structure.
+ */
+struct inostatlist {
+ long il_numalloced; /* number of inodes allocated in this cg */
+ struct inostat *il_stat;/* inostat info for this cylinder group */
+} *inostathead;
+
+/*
+ * buffer cache structure.
+ */
+struct bufarea {
+ struct bufarea *b_next; /* free list queue */
+ struct bufarea *b_prev; /* free list queue */
+ ufs2_daddr_t b_bno;
+ int b_size;
+ int b_errs;
+ int b_flags;
+ union {
+ char *b_buf; /* buffer space */
+ ufs1_daddr_t *b_indir1; /* UFS1 indirect block */
+ ufs2_daddr_t *b_indir2; /* UFS2 indirect block */
+ struct fs *b_fs; /* super block */
+ struct cg *b_cg; /* cylinder group */
+ struct ufs1_dinode *b_dinode1; /* UFS1 inode block */
+ struct ufs2_dinode *b_dinode2; /* UFS2 inode block */
+ } b_un;
+ char b_dirty;
+};
+
+#define IBLK(bp, i) \
+ ((sblock.fs_magic == FS_UFS1_MAGIC) ? \
+ (bp)->b_un.b_indir1[i] : (bp)->b_un.b_indir2[i])
+
+#define IBLK_SET(bp, i, val) do { \
+ if (sblock.fs_magic == FS_UFS1_MAGIC) \
+ (bp)->b_un.b_indir1[i] = (val); \
+ else \
+ (bp)->b_un.b_indir2[i] = (val); \
+ } while (0)
+
+#define B_INUSE 1
+
+#define MINBUFS 5 /* minimum number of buffers required */
+struct bufarea bufhead; /* head of list of other blks in filesys */
+struct bufarea sblk; /* file system superblock */
+struct bufarea cgblk; /* cylinder group blocks */
+struct bufarea *pdirbp; /* current directory contents */
+struct bufarea *pbp; /* current inode block */
+
+#define dirty(bp) do { \
+ if (fswritefd < 0) \
+ pfatal("SETTING DIRTY FLAG IN READ_ONLY MODE\n"); \
+ else \
+ (bp)->b_dirty = 1; \
+} while (0)
+#define initbarea(bp) do { \
+ (bp)->b_dirty = 0; \
+ (bp)->b_bno = (ufs2_daddr_t)-1; \
+ (bp)->b_flags = 0; \
+} while (0)
+
+#define sbdirty() dirty(&sblk)
+#define cgdirty() dirty(&cgblk)
+#define sblock (*sblk.b_un.b_fs)
+#define cgrp (*cgblk.b_un.b_cg)
+
+enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE};
+ino_t cursnapshot;
+
+struct inodesc {
+ enum fixstate id_fix; /* policy on fixing errors */
+ int (*id_func)(struct inodesc *);
+ /* function to be applied to blocks of inode */
+ ino_t id_number; /* inode number described */
+ ino_t id_parent; /* for DATA nodes, their parent */
+ ufs_lbn_t id_lbn; /* logical block number of current block */
+ ufs2_daddr_t id_blkno; /* current block number being examined */
+ int id_numfrags; /* number of frags contained in block */
+ off_t id_filesize; /* for DATA nodes, the size of the directory */
+ ufs2_daddr_t id_entryno;/* for DATA nodes, current entry number */
+ int id_loc; /* for DATA nodes, current location in dir */
+ struct direct *id_dirp; /* for DATA nodes, ptr to current entry */
+ char *id_name; /* for DATA nodes, name to find or enter */
+ char id_type; /* type of descriptor, DATA or ADDR */
+};
+/* file types */
+#define DATA 1 /* a directory */
+#define SNAP 2 /* a snapshot */
+#define ADDR 3 /* anything but a directory or a snapshot */
+
+/*
+ * Linked list of duplicate blocks.
+ *
+ * The list is composed of two parts. The first part of the
+ * list (from duplist through the node pointed to by muldup)
+ * contains a single copy of each duplicate block that has been
+ * found. The second part of the list (from muldup to the end)
+ * contains duplicate blocks that have been found more than once.
+ * To check if a block has been found as a duplicate it is only
+ * necessary to search from duplist through muldup. To find the
+ * total number of times that a block has been found as a duplicate
+ * the entire list must be searched for occurences of the block
+ * in question. The following diagram shows a sample list where
+ * w (found twice), x (found once), y (found three times), and z
+ * (found once) are duplicate block numbers:
+ *
+ * w -> y -> x -> z -> y -> w -> y
+ * ^ ^
+ * | |
+ * duplist muldup
+ */
+struct dups {
+ struct dups *next;
+ ufs2_daddr_t dup;
+};
+struct dups *duplist; /* head of dup list */
+struct dups *muldup; /* end of unique duplicate dup block numbers */
+
+/*
+ * Inode cache data structures.
+ */
+struct inoinfo {
+ struct inoinfo *i_nexthash; /* next entry in hash chain */
+ ino_t i_number; /* inode number of this entry */
+ ino_t i_parent; /* inode number of parent */
+ ino_t i_dotdot; /* inode number of `..' */
+ size_t i_isize; /* size of inode */
+ u_int i_numblks; /* size of block array in bytes */
+ ufs2_daddr_t i_blks[1]; /* actually longer */
+} **inphead, **inpsort;
+long numdirs, dirhash, listmax, inplast;
+long countdirs; /* number of directories we actually found */
+
+#define MIBSIZE 3 /* size of fsck sysctl MIBs */
+int adjrefcnt[MIBSIZE]; /* MIB command to adjust inode reference cnt */
+int adjblkcnt[MIBSIZE]; /* MIB command to adjust inode block count */
+int adjndir[MIBSIZE]; /* MIB command to adjust number of directories */
+int adjnbfree[MIBSIZE]; /* MIB command to adjust number of free blocks */
+int adjnifree[MIBSIZE]; /* MIB command to adjust number of free inodes */
+int adjnffree[MIBSIZE]; /* MIB command to adjust number of free frags */
+int adjnumclusters[MIBSIZE]; /* MIB command to adjust number of free clusters */
+int freefiles[MIBSIZE]; /* MIB command to free a set of files */
+int freedirs[MIBSIZE]; /* MIB command to free a set of directories */
+int freeblks[MIBSIZE]; /* MIB command to free a set of data blocks */
+struct fsck_cmd cmd; /* sysctl file system update commands */
+char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */
+char *cdevname; /* name of device being checked */
+long dev_bsize; /* computed value of DEV_BSIZE */
+long secsize; /* actual disk sector size */
+char nflag; /* assume a no response */
+char yflag; /* assume a yes response */
+int bkgrdflag; /* use a snapshot to run on an active system */
+int bflag; /* location of alternate super block */
+int debug; /* output debugging info */
+int cvtlevel; /* convert to newer file system format */
+int bkgrdcheck; /* determine if background check is possible */
+int bkgrdsumadj; /* whether the kernel have ability to adjust superblock summary */
+char usedsoftdep; /* just fix soft dependency inconsistencies */
+char preen; /* just fix normal inconsistencies */
+char rerun; /* rerun fsck. Only used in non-preen mode */
+int returntosingle; /* 1 => return to single user mode on exit */
+char resolved; /* cleared if unresolved changes => not clean */
+char havesb; /* superblock has been read */
+char skipclean; /* skip clean file systems if preening */
+int fsmodified; /* 1 => write done to file system */
+int fsreadfd; /* file descriptor for reading file system */
+int fswritefd; /* file descriptor for writing file system */
+
+ufs2_daddr_t maxfsblock; /* number of blocks in the file system */
+char *blockmap; /* ptr to primary blk allocation map */
+ino_t maxino; /* number of inodes in file system */
+
+ino_t lfdir; /* lost & found directory inode number */
+const char *lfname; /* lost & found directory name */
+int lfmode; /* lost & found directory creation mode */
+
+ufs2_daddr_t n_blks; /* number of blocks in use */
+ino_t n_files; /* number of files in use */
+
+int got_siginfo; /* received a SIGINFO */
+int got_sigalarm; /* received a SIGALRM */
+
+#define clearinode(dp) \
+ if (sblock.fs_magic == FS_UFS1_MAGIC) { \
+ (dp)->dp1 = ufs1_zino; \
+ } else { \
+ (dp)->dp2 = ufs2_zino; \
+ }
+struct ufs1_dinode ufs1_zino;
+struct ufs2_dinode ufs2_zino;
+
+#define setbmap(blkno) setbit(blockmap, blkno)
+#define testbmap(blkno) isset(blockmap, blkno)
+#define clrbmap(blkno) clrbit(blockmap, blkno)
+
+#define STOP 0x01
+#define SKIP 0x02
+#define KEEPON 0x04
+#define ALTERED 0x08
+#define FOUND 0x10
+
+#define EEXIT 8 /* Standard error exit. */
+
+struct fstab;
+
+
+void adjust(struct inodesc *, int lcnt);
+ufs2_daddr_t allocblk(long frags);
+ino_t allocdir(ino_t parent, ino_t request, int mode);
+ino_t allocino(ino_t request, int type);
+void blkerror(ino_t ino, const char *type, ufs2_daddr_t blk);
+char *blockcheck(char *name);
+int bread(int fd, char *buf, ufs2_daddr_t blk, long size);
+void bufinit(void);
+void bwrite(int fd, char *buf, ufs2_daddr_t blk, long size);
+void cacheino(union dinode *dp, ino_t inumber);
+void catch(int);
+void catchquit(int);
+int changeino(ino_t dir, const char *name, ino_t newnum);
+int chkrange(ufs2_daddr_t blk, int cnt);
+void ckfini(int markclean);
+int ckinode(union dinode *dp, struct inodesc *);
+void clri(struct inodesc *, const char *type, int flag);
+int clearentry(struct inodesc *);
+void direrror(ino_t ino, const char *errmesg);
+int dirscan(struct inodesc *);
+int dofix(struct inodesc *, const char *msg);
+int eascan(struct inodesc *, struct ufs2_dinode *dp);
+void ffs_clrblock(struct fs *, u_char *, ufs1_daddr_t);
+void ffs_fragacct(struct fs *, int, int32_t [], int);
+int ffs_isblock(struct fs *, u_char *, ufs1_daddr_t);
+void ffs_setblock(struct fs *, u_char *, ufs1_daddr_t);
+void fileerror(ino_t cwd, ino_t ino, const char *errmesg);
+int findino(struct inodesc *);
+int findname(struct inodesc *);
+void flush(int fd, struct bufarea *bp);
+void freeblk(ufs2_daddr_t blkno, long frags);
+void freeino(ino_t ino);
+void freeinodebuf(void);
+int ftypeok(union dinode *dp);
+void getblk(struct bufarea *bp, ufs2_daddr_t blk, long size);
+struct bufarea *getdatablk(ufs2_daddr_t blkno, long size);
+struct inoinfo *getinoinfo(ino_t inumber);
+union dinode *getnextinode(ino_t inumber);
+void getpathname(char *namebuf, ino_t curdir, ino_t ino);
+union dinode *ginode(ino_t inumber);
+void infohandler(int sig);
+void alarmhandler(int sig);
+void inocleanup(void);
+void inodirty(void);
+struct inostat *inoinfo(ino_t inum);
+int linkup(ino_t orphan, ino_t parentdir, char *name);
+int makeentry(ino_t parent, ino_t ino, const char *name);
+void panic(const char *fmt, ...) __printflike(1, 2);
+void pass1(void);
+void pass1b(void);
+int pass1check(struct inodesc *);
+void pass2(void);
+void pass3(void);
+void pass4(void);
+int pass4check(struct inodesc *);
+void pass5(void);
+void pfatal(const char *fmt, ...) __printflike(1, 2);
+void pinode(ino_t ino);
+void propagate(void);
+void pwarn(const char *fmt, ...) __printflike(1, 2);
+int readsb(int listerr);
+int reply(const char *question);
+void rwerror(const char *mesg, ufs2_daddr_t blk);
+void sblock_init(void);
+void setinodebuf(ino_t);
+int setup(char *dev);
diff --git a/sbin/fsck_ffs/fsck_ffs.8 b/sbin/fsck_ffs/fsck_ffs.8
new file mode 100644
index 0000000..a64c7ce
--- /dev/null
+++ b/sbin/fsck_ffs/fsck_ffs.8
@@ -0,0 +1,317 @@
+.\"
+.\" Copyright (c) 1980, 1989, 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.
+.\" 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 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.
+.\"
+.\" @(#)fsck.8 8.4 (Berkeley) 5/9/95
+.\" $FreeBSD$
+.\"
+.Dd April 24, 2001
+.Dt FSCK_FFS 8
+.Os
+.Sh NAME
+.Nm fsck_ffs ,
+.Nm fsck_ufs
+.Nd file system consistency check and interactive repair
+.Sh SYNOPSIS
+.Nm
+.Op Fl BFpfny
+.Op Fl b Ar block
+.Op Fl c Ar level
+.Op Fl m Ar mode
+.Ar filesystem
+.Ar ...
+.Sh DESCRIPTION
+The specified disk partitions and/or file systems are checked.
+In "preen" mode the clean flag of each file system's superblock is examined
+and only those file systems that
+are not marked clean are checked.
+File systems are marked clean when they are unmounted,
+when they have been mounted read-only, or when
+.Nm
+runs on them successfully.
+If the
+.Fl f
+option is specified, the file systems
+will be checked regardless of the state of their clean flag.
+.Pp
+The kernel takes care that only a restricted class of innocuous file system
+inconsistencies can happen unless hardware or software failures intervene.
+These are limited to the following:
+.Pp
+.Bl -item -compact -offset indent
+.It
+Unreferenced inodes
+.It
+Link counts in inodes too large
+.It
+Missing blocks in the free map
+.It
+Blocks in the free map also in files
+.It
+Counts in the super-block wrong
+.El
+.Pp
+These are the only inconsistencies that
+.Nm
+with the
+.Fl p
+option will correct; if it encounters other inconsistencies, it exits
+with an abnormal return status and an automatic reboot will then fail.
+For each corrected inconsistency one or more lines will be printed
+identifying the file system on which the correction will take place,
+and the nature of the correction.
+After successfully correcting a file system,
+.Nm
+will print the number of files on that file system,
+the number of used and free blocks,
+and the percentage of fragmentation.
+.Pp
+If sent a
+.Dv QUIT
+signal,
+.Nm
+will finish the file system checks, then exit with an abnormal
+return status that causes an automatic reboot to fail.
+This is useful when you want to finish the file system checks during an
+automatic reboot,
+but do not want the machine to come up multiuser after the checks complete.
+.Pp
+If
+.Nm
+receives a
+.Dv SIGINFO
+(see the
+.Dq status
+argument for
+.Xr stty 1 )
+signal, a line will be written to the standard output indicating
+the name of the device currently being checked, the current phase
+number and phase-specific progress information.
+.Pp
+Without the
+.Fl p
+option,
+.Nm
+audits and interactively repairs inconsistent conditions for file systems.
+If the file system is inconsistent the operator is prompted for concurrence
+before each correction is attempted.
+It should be noted that some of the corrective actions which are not
+correctable under the
+.Fl p
+option will result in some loss of data.
+The amount and severity of data lost may be determined from the diagnostic
+output.
+The default action for each consistency correction
+is to wait for the operator to respond
+.Li yes
+or
+.Li no .
+If the operator does not have write permission on the file system
+.Nm
+will default to a
+.Fl n
+action.
+.Pp
+The following flags are interpreted by
+.Nm :
+.Bl -tag -width indent
+.It Fl F
+Determine whether the file system needs to be cleaned immediately
+in foreground, or if its cleaning can be deferred to background.
+To be eligible for background cleaning it must have been running
+with soft updates, not have been marked as needing a foreground check,
+and be mounted and writable when the background check is to be done.
+If these conditions are met, then
+.Nm
+exits with a zero exit status.
+Otherwise it exits with a non-zero exit status.
+If the file system is clean,
+it will exit with a non-zero exit status so that the clean status
+of the file system can be verified and reported during the foreground
+checks.
+Note that when invoked with the
+.Fl F
+flag, no cleanups are done.
+The only thing that
+.Nm
+does is to determine whether a foreground or background
+check is needed and exit with an appropriate status code.
+.It Fl B
+A check is done on the specified and possibly active file system.
+The set of corrections that can be done is limited to those done
+when running in preen mode (see the
+.Fl p
+flag).
+If unexpected errors are found,
+the file system is marked as needing a foreground check and
+.Nm
+exits without attempting any further cleaning.
+.It Fl b
+Use the block specified immediately after the flag as
+the super block for the file system.
+An alternate super block is usually located at block 32 for UFS1,
+and block 160 for UFS2.
+.It Fl c
+Convert the file system to the specified level.
+Note that the level of a file system can only be raised.
+There are currently four levels defined:
+.Bl -tag -width indent
+.It 0
+The file system is in the old (static table) format.
+.It 1
+The file system is in the new (dynamic table) format.
+.It 2
+The file system supports 32-bit uid's and gid's,
+short symbolic links are stored in the inode,
+and directories have an added field showing the file type.
+.It 3
+If maxcontig is greater than one,
+build the free segment maps to aid in finding contiguous sets of blocks.
+If maxcontig is equal to one, delete any existing segment maps.
+.El
+.Pp
+In interactive mode,
+.Nm
+will list the conversion to be made
+and ask whether the conversion should be done.
+If a negative answer is given,
+no further operations are done on the file system.
+In preen mode,
+the conversion is listed and done if
+possible without user interaction.
+Conversion in preen mode is best used when all the file systems
+are being converted at once.
+The format of a file system can be determined from the
+first line of output from
+.Xr dumpfs 8 .
+.It Fl f
+Force
+.Nm
+to check
+.Sq clean
+file systems when preening.
+.It Fl m
+Use the mode specified in octal immediately after the flag as the
+permission bits to use when creating the
+.Pa lost+found
+directory rather than the default 1777.
+In particular, systems that do not wish to have lost files accessible
+by all users on the system should use a more restrictive
+set of permissions such as 700.
+.It Fl n
+Assume a no response to all questions asked by
+.Nm
+except for
+.Ql CONTINUE? ,
+which is assumed to be affirmative;
+do not open the file system for writing.
+.It Fl p
+Preen file systems (see above).
+.It Fl y
+Assume a yes response to all questions asked by
+.Nm ;
+this should be used with great caution as this is a free license
+to continue after essentially unlimited trouble has been encountered.
+.El
+.Pp
+Inconsistencies checked are as follows:
+.Pp
+.Bl -enum -compact
+.It
+Blocks claimed by more than one inode or the free map.
+.It
+Blocks claimed by an inode outside the range of the file system.
+.It
+Incorrect link counts.
+.It
+Size checks:
+.Bl -item -offset indent -compact
+.It
+Directory size not a multiple of DIRBLKSIZ.
+.It
+Partially truncated file.
+.El
+.It
+Bad inode format.
+.It
+Blocks not accounted for anywhere.
+.It
+Directory checks:
+.Bl -item -offset indent -compact
+.It
+File pointing to unallocated inode.
+.It
+Inode number out of range.
+.It
+Directories with unallocated blocks (holes).
+.It
+Dot or dot-dot not the first two entries of a directory
+or having the wrong inode number.
+.El
+.It
+Super Block checks:
+.Bl -item -offset indent -compact
+.It
+More blocks for inodes than there are in the file system.
+.It
+Bad free block map format.
+.It
+Total free block and/or free inode count incorrect.
+.El
+.El
+.Pp
+Orphaned files and directories (allocated but unreferenced) are,
+with the operator's concurrence, reconnected by
+placing them in the
+.Pa lost+found
+directory.
+The name assigned is the inode number.
+If the
+.Pa lost+found
+directory does not exist, it is created.
+If there is insufficient space its size is increased.
+.Sh FILES
+.Bl -tag -width /etc/fstab -compact
+.It Pa /etc/fstab
+contains default list of file systems to check.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh DIAGNOSTICS
+The diagnostics produced by
+.Nm
+are fully enumerated and explained in Appendix A of
+.Rs
+.%T "Fsck \- The UNIX File System Check Program"
+.Re
+.Sh SEE ALSO
+.Xr fs 5 ,
+.Xr fstab 5 ,
+.Xr fsck 8 ,
+.Xr fsdb 8 ,
+.Xr newfs 8 ,
+.Xr reboot 8
diff --git a/sbin/fsck_ffs/fsutil.c b/sbin/fsck_ffs/fsutil.c
new file mode 100644
index 0000000..6bb590e
--- /dev/null
+++ b/sbin/fsck_ffs/fsutil.c
@@ -0,0 +1,709 @@
+/*
+ * Copyright (c) 1980, 1986, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char sccsid[] = "@(#)utilities.c 8.6 (Berkeley) 5/19/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/disklabel.h>
+#include <sys/stat.h>
+#include <sys/disklabel.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <fstab.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "fsck.h"
+
+static void slowio_start(void);
+static void slowio_end(void);
+
+long diskreads, totalreads; /* Disk cache statistics */
+struct timeval slowio_starttime;
+int slowio_delay_usec = 10000; /* Initial IO delay for background fsck */
+int slowio_pollcnt;
+
+int
+ftypeok(union dinode *dp)
+{
+ switch (DIP(dp, di_mode) & IFMT) {
+
+ case IFDIR:
+ case IFREG:
+ case IFBLK:
+ case IFCHR:
+ case IFLNK:
+ case IFSOCK:
+ case IFIFO:
+ return (1);
+
+ default:
+ if (debug)
+ printf("bad file type 0%o\n", DIP(dp, di_mode));
+ return (0);
+ }
+}
+
+int
+reply(const char *question)
+{
+ int persevere;
+ char c;
+
+ if (preen)
+ pfatal("INTERNAL ERROR: GOT TO reply()");
+ persevere = !strcmp(question, "CONTINUE");
+ printf("\n");
+ if (!persevere && (nflag || (fswritefd < 0 && bkgrdflag == 0))) {
+ printf("%s? no\n\n", question);
+ resolved = 0;
+ return (0);
+ }
+ if (yflag || (persevere && nflag)) {
+ printf("%s? yes\n\n", question);
+ return (1);
+ }
+ do {
+ printf("%s? [yn] ", question);
+ (void) fflush(stdout);
+ c = getc(stdin);
+ while (c != '\n' && getc(stdin) != '\n') {
+ if (feof(stdin)) {
+ resolved = 0;
+ return (0);
+ }
+ }
+ } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
+ printf("\n");
+ if (c == 'y' || c == 'Y')
+ return (1);
+ resolved = 0;
+ return (0);
+}
+
+/*
+ * Look up state information for an inode.
+ */
+struct inostat *
+inoinfo(ino_t inum)
+{
+ static struct inostat unallocated = { USTATE, 0, 0 };
+ struct inostatlist *ilp;
+ int iloff;
+
+ if (inum > maxino)
+ errx(EEXIT, "inoinfo: inumber %d out of range", inum);
+ ilp = &inostathead[inum / sblock.fs_ipg];
+ iloff = inum % sblock.fs_ipg;
+ if (iloff >= ilp->il_numalloced)
+ return (&unallocated);
+ return (&ilp->il_stat[iloff]);
+}
+
+/*
+ * Malloc buffers and set up cache.
+ */
+void
+bufinit(void)
+{
+ struct bufarea *bp;
+ long bufcnt, i;
+ char *bufp;
+
+ pbp = pdirbp = (struct bufarea *)0;
+ bufp = malloc((unsigned int)sblock.fs_bsize);
+ if (bufp == 0)
+ errx(EEXIT, "cannot allocate buffer pool");
+ cgblk.b_un.b_buf = bufp;
+ initbarea(&cgblk);
+ bufhead.b_next = bufhead.b_prev = &bufhead;
+ bufcnt = MAXBUFSPACE / sblock.fs_bsize;
+ if (bufcnt < MINBUFS)
+ bufcnt = MINBUFS;
+ for (i = 0; i < bufcnt; i++) {
+ bp = (struct bufarea *)malloc(sizeof(struct bufarea));
+ bufp = malloc((unsigned int)sblock.fs_bsize);
+ if (bp == NULL || bufp == NULL) {
+ if (i >= MINBUFS)
+ break;
+ errx(EEXIT, "cannot allocate buffer pool");
+ }
+ bp->b_un.b_buf = bufp;
+ bp->b_prev = &bufhead;
+ bp->b_next = bufhead.b_next;
+ bufhead.b_next->b_prev = bp;
+ bufhead.b_next = bp;
+ initbarea(bp);
+ }
+ bufhead.b_size = i; /* save number of buffers */
+}
+
+/*
+ * Manage a cache of directory blocks.
+ */
+struct bufarea *
+getdatablk(ufs2_daddr_t blkno, long size)
+{
+ struct bufarea *bp;
+
+ for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
+ if (bp->b_bno == fsbtodb(&sblock, blkno))
+ goto foundit;
+ for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
+ if ((bp->b_flags & B_INUSE) == 0)
+ break;
+ if (bp == &bufhead)
+ errx(EEXIT, "deadlocked buffer pool");
+ getblk(bp, blkno, size);
+ /* fall through */
+foundit:
+ bp->b_prev->b_next = bp->b_next;
+ bp->b_next->b_prev = bp->b_prev;
+ bp->b_prev = &bufhead;
+ bp->b_next = bufhead.b_next;
+ bufhead.b_next->b_prev = bp;
+ bufhead.b_next = bp;
+ bp->b_flags |= B_INUSE;
+ return (bp);
+}
+
+void
+getblk(struct bufarea *bp, ufs2_daddr_t blk, long size)
+{
+ ufs2_daddr_t dblk;
+
+ totalreads++;
+ dblk = fsbtodb(&sblock, blk);
+ if (bp->b_bno != dblk) {
+ flush(fswritefd, bp);
+ diskreads++;
+ bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
+ bp->b_bno = dblk;
+ bp->b_size = size;
+ }
+}
+
+void
+flush(int fd, struct bufarea *bp)
+{
+ int i, j;
+
+ if (!bp->b_dirty)
+ return;
+ bp->b_dirty = 0;
+ if (fswritefd < 0) {
+ pfatal("WRITING IN READ_ONLY MODE.\n");
+ return;
+ }
+ if (bp->b_errs != 0)
+ pfatal("WRITING %sZERO'ED BLOCK %lld TO DISK\n",
+ (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
+ (long long)bp->b_bno);
+ bp->b_errs = 0;
+ bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
+ if (bp != &sblk)
+ return;
+ for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
+ bwrite(fswritefd, (char *)sblock.fs_csp + i,
+ fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
+ sblock.fs_cssize - i < sblock.fs_bsize ?
+ sblock.fs_cssize - i : sblock.fs_bsize);
+ }
+}
+
+void
+rwerror(const char *mesg, ufs2_daddr_t blk)
+{
+
+ if (bkgrdcheck)
+ exit(EEXIT);
+ if (preen == 0)
+ printf("\n");
+ pfatal("CANNOT %s: %ld", mesg, (long)blk);
+ if (reply("CONTINUE") == 0)
+ exit(EEXIT);
+}
+
+void
+ckfini(int markclean)
+{
+ struct bufarea *bp, *nbp;
+ int ofsmodified, cnt = 0;
+
+ if (bkgrdflag) {
+ unlink(snapname);
+ if ((!(sblock.fs_flags & FS_UNCLEAN)) != markclean) {
+ cmd.value = FS_UNCLEAN;
+ cmd.size = markclean ? -1 : 1;
+ if (sysctlbyname("vfs.ffs.setflags", 0, 0,
+ &cmd, sizeof cmd) == -1)
+ rwerror("SET FILE SYSTEM FLAGS", FS_UNCLEAN);
+ if (!preen) {
+ printf("\n***** FILE SYSTEM MARKED %s *****\n",
+ markclean ? "CLEAN" : "DIRTY");
+ if (!markclean)
+ rerun = 1;
+ }
+ } else if (!preen && !markclean) {
+ printf("\n***** FILE SYSTEM STILL DIRTY *****\n");
+ rerun = 1;
+ }
+ }
+ if (fswritefd < 0) {
+ (void)close(fsreadfd);
+ return;
+ }
+ flush(fswritefd, &sblk);
+ if (havesb && cursnapshot == 0 && sblock.fs_magic == FS_UFS2_MAGIC &&
+ sblk.b_bno != sblock.fs_sblockloc / dev_bsize &&
+ !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
+ sblk.b_bno = sblock.fs_sblockloc / dev_bsize;
+ sbdirty();
+ flush(fswritefd, &sblk);
+ }
+ flush(fswritefd, &cgblk);
+ free(cgblk.b_un.b_buf);
+ for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
+ cnt++;
+ flush(fswritefd, bp);
+ nbp = bp->b_prev;
+ free(bp->b_un.b_buf);
+ free((char *)bp);
+ }
+ if (bufhead.b_size != cnt)
+ errx(EEXIT, "panic: lost %d buffers", bufhead.b_size - cnt);
+ pbp = pdirbp = (struct bufarea *)0;
+ if (cursnapshot == 0 && sblock.fs_clean != markclean) {
+ if ((sblock.fs_clean = markclean) != 0) {
+ sblock.fs_flags &= ~(FS_UNCLEAN | FS_NEEDSFSCK);
+ sblock.fs_pendingblocks = 0;
+ sblock.fs_pendinginodes = 0;
+ }
+ sbdirty();
+ ofsmodified = fsmodified;
+ flush(fswritefd, &sblk);
+ fsmodified = ofsmodified;
+ if (!preen) {
+ printf("\n***** FILE SYSTEM MARKED %s *****\n",
+ markclean ? "CLEAN" : "DIRTY");
+ if (!markclean)
+ rerun = 1;
+ }
+ } else if (!preen && !markclean) {
+ printf("\n***** FILE SYSTEM STILL DIRTY *****\n");
+ rerun = 1;
+ }
+ if (debug && totalreads > 0)
+ printf("cache missed %ld of %ld (%d%%)\n", diskreads,
+ totalreads, (int)(diskreads * 100 / totalreads));
+ (void)close(fsreadfd);
+ (void)close(fswritefd);
+}
+
+int
+bread(int fd, char *buf, ufs2_daddr_t blk, long size)
+{
+ char *cp;
+ int i, errs;
+ off_t offset;
+
+ offset = blk;
+ offset *= dev_bsize;
+ if (bkgrdflag)
+ slowio_start();
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK BLK", blk);
+ else if (read(fd, buf, (int)size) == size) {
+ if (bkgrdflag)
+ slowio_end();
+ return (0);
+ }
+ rwerror("READ BLK", blk);
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK BLK", blk);
+ errs = 0;
+ memset(buf, 0, (size_t)size);
+ printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
+ for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
+ if (read(fd, cp, (int)secsize) != secsize) {
+ (void)lseek(fd, offset + i + secsize, 0);
+ if (secsize != dev_bsize && dev_bsize != 1)
+ printf(" %jd (%jd),",
+ (intmax_t)(blk * dev_bsize + i) / secsize,
+ (intmax_t)blk + i / dev_bsize);
+ else
+ printf(" %jd,", (intmax_t)blk + i / dev_bsize);
+ errs++;
+ }
+ }
+ printf("\n");
+ if (errs)
+ resolved = 0;
+ return (errs);
+}
+
+void
+bwrite(int fd, char *buf, ufs2_daddr_t blk, long size)
+{
+ int i;
+ char *cp;
+ off_t offset;
+
+ if (fd < 0)
+ return;
+ offset = blk;
+ offset *= dev_bsize;
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK BLK", blk);
+ else if (write(fd, buf, (int)size) == size) {
+ fsmodified = 1;
+ return;
+ }
+ resolved = 0;
+ rwerror("WRITE BLK", blk);
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK BLK", blk);
+ printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
+ for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
+ if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
+ (void)lseek(fd, offset + i + dev_bsize, 0);
+ printf(" %jd,", (intmax_t)blk + i / dev_bsize);
+ }
+ printf("\n");
+ return;
+}
+
+/*
+ * allocate a data block with the specified number of fragments
+ */
+ufs2_daddr_t
+allocblk(long frags)
+{
+ int i, j, k, cg, baseblk;
+ struct cg *cgp = &cgrp;
+
+ if (frags <= 0 || frags > sblock.fs_frag)
+ return (0);
+ for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
+ for (j = 0; j <= sblock.fs_frag - frags; j++) {
+ if (testbmap(i + j))
+ continue;
+ for (k = 1; k < frags; k++)
+ if (testbmap(i + j + k))
+ break;
+ if (k < frags) {
+ j += k;
+ continue;
+ }
+ cg = dtog(&sblock, i + j);
+ getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
+ if (!cg_chkmagic(cgp))
+ pfatal("CG %d: BAD MAGIC NUMBER\n", cg);
+ baseblk = dtogd(&sblock, i + j);
+ for (k = 0; k < frags; k++) {
+ setbmap(i + j + k);
+ clrbit(cg_blksfree(cgp), baseblk + k);
+ }
+ n_blks += frags;
+ if (frags == sblock.fs_frag)
+ cgp->cg_cs.cs_nbfree--;
+ else
+ cgp->cg_cs.cs_nffree -= frags;
+ cgdirty();
+ return (i + j);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Free a previously allocated block
+ */
+void
+freeblk(ufs2_daddr_t blkno, long frags)
+{
+ struct inodesc idesc;
+
+ idesc.id_blkno = blkno;
+ idesc.id_numfrags = frags;
+ (void)pass4check(&idesc);
+}
+
+/* Slow down IO so as to leave some disk bandwidth for other processes */
+void
+slowio_start()
+{
+
+ /* Delay one in every 8 operations */
+ slowio_pollcnt = (slowio_pollcnt + 1) & 7;
+ if (slowio_pollcnt == 0) {
+ gettimeofday(&slowio_starttime, NULL);
+ }
+}
+
+void
+slowio_end()
+{
+ struct timeval tv;
+ int delay_usec;
+
+ if (slowio_pollcnt != 0)
+ return;
+
+ /* Update the slowdown interval. */
+ gettimeofday(&tv, NULL);
+ delay_usec = (tv.tv_sec - slowio_starttime.tv_sec) * 1000000 +
+ (tv.tv_usec - slowio_starttime.tv_usec);
+ if (delay_usec < 64)
+ delay_usec = 64;
+ if (delay_usec > 2500000)
+ delay_usec = 2500000;
+ slowio_delay_usec = (slowio_delay_usec * 63 + delay_usec) >> 6;
+ /* delay by 8 times the average IO delay */
+ if (slowio_delay_usec > 64)
+ usleep(slowio_delay_usec * 8);
+}
+
+/*
+ * Find a pathname
+ */
+void
+getpathname(char *namebuf, ino_t curdir, ino_t ino)
+{
+ int len;
+ char *cp;
+ struct inodesc idesc;
+ static int busy = 0;
+
+ if (curdir == ino && ino == ROOTINO) {
+ (void)strcpy(namebuf, "/");
+ return;
+ }
+ if (busy || !INO_IS_DVALID(curdir)) {
+ (void)strcpy(namebuf, "?");
+ return;
+ }
+ busy = 1;
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = DATA;
+ idesc.id_fix = IGNORE;
+ cp = &namebuf[MAXPATHLEN - 1];
+ *cp = '\0';
+ if (curdir != ino) {
+ idesc.id_parent = curdir;
+ goto namelookup;
+ }
+ while (ino != ROOTINO) {
+ idesc.id_number = ino;
+ idesc.id_func = findino;
+ idesc.id_name = strdup("..");
+ if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
+ break;
+ namelookup:
+ idesc.id_number = idesc.id_parent;
+ idesc.id_parent = ino;
+ idesc.id_func = findname;
+ idesc.id_name = namebuf;
+ if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
+ break;
+ len = strlen(namebuf);
+ cp -= len;
+ memmove(cp, namebuf, (size_t)len);
+ *--cp = '/';
+ if (cp < &namebuf[MAXNAMLEN])
+ break;
+ ino = idesc.id_number;
+ }
+ busy = 0;
+ if (ino != ROOTINO)
+ *--cp = '?';
+ memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
+}
+
+void
+catch(int sig __unused)
+{
+
+ ckfini(0);
+ exit(12);
+}
+
+/*
+ * When preening, allow a single quit to signal
+ * a special exit after file system checks complete
+ * so that reboot sequence may be interrupted.
+ */
+void
+catchquit(int sig __unused)
+{
+ printf("returning to single-user after file system check\n");
+ returntosingle = 1;
+ (void)signal(SIGQUIT, SIG_DFL);
+}
+
+/*
+ * determine whether an inode should be fixed.
+ */
+int
+dofix(struct inodesc *idesc, const char *msg)
+{
+
+ switch (idesc->id_fix) {
+
+ case DONTKNOW:
+ if (idesc->id_type == DATA)
+ direrror(idesc->id_number, msg);
+ else
+ pwarn("%s", msg);
+ if (preen) {
+ printf(" (SALVAGED)\n");
+ idesc->id_fix = FIX;
+ return (ALTERED);
+ }
+ if (reply("SALVAGE") == 0) {
+ idesc->id_fix = NOFIX;
+ return (0);
+ }
+ idesc->id_fix = FIX;
+ return (ALTERED);
+
+ case FIX:
+ return (ALTERED);
+
+ case NOFIX:
+ case IGNORE:
+ return (0);
+
+ default:
+ errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix);
+ }
+ /* NOTREACHED */
+ return (0);
+}
+
+#include <stdarg.h>
+
+/*
+ * An unexpected inconsistency occured.
+ * Die if preening or file system is running with soft dependency protocol,
+ * otherwise just print message and continue.
+ */
+void
+pfatal(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ if (!preen) {
+ (void)vfprintf(stdout, fmt, ap);
+ va_end(ap);
+ if (usedsoftdep)
+ (void)fprintf(stdout,
+ "\nUNEXPECTED SOFT UPDATE INCONSISTENCY\n");
+ /*
+ * Force foreground fsck to clean up inconsistency.
+ */
+ if (bkgrdflag) {
+ cmd.value = FS_NEEDSFSCK;
+ cmd.size = 1;
+ if (sysctlbyname("vfs.ffs.setflags", 0, 0,
+ &cmd, sizeof cmd) == -1)
+ pwarn("CANNOT SET FS_NEEDSFSCK FLAG\n");
+ fprintf(stdout, "CANNOT RUN IN BACKGROUND\n");
+ ckfini(0);
+ exit(EEXIT);
+ }
+ return;
+ }
+ if (cdevname == NULL)
+ cdevname = strdup("fsck");
+ (void)fprintf(stdout, "%s: ", cdevname);
+ (void)vfprintf(stdout, fmt, ap);
+ (void)fprintf(stdout,
+ "\n%s: UNEXPECTED%sINCONSISTENCY; RUN fsck MANUALLY.\n",
+ cdevname, usedsoftdep ? " SOFT UPDATE " : " ");
+ /*
+ * Force foreground fsck to clean up inconsistency.
+ */
+ if (bkgrdflag) {
+ cmd.value = FS_NEEDSFSCK;
+ cmd.size = 1;
+ if (sysctlbyname("vfs.ffs.setflags", 0, 0,
+ &cmd, sizeof cmd) == -1)
+ pwarn("CANNOT SET FS_NEEDSFSCK FLAG\n");
+ }
+ ckfini(0);
+ exit(EEXIT);
+}
+
+/*
+ * Pwarn just prints a message when not preening or running soft dependency
+ * protocol, or a warning (preceded by filename) when preening.
+ */
+void
+pwarn(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ if (preen)
+ (void)fprintf(stdout, "%s: ", cdevname);
+ (void)vfprintf(stdout, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Stub for routines from kernel.
+ */
+void
+panic(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ pfatal("INTERNAL INCONSISTENCY:");
+ (void)vfprintf(stdout, fmt, ap);
+ va_end(ap);
+ exit(EEXIT);
+}
diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c
new file mode 100644
index 0000000..e470f99
--- /dev/null
+++ b/sbin/fsck_ffs/inode.c
@@ -0,0 +1,678 @@
+/*
+ * Copyright (c) 1980, 1986, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char sccsid[] = "@(#)inode.c 8.8 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stdint.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <pwd.h>
+#include <string.h>
+
+#include "fsck.h"
+
+static ino_t startinum;
+
+static int iblock(struct inodesc *, long ilevel, off_t isize);
+
+int
+ckinode(union dinode *dp, struct inodesc *idesc)
+{
+ off_t remsize, sizepb;
+ int i, offset, ret;
+ union dinode dino;
+ ufs2_daddr_t ndb;
+ mode_t mode;
+ char pathbuf[MAXPATHLEN + 1];
+
+ if (idesc->id_fix != IGNORE)
+ idesc->id_fix = DONTKNOW;
+ idesc->id_lbn = -1;
+ idesc->id_entryno = 0;
+ idesc->id_filesize = DIP(dp, di_size);
+ mode = DIP(dp, di_mode) & IFMT;
+ if (mode == IFBLK || mode == IFCHR || (mode == IFLNK &&
+ DIP(dp, di_size) < (unsigned)sblock.fs_maxsymlinklen))
+ return (KEEPON);
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ dino.dp1 = dp->dp1;
+ else
+ dino.dp2 = dp->dp2;
+ ndb = howmany(DIP(&dino, di_size), sblock.fs_bsize);
+ for (i = 0; i < NDADDR; i++) {
+ idesc->id_lbn++;
+ if (--ndb == 0 &&
+ (offset = blkoff(&sblock, DIP(&dino, di_size))) != 0)
+ idesc->id_numfrags =
+ numfrags(&sblock, fragroundup(&sblock, offset));
+ else
+ idesc->id_numfrags = sblock.fs_frag;
+ if (DIP(&dino, di_db[i]) == 0) {
+ if (idesc->id_type == DATA && ndb >= 0) {
+ /* An empty block in a directory XXX */
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
+ pathbuf);
+ if (reply("ADJUST LENGTH") == 1) {
+ dp = ginode(idesc->id_number);
+ DIP_SET(dp, di_size,
+ i * sblock.fs_bsize);
+ printf(
+ "YOU MUST RERUN FSCK AFTERWARDS\n");
+ rerun = 1;
+ inodirty();
+
+ }
+ }
+ continue;
+ }
+ idesc->id_blkno = DIP(&dino, di_db[i]);
+ if (idesc->id_type != DATA)
+ ret = (*idesc->id_func)(idesc);
+ else
+ ret = dirscan(idesc);
+ if (ret & STOP)
+ return (ret);
+ }
+ idesc->id_numfrags = sblock.fs_frag;
+ remsize = DIP(&dino, di_size) - sblock.fs_bsize * NDADDR;
+ sizepb = sblock.fs_bsize;
+ for (i = 0; i < NIADDR; i++) {
+ sizepb *= NINDIR(&sblock);
+ if (DIP(&dino, di_ib[i])) {
+ idesc->id_blkno = DIP(&dino, di_ib[i]);
+ ret = iblock(idesc, i + 1, remsize);
+ if (ret & STOP)
+ return (ret);
+ } else {
+ idesc->id_lbn += sizepb / sblock.fs_bsize;
+ if (idesc->id_type == DATA && remsize > 0) {
+ /* An empty block in a directory XXX */
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
+ pathbuf);
+ if (reply("ADJUST LENGTH") == 1) {
+ dp = ginode(idesc->id_number);
+ DIP_SET(dp, di_size,
+ DIP(dp, di_size) - remsize);
+ remsize = 0;
+ printf(
+ "YOU MUST RERUN FSCK AFTERWARDS\n");
+ rerun = 1;
+ inodirty();
+ break;
+ }
+ }
+ }
+ remsize -= sizepb;
+ }
+ return (KEEPON);
+}
+
+static int
+iblock(struct inodesc *idesc, long ilevel, off_t isize)
+{
+ struct bufarea *bp;
+ int i, n, (*func)(struct inodesc *), nif;
+ off_t sizepb;
+ char buf[BUFSIZ];
+ char pathbuf[MAXPATHLEN + 1];
+ union dinode *dp;
+
+ if (idesc->id_type != DATA) {
+ func = idesc->id_func;
+ if (((n = (*func)(idesc)) & KEEPON) == 0)
+ return (n);
+ } else
+ func = dirscan;
+ if (chkrange(idesc->id_blkno, idesc->id_numfrags))
+ return (SKIP);
+ bp = getdatablk(idesc->id_blkno, sblock.fs_bsize);
+ ilevel--;
+ for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++)
+ sizepb *= NINDIR(&sblock);
+ if (howmany(isize, sizepb) > NINDIR(&sblock))
+ nif = NINDIR(&sblock);
+ else
+ nif = howmany(isize, sizepb);
+ if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) {
+ for (i = nif; i < NINDIR(&sblock); i++) {
+ if (IBLK(bp, i) == 0)
+ continue;
+ (void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu",
+ (u_long)idesc->id_number);
+ if (preen) {
+ pfatal("%s", buf);
+ } else if (dofix(idesc, buf)) {
+ IBLK_SET(bp, i, 0);
+ dirty(bp);
+ }
+ }
+ flush(fswritefd, bp);
+ }
+ for (i = 0; i < nif; i++) {
+ if (ilevel == 0)
+ idesc->id_lbn++;
+ if (IBLK(bp, i)) {
+ idesc->id_blkno = IBLK(bp, i);
+ if (ilevel == 0)
+ n = (*func)(idesc);
+ else
+ n = iblock(idesc, ilevel, isize);
+ if (n & STOP) {
+ bp->b_flags &= ~B_INUSE;
+ return (n);
+ }
+ } else {
+ if (idesc->id_type == DATA && isize > 0) {
+ /* An empty block in a directory XXX */
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
+ pathbuf);
+ if (reply("ADJUST LENGTH") == 1) {
+ dp = ginode(idesc->id_number);
+ DIP_SET(dp, di_size,
+ DIP(dp, di_size) - isize);
+ isize = 0;
+ printf(
+ "YOU MUST RERUN FSCK AFTERWARDS\n");
+ rerun = 1;
+ inodirty();
+ bp->b_flags &= ~B_INUSE;
+ return(STOP);
+ }
+ }
+ }
+ isize -= sizepb;
+ }
+ bp->b_flags &= ~B_INUSE;
+ return (KEEPON);
+}
+
+/*
+ * Check that a block in a legal block number.
+ * Return 0 if in range, 1 if out of range.
+ */
+int
+chkrange(ufs2_daddr_t blk, int cnt)
+{
+ int c;
+
+ if (cnt <= 0 || blk <= 0 || blk > maxfsblock ||
+ cnt - 1 > maxfsblock - blk)
+ return (1);
+ if (cnt > sblock.fs_frag ||
+ fragnum(&sblock, blk) + cnt > sblock.fs_frag) {
+ if (debug)
+ printf("bad size: blk %ld, offset %i, size %d\n",
+ (long)blk, (int)fragnum(&sblock, blk), cnt);
+ return (1);
+ }
+ c = dtog(&sblock, blk);
+ if (blk < cgdmin(&sblock, c)) {
+ if ((blk + cnt) > cgsblock(&sblock, c)) {
+ if (debug) {
+ printf("blk %ld < cgdmin %ld;",
+ (long)blk, (long)cgdmin(&sblock, c));
+ printf(" blk + cnt %ld > cgsbase %ld\n",
+ (long)(blk + cnt),
+ (long)cgsblock(&sblock, c));
+ }
+ return (1);
+ }
+ } else {
+ if ((blk + cnt) > cgbase(&sblock, c+1)) {
+ if (debug) {
+ printf("blk %ld >= cgdmin %ld;",
+ (long)blk, (long)cgdmin(&sblock, c));
+ printf(" blk + cnt %ld > sblock.fs_fpg %ld\n",
+ (long)(blk + cnt), (long)sblock.fs_fpg);
+ }
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * General purpose interface for reading inodes.
+ */
+union dinode *
+ginode(ino_t inumber)
+{
+ ufs2_daddr_t iblk;
+
+ if (inumber < ROOTINO || inumber > maxino)
+ errx(EEXIT, "bad inode number %d to ginode", inumber);
+ if (startinum == 0 ||
+ inumber < startinum || inumber >= startinum + INOPB(&sblock)) {
+ iblk = ino_to_fsba(&sblock, inumber);
+ if (pbp != 0)
+ pbp->b_flags &= ~B_INUSE;
+ pbp = getdatablk(iblk, sblock.fs_bsize);
+ startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock);
+ }
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ return ((union dinode *)
+ &pbp->b_un.b_dinode1[inumber % INOPB(&sblock)]);
+ return ((union dinode *)&pbp->b_un.b_dinode2[inumber % INOPB(&sblock)]);
+}
+
+/*
+ * Special purpose version of ginode used to optimize first pass
+ * over all the inodes in numerical order.
+ */
+static ino_t nextino, lastinum, lastvalidinum;
+static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
+static caddr_t inodebuf;
+
+union dinode *
+getnextinode(ino_t inumber)
+{
+ long size;
+ ufs2_daddr_t dblk;
+ union dinode *dp;
+ static caddr_t nextinop;
+
+ if (inumber != nextino++ || inumber > lastvalidinum)
+ errx(EEXIT, "bad inode number %d to nextinode", inumber);
+ if (inumber >= lastinum) {
+ readcnt++;
+ dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
+ if (readcnt % readpercg == 0) {
+ size = partialsize;
+ lastinum += partialcnt;
+ } else {
+ size = inobufsize;
+ lastinum += fullcnt;
+ }
+ /*
+ * If bread returns an error, it will already have zeroed
+ * out the buffer, so we do not need to do so here.
+ */
+ (void)bread(fsreadfd, inodebuf, dblk, size);
+ nextinop = inodebuf;
+ }
+ dp = (union dinode *)nextinop;
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ nextinop += sizeof(struct ufs1_dinode);
+ else
+ nextinop += sizeof(struct ufs2_dinode);
+ return (dp);
+}
+
+void
+setinodebuf(ino_t inum)
+{
+
+ if (inum % sblock.fs_ipg != 0)
+ errx(EEXIT, "bad inode number %d to setinodebuf", inum);
+ lastvalidinum = inum + sblock.fs_ipg - 1;
+ startinum = 0;
+ nextino = inum;
+ lastinum = inum;
+ readcnt = 0;
+ if (inodebuf != NULL)
+ return;
+ inobufsize = blkroundup(&sblock, INOBUFSIZE);
+ fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ?
+ sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
+ readpercg = sblock.fs_ipg / fullcnt;
+ partialcnt = sblock.fs_ipg % fullcnt;
+ partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ?
+ sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
+ if (partialcnt != 0) {
+ readpercg++;
+ } else {
+ partialcnt = fullcnt;
+ partialsize = inobufsize;
+ }
+ if ((inodebuf = malloc((unsigned)inobufsize)) == NULL)
+ errx(EEXIT, "cannot allocate space for inode buffer");
+}
+
+void
+freeinodebuf(void)
+{
+
+ if (inodebuf != NULL)
+ free((char *)inodebuf);
+ inodebuf = NULL;
+}
+
+/*
+ * Routines to maintain information about directory inodes.
+ * This is built during the first pass and used during the
+ * second and third passes.
+ *
+ * Enter inodes into the cache.
+ */
+void
+cacheino(union dinode *dp, ino_t inumber)
+{
+ struct inoinfo *inp, **inpp;
+ int i, blks;
+
+ if (howmany(DIP(dp, di_size), sblock.fs_bsize) > NDADDR)
+ blks = NDADDR + NIADDR;
+ else
+ blks = howmany(DIP(dp, di_size), sblock.fs_bsize);
+ inp = (struct inoinfo *)
+ malloc(sizeof(*inp) + (blks - 1) * sizeof(ufs2_daddr_t));
+ if (inp == NULL)
+ errx(EEXIT, "cannot increase directory list");
+ inpp = &inphead[inumber % dirhash];
+ inp->i_nexthash = *inpp;
+ *inpp = inp;
+ inp->i_parent = inumber == ROOTINO ? ROOTINO : (ino_t)0;
+ inp->i_dotdot = (ino_t)0;
+ inp->i_number = inumber;
+ inp->i_isize = DIP(dp, di_size);
+ inp->i_numblks = blks;
+ for (i = 0; i < (blks < NDADDR ? blks : NDADDR); i++)
+ inp->i_blks[i] = DIP(dp, di_db[i]);
+ if (blks > NDADDR)
+ for (i = 0; i < NIADDR; i++)
+ inp->i_blks[NDADDR + i] = DIP(dp, di_ib[i]);
+ if (inplast == listmax) {
+ listmax += 100;
+ inpsort = (struct inoinfo **)realloc((char *)inpsort,
+ (unsigned)listmax * sizeof(struct inoinfo *));
+ if (inpsort == NULL)
+ errx(EEXIT, "cannot increase directory list");
+ }
+ inpsort[inplast++] = inp;
+}
+
+/*
+ * Look up an inode cache structure.
+ */
+struct inoinfo *
+getinoinfo(ino_t inumber)
+{
+ struct inoinfo *inp;
+
+ for (inp = inphead[inumber % dirhash]; inp; inp = inp->i_nexthash) {
+ if (inp->i_number != inumber)
+ continue;
+ return (inp);
+ }
+ errx(EEXIT, "cannot find inode %d", inumber);
+ return ((struct inoinfo *)0);
+}
+
+/*
+ * Clean up all the inode cache structure.
+ */
+void
+inocleanup(void)
+{
+ struct inoinfo **inpp;
+
+ if (inphead == NULL)
+ return;
+ for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
+ free((char *)(*inpp));
+ free((char *)inphead);
+ free((char *)inpsort);
+ inphead = inpsort = NULL;
+}
+
+void
+inodirty(void)
+{
+
+ dirty(pbp);
+}
+
+void
+clri(struct inodesc *idesc, const char *type, int flag)
+{
+ union dinode *dp;
+
+ dp = ginode(idesc->id_number);
+ if (flag == 1) {
+ pwarn("%s %s", type,
+ (DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE");
+ pinode(idesc->id_number);
+ }
+ if (preen || reply("CLEAR") == 1) {
+ if (preen)
+ printf(" (CLEARED)\n");
+ n_files--;
+ if (bkgrdflag == 0) {
+ (void)ckinode(dp, idesc);
+ inoinfo(idesc->id_number)->ino_state = USTATE;
+ clearinode(dp);
+ inodirty();
+ } else {
+ cmd.value = idesc->id_number;
+ cmd.size = -DIP(dp, di_nlink);
+ if (debug)
+ printf("adjrefcnt ino %ld amt %lld\n",
+ (long)cmd.value, (long long)cmd.size);
+ if (sysctl(adjrefcnt, MIBSIZE, 0, 0,
+ &cmd, sizeof cmd) == -1)
+ rwerror("ADJUST INODE", cmd.value);
+ }
+ }
+}
+
+int
+findname(struct inodesc *idesc)
+{
+ struct direct *dirp = idesc->id_dirp;
+
+ if (dirp->d_ino != idesc->id_parent || idesc->id_entryno < 2) {
+ idesc->id_entryno++;
+ return (KEEPON);
+ }
+ memmove(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1);
+ return (STOP|FOUND);
+}
+
+int
+findino(struct inodesc *idesc)
+{
+ struct direct *dirp = idesc->id_dirp;
+
+ if (dirp->d_ino == 0)
+ return (KEEPON);
+ if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
+ dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) {
+ idesc->id_parent = dirp->d_ino;
+ return (STOP|FOUND);
+ }
+ return (KEEPON);
+}
+
+int
+clearentry(struct inodesc *idesc)
+{
+ struct direct *dirp = idesc->id_dirp;
+
+ if (dirp->d_ino != idesc->id_parent || idesc->id_entryno < 2) {
+ idesc->id_entryno++;
+ return (KEEPON);
+ }
+ dirp->d_ino = 0;
+ return (STOP|FOUND|ALTERED);
+}
+
+void
+pinode(ino_t ino)
+{
+ union dinode *dp;
+ char *p;
+ struct passwd *pw;
+ time_t t;
+
+ printf(" I=%lu ", (u_long)ino);
+ if (ino < ROOTINO || ino > maxino)
+ return;
+ dp = ginode(ino);
+ printf(" OWNER=");
+ if ((pw = getpwuid((int)DIP(dp, di_uid))) != 0)
+ printf("%s ", pw->pw_name);
+ else
+ printf("%u ", (unsigned)DIP(dp, di_uid));
+ printf("MODE=%o\n", DIP(dp, di_mode));
+ if (preen)
+ printf("%s: ", cdevname);
+ printf("SIZE=%ju ", (uintmax_t)DIP(dp, di_size));
+ t = DIP(dp, di_mtime);
+ p = ctime(&t);
+ printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
+}
+
+void
+blkerror(ino_t ino, const char *type, ufs2_daddr_t blk)
+{
+
+ pfatal("%jd %s I=%ju", (intmax_t)blk, type, (uintmax_t)ino);
+ printf("\n");
+ switch (inoinfo(ino)->ino_state) {
+
+ case FSTATE:
+ case FZLINK:
+ inoinfo(ino)->ino_state = FCLEAR;
+ return;
+
+ case DSTATE:
+ case DZLINK:
+ inoinfo(ino)->ino_state = DCLEAR;
+ return;
+
+ case FCLEAR:
+ case DCLEAR:
+ return;
+
+ default:
+ errx(EEXIT, "BAD STATE %d TO BLKERR", inoinfo(ino)->ino_state);
+ /* NOTREACHED */
+ }
+}
+
+/*
+ * allocate an unused inode
+ */
+ino_t
+allocino(ino_t request, int type)
+{
+ ino_t ino;
+ union dinode *dp;
+ struct cg *cgp = &cgrp;
+ int cg;
+
+ if (request == 0)
+ request = ROOTINO;
+ else if (inoinfo(request)->ino_state != USTATE)
+ return (0);
+ for (ino = request; ino < maxino; ino++)
+ if (inoinfo(ino)->ino_state == USTATE)
+ break;
+ if (ino == maxino)
+ return (0);
+ cg = ino_to_cg(&sblock, ino);
+ getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
+ if (!cg_chkmagic(cgp))
+ pfatal("CG %d: BAD MAGIC NUMBER\n", cg);
+ setbit(cg_inosused(cgp), ino % sblock.fs_ipg);
+ cgp->cg_cs.cs_nifree--;
+ switch (type & IFMT) {
+ case IFDIR:
+ inoinfo(ino)->ino_state = DSTATE;
+ cgp->cg_cs.cs_ndir++;
+ break;
+ case IFREG:
+ case IFLNK:
+ inoinfo(ino)->ino_state = FSTATE;
+ break;
+ default:
+ return (0);
+ }
+ cgdirty();
+ dp = ginode(ino);
+ DIP_SET(dp, di_db[0], allocblk((long)1));
+ if (DIP(dp, di_db[0]) == 0) {
+ inoinfo(ino)->ino_state = USTATE;
+ return (0);
+ }
+ DIP_SET(dp, di_mode, type);
+ DIP_SET(dp, di_flags, 0);
+ DIP_SET(dp, di_atime, time(NULL));
+ DIP_SET(dp, di_ctime, DIP(dp, di_atime));
+ DIP_SET(dp, di_mtime, DIP(dp, di_ctime));
+ DIP_SET(dp, di_mtimensec, 0);
+ DIP_SET(dp, di_ctimensec, 0);
+ DIP_SET(dp, di_atimensec, 0);
+ DIP_SET(dp, di_size, sblock.fs_fsize);
+ DIP_SET(dp, di_blocks, btodb(sblock.fs_fsize));
+ n_files++;
+ inodirty();
+ inoinfo(ino)->ino_type = IFTODT(type);
+ return (ino);
+}
+
+/*
+ * deallocate an inode
+ */
+void
+freeino(ino_t ino)
+{
+ struct inodesc idesc;
+ union dinode *dp;
+
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ idesc.id_number = ino;
+ dp = ginode(ino);
+ (void)ckinode(dp, &idesc);
+ clearinode(dp);
+ inodirty();
+ inoinfo(ino)->ino_state = USTATE;
+ n_files--;
+}
diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c
new file mode 100644
index 0000000..a717969
--- /dev/null
+++ b/sbin/fsck_ffs/main.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright (c) 1980, 1986, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1986, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/14/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/mount.h>
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+#include <sys/disklabel.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fstab.h>
+#include <grp.h>
+#include <paths.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "fsck.h"
+
+static void usage(void) __dead2;
+static int argtoi(int flag, const char *req, const char *str, int base);
+static int checkfilesys(char *filesys);
+static struct statfs *getmntpt(const char *);
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ struct rlimit rlimit;
+ struct itimerval itimerval;
+ int ret = 0;
+
+ sync();
+ skipclean = 1;
+ while ((ch = getopt(argc, argv, "b:Bc:dfFm:npy")) != -1) {
+ switch (ch) {
+ case 'b':
+ skipclean = 0;
+ bflag = argtoi('b', "number", optarg, 10);
+ printf("Alternate super block location: %d\n", bflag);
+ break;
+
+ case 'B':
+ bkgrdflag = 1;
+ break;
+
+ case 'c':
+ skipclean = 0;
+ cvtlevel = argtoi('c', "conversion level", optarg, 10);
+ if (cvtlevel < 3)
+ errx(EEXIT, "cannot do level %d conversion",
+ cvtlevel);
+ break;
+
+ case 'd':
+ debug++;
+ break;
+
+ case 'f':
+ skipclean = 0;
+ break;
+
+ case 'F':
+ bkgrdcheck = 1;
+ break;
+
+ case 'm':
+ lfmode = argtoi('m', "mode", optarg, 8);
+ if (lfmode &~ 07777)
+ errx(EEXIT, "bad mode to -m: %o", lfmode);
+ printf("** lost+found creation mode %o\n", lfmode);
+ break;
+
+ case 'n':
+ nflag++;
+ yflag = 0;
+ break;
+
+ case 'p':
+ preen++;
+ break;
+
+ case 'y':
+ yflag++;
+ nflag = 0;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!argc)
+ usage();
+
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ (void)signal(SIGINT, catch);
+ if (preen)
+ (void)signal(SIGQUIT, catchquit);
+ signal(SIGINFO, infohandler);
+ if (bkgrdflag) {
+ signal(SIGALRM, alarmhandler);
+ itimerval.it_interval.tv_sec = 5;
+ itimerval.it_interval.tv_usec = 0;
+ itimerval.it_value.tv_sec = 5;
+ itimerval.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &itimerval, NULL);
+ }
+ /*
+ * Push up our allowed memory limit so we can cope
+ * with huge file systems.
+ */
+ if (getrlimit(RLIMIT_DATA, &rlimit) == 0) {
+ rlimit.rlim_cur = rlimit.rlim_max;
+ (void)setrlimit(RLIMIT_DATA, &rlimit);
+ }
+ while (argc-- > 0)
+ (void)checkfilesys(*argv++);
+
+ if (returntosingle)
+ ret = 2;
+ exit(ret);
+}
+
+static int
+argtoi(int flag, const char *req, const char *str, int base)
+{
+ char *cp;
+ int ret;
+
+ ret = (int)strtol(str, &cp, base);
+ if (cp == str || *cp)
+ errx(EEXIT, "-%c flag requires a %s", flag, req);
+ return (ret);
+}
+
+/*
+ * Check the specified file system.
+ */
+/* ARGSUSED */
+static int
+checkfilesys(char *filesys)
+{
+ ufs2_daddr_t n_ffree, n_bfree;
+ struct ufs_args args;
+ struct dups *dp;
+ struct statfs *mntp;
+ struct stat snapdir;
+ struct group *grp;
+ ufs2_daddr_t blks;
+ int cylno, ret;
+ ino_t files;
+ size_t size;
+
+ cdevname = filesys;
+ if (debug && preen)
+ pwarn("starting\n");
+ /*
+ * Make best effort to get the disk name. Check first to see
+ * if it is listed among the mounted file systems. Failing that
+ * check to see if it is listed in /etc/fstab.
+ */
+ mntp = getmntpt(filesys);
+ if (mntp != NULL)
+ filesys = mntp->f_mntfromname;
+ else
+ filesys = blockcheck(filesys);
+ /*
+ * If -F flag specified, check to see whether a background check
+ * is possible and needed. If possible and needed, exit with
+ * status zero. Otherwise exit with status non-zero. A non-zero
+ * exit status will cause a foreground check to be run.
+ */
+ sblock_init();
+ if (bkgrdcheck) {
+ if ((fsreadfd = open(filesys, O_RDONLY)) < 0 || readsb(0) == 0)
+ exit(3); /* Cannot read superblock */
+ close(fsreadfd);
+ if (sblock.fs_flags & FS_NEEDSFSCK)
+ exit(4); /* Earlier background failed */
+ if ((sblock.fs_flags & FS_DOSOFTDEP) == 0)
+ exit(5); /* Not running soft updates */
+ size = MIBSIZE;
+ if (sysctlnametomib("vfs.ffs.adjrefcnt", adjrefcnt, &size) < 0)
+ exit(6); /* Lacks kernel support */
+ if ((mntp == NULL && sblock.fs_clean == 1) ||
+ (mntp != NULL && (sblock.fs_flags & FS_UNCLEAN) == 0))
+ exit(7); /* Filesystem clean, report it now */
+ exit(0);
+ }
+ /*
+ * If we are to do a background check:
+ * Get the mount point information of the file system
+ * create snapshot file
+ * return created snapshot file
+ * if not found, clear bkgrdflag and proceed with normal fsck
+ */
+ if (bkgrdflag) {
+ if (mntp == NULL) {
+ bkgrdflag = 0;
+ pfatal("NOT MOUNTED, CANNOT RUN IN BACKGROUND\n");
+ } else if ((mntp->f_flags & MNT_SOFTDEP) == 0) {
+ bkgrdflag = 0;
+ pfatal("NOT USING SOFT UPDATES, %s\n",
+ "CANNOT RUN IN BACKGROUND");
+ } else if ((mntp->f_flags & MNT_RDONLY) != 0) {
+ bkgrdflag = 0;
+ pfatal("MOUNTED READ-ONLY, CANNOT RUN IN BACKGROUND\n");
+ } else if ((fsreadfd = open(filesys, O_RDONLY)) >= 0) {
+ if (readsb(0) != 0) {
+ if (sblock.fs_flags & FS_NEEDSFSCK) {
+ bkgrdflag = 0;
+ pfatal("UNEXPECTED INCONSISTENCY, %s\n",
+ "CANNOT RUN IN BACKGROUND\n");
+ }
+ if ((sblock.fs_flags & FS_UNCLEAN) == 0 &&
+ skipclean && preen) {
+ /*
+ * file system is clean;
+ * skip snapshot and report it clean
+ */
+ pwarn("FILE SYSTEM CLEAN; %s\n",
+ "SKIPPING CHECKS");
+ goto clean;
+ }
+ }
+ close(fsreadfd);
+ }
+ if (bkgrdflag) {
+ snprintf(snapname, sizeof snapname, "%s/.snap",
+ mntp->f_mntonname);
+ if (stat(snapname, &snapdir) < 0) {
+ if (errno != ENOENT) {
+ bkgrdflag = 0;
+ pfatal("CANNOT FIND %s %s: %s, %s\n",
+ "SNAPSHOT DIRECTORY",
+ snapname, strerror(errno),
+ "CANNOT RUN IN BACKGROUND");
+ } else if ((grp = getgrnam("operator")) == 0 ||
+ mkdir(snapname, 0770) < 0 ||
+ chown(snapname, -1, grp->gr_gid) < 0 ||
+ chmod(snapname, 0770) < 0) {
+ bkgrdflag = 0;
+ pfatal("CANNOT CREATE %s %s: %s, %s\n",
+ "SNAPSHOT DIRECTORY",
+ snapname, strerror(errno),
+ "CANNOT RUN IN BACKGROUND");
+ }
+ } else if (!S_ISDIR(snapdir.st_mode)) {
+ bkgrdflag = 0;
+ pfatal("%s IS NOT A DIRECTORY, %s\n", snapname,
+ "CANNOT RUN IN BACKGROUND");
+ }
+ }
+ if (bkgrdflag) {
+ snprintf(snapname, sizeof snapname,
+ "%s/.snap/fsck_snapshot", mntp->f_mntonname);
+ memset(&args, 0, sizeof args);
+ args.fspec = snapname;
+ while (mount("ffs", mntp->f_mntonname,
+ mntp->f_flags | MNT_UPDATE | MNT_SNAPSHOT,
+ &args) < 0) {
+ if (errno == EEXIST && unlink(snapname) == 0)
+ continue;
+ bkgrdflag = 0;
+ pfatal("CANNOT CREATE SNAPSHOT %s: %s\n",
+ snapname, strerror(errno));
+ break;
+ }
+ if (bkgrdflag != 0)
+ filesys = snapname;
+ }
+ }
+
+ switch (setup(filesys)) {
+ case 0:
+ if (preen)
+ pfatal("CAN'T CHECK FILE SYSTEM.");
+ return (0);
+ case -1:
+ clean:
+ pwarn("clean, %ld free ", (long)(sblock.fs_cstotal.cs_nffree +
+ sblock.fs_frag * sblock.fs_cstotal.cs_nbfree));
+ printf("(%lld frags, %lld blocks, %.1f%% fragmentation)\n",
+ (long long)sblock.fs_cstotal.cs_nffree,
+ (long long)sblock.fs_cstotal.cs_nbfree,
+ sblock.fs_cstotal.cs_nffree * 100.0 / sblock.fs_dsize);
+ return (0);
+ }
+
+ /*
+ * Cleared if any questions answered no. Used to decide if
+ * the superblock should be marked clean.
+ */
+ resolved = 1;
+ /*
+ * 1: scan inodes tallying blocks used
+ */
+ if (preen == 0) {
+ printf("** Last Mounted on %s\n", sblock.fs_fsmnt);
+ if (mntp != NULL && mntp->f_flags & MNT_ROOTFS)
+ printf("** Root file system\n");
+ printf("** Phase 1 - Check Blocks and Sizes\n");
+ }
+ pass1();
+
+ /*
+ * 1b: locate first references to duplicates, if any
+ */
+ if (duplist) {
+ if (preen || usedsoftdep)
+ pfatal("INTERNAL ERROR: dups with -p");
+ printf("** Phase 1b - Rescan For More DUPS\n");
+ pass1b();
+ }
+
+ /*
+ * 2: traverse directories from root to mark all connected directories
+ */
+ if (preen == 0)
+ printf("** Phase 2 - Check Pathnames\n");
+ pass2();
+
+ /*
+ * 3: scan inodes looking for disconnected directories
+ */
+ if (preen == 0)
+ printf("** Phase 3 - Check Connectivity\n");
+ pass3();
+
+ /*
+ * 4: scan inodes looking for disconnected files; check reference counts
+ */
+ if (preen == 0)
+ printf("** Phase 4 - Check Reference Counts\n");
+ pass4();
+
+ /*
+ * 5: check and repair resource counts in cylinder groups
+ */
+ if (preen == 0)
+ printf("** Phase 5 - Check Cyl groups\n");
+ pass5();
+
+ /*
+ * print out summary statistics
+ */
+ n_ffree = sblock.fs_cstotal.cs_nffree;
+ n_bfree = sblock.fs_cstotal.cs_nbfree;
+ files = maxino - ROOTINO - sblock.fs_cstotal.cs_nifree - n_files;
+ blks = n_blks +
+ sblock.fs_ncg * (cgdmin(&sblock, 0) - cgsblock(&sblock, 0));
+ blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0);
+ blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
+ blks = maxfsblock - (n_ffree + sblock.fs_frag * n_bfree) - blks;
+ if (bkgrdflag && (files > 0 || blks > 0)) {
+ countdirs = sblock.fs_cstotal.cs_ndir - countdirs;
+ pwarn("Reclaimed: %ld directories, %ld files, %lld fragments\n",
+ countdirs, (long)files - countdirs, (long long)blks);
+ }
+ pwarn("%ld files, %jd used, %ju free ",
+ (long)n_files, (intmax_t)n_blks,
+ (uintmax_t)n_ffree + sblock.fs_frag * n_bfree);
+ printf("(%ju frags, %ju blocks, %.1f%% fragmentation)\n",
+ (uintmax_t)n_ffree, (uintmax_t)n_bfree,
+ n_ffree * 100.0 / sblock.fs_dsize);
+ if (debug) {
+ if (files < 0)
+ printf("%d inodes missing\n", -files);
+ if (blks < 0)
+ printf("%lld blocks missing\n", -(long long)blks);
+ if (duplist != NULL) {
+ printf("The following duplicate blocks remain:");
+ for (dp = duplist; dp; dp = dp->next)
+ printf(" %lld,", (long long)dp->dup);
+ printf("\n");
+ }
+ }
+ duplist = (struct dups *)0;
+ muldup = (struct dups *)0;
+ inocleanup();
+ if (fsmodified) {
+ sblock.fs_time = time(NULL);
+ sbdirty();
+ }
+ if (cvtlevel && sblk.b_dirty) {
+ /*
+ * Write out the duplicate super blocks
+ */
+ for (cylno = 0; cylno < sblock.fs_ncg; cylno++)
+ bwrite(fswritefd, (char *)&sblock,
+ fsbtodb(&sblock, cgsblock(&sblock, cylno)),
+ SBLOCKSIZE);
+ }
+ if (rerun)
+ resolved = 0;
+
+ /*
+ * Check to see if the file system is mounted read-write.
+ */
+ if (bkgrdflag == 0 && mntp != NULL && (mntp->f_flags & MNT_RDONLY) == 0)
+ resolved = 0;
+ ckfini(resolved);
+
+ for (cylno = 0; cylno < sblock.fs_ncg; cylno++)
+ if (inostathead[cylno].il_stat != NULL)
+ free((char *)inostathead[cylno].il_stat);
+ free((char *)inostathead);
+ inostathead = NULL;
+ if (fsmodified && !preen)
+ printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
+ if (rerun)
+ printf("\n***** PLEASE RERUN FSCK *****\n");
+ if (mntp != NULL) {
+ /*
+ * We modified a mounted file system. Do a mount update on
+ * it unless it is read-write, so we can continue using it
+ * as safely as possible.
+ */
+ if (mntp->f_flags & MNT_RDONLY) {
+ args.fspec = 0;
+ args.export.ex_flags = 0;
+ args.export.ex_root = 0;
+ ret = mount("ufs", mntp->f_mntonname,
+ mntp->f_flags | MNT_UPDATE | MNT_RELOAD, &args);
+ if (ret == 0)
+ return (0);
+ pwarn("mount reload of '%s' failed: %s\n\n",
+ mntp->f_mntonname, strerror(errno));
+ }
+ if (!fsmodified)
+ return (0);
+ if (!preen)
+ printf("\n***** REBOOT NOW *****\n");
+ sync();
+ return (4);
+ }
+ return (0);
+}
+
+/*
+ * Get the mount point information for name.
+ */
+static struct statfs *
+getmntpt(const char *name)
+{
+ struct stat devstat, mntdevstat;
+ char device[sizeof(_PATH_DEV) - 1 + MNAMELEN];
+ char *ddevname;
+ struct statfs *mntbuf, *statfsp;
+ int i, mntsize, isdev;
+
+ if (stat(name, &devstat) != 0)
+ return (NULL);
+ if (S_ISCHR(devstat.st_mode) || S_ISBLK(devstat.st_mode))
+ isdev = 1;
+ else
+ isdev = 0;
+ mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
+ for (i = 0; i < mntsize; i++) {
+ statfsp = &mntbuf[i];
+ ddevname = statfsp->f_mntfromname;
+ if (*ddevname != '/') {
+ strcpy(device, _PATH_DEV);
+ strcat(device, ddevname);
+ strcpy(statfsp->f_mntfromname, device);
+ }
+ if (isdev == 0) {
+ if (strcmp(name, statfsp->f_mntonname))
+ continue;
+ return (statfsp);
+ }
+ if (stat(ddevname, &mntdevstat) == 0 &&
+ mntdevstat.st_rdev == devstat.st_rdev)
+ return (statfsp);
+ }
+ statfsp = NULL;
+ return (statfsp);
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ "usage: %s [-BFpfny] [-b block] [-c level] [-m mode] "
+ "filesystem ...\n",
+ getprogname());
+ exit(1);
+}
diff --git a/sbin/fsck_ffs/pass1.c b/sbin/fsck_ffs/pass1.c
new file mode 100644
index 0000000..f728877
--- /dev/null
+++ b/sbin/fsck_ffs/pass1.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 1980, 1986, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char sccsid[] = "@(#)pass1.c 8.6 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "fsck.h"
+
+static ufs2_daddr_t badblk;
+static ufs2_daddr_t dupblk;
+static ino_t lastino; /* last inode in use */
+
+static void checkinode(ino_t inumber, struct inodesc *);
+
+void
+pass1(void)
+{
+ struct inostat *info;
+ struct inodesc idesc;
+ ino_t inumber, inosused;
+ ufs2_daddr_t i, cgd;
+ u_int8_t *cp;
+ int c;
+
+ /*
+ * Set file system reserved blocks in used block map.
+ */
+ for (c = 0; c < sblock.fs_ncg; c++) {
+ cgd = cgdmin(&sblock, c);
+ if (c == 0) {
+ i = cgbase(&sblock, c);
+ } else
+ i = cgsblock(&sblock, c);
+ for (; i < cgd; i++)
+ setbmap(i);
+ }
+ i = sblock.fs_csaddr;
+ cgd = i + howmany(sblock.fs_cssize, sblock.fs_fsize);
+ for (; i < cgd; i++)
+ setbmap(i);
+
+ /*
+ * Find all allocated blocks.
+ */
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_func = pass1check;
+ n_files = n_blks = 0;
+ for (c = 0; c < sblock.fs_ncg; c++) {
+ inumber = c * sblock.fs_ipg;
+ setinodebuf(inumber);
+ getblk(&cgblk, cgtod(&sblock, c), sblock.fs_cgsize);
+ if (sblock.fs_magic == FS_UFS2_MAGIC)
+ inosused = cgrp.cg_initediblk;
+ else
+ inosused = sblock.fs_ipg;
+ if (got_siginfo) {
+ printf("%s: phase 1: cyl group %d of %d (%d%%)\n",
+ cdevname, c, sblock.fs_ncg,
+ c * 100 / sblock.fs_ncg);
+ got_siginfo = 0;
+ }
+ if (got_sigalarm) {
+ setproctitle("%s p1 %d%%", cdevname,
+ c * 100 / sblock.fs_ncg);
+ got_sigalarm = 0;
+ }
+ /*
+ * If we are using soft updates, then we can trust the
+ * cylinder group inode allocation maps to tell us which
+ * inodes are allocated. We will scan the used inode map
+ * to find the inodes that are really in use, and then
+ * read only those inodes in from disk.
+ */
+ if (preen && usedsoftdep) {
+ if (!cg_chkmagic(&cgrp))
+ pfatal("CG %d: BAD MAGIC NUMBER\n", c);
+ cp = &cg_inosused(&cgrp)[(inosused - 1) / CHAR_BIT];
+ for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) {
+ if (*cp == 0)
+ continue;
+ for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) {
+ if (*cp & i)
+ break;
+ inosused--;
+ }
+ break;
+ }
+ if (inosused < 0)
+ inosused = 0;
+ }
+ /*
+ * Allocate inoinfo structures for the allocated inodes.
+ */
+ inostathead[c].il_numalloced = inosused;
+ if (inosused == 0) {
+ inostathead[c].il_stat = 0;
+ continue;
+ }
+ info = calloc((unsigned)inosused, sizeof(struct inostat));
+ if (info == NULL)
+ errx(EEXIT, "cannot alloc %u bytes for inoinfo",
+ (unsigned)(sizeof(struct inostat) * inosused));
+ inostathead[c].il_stat = info;
+ /*
+ * Scan the allocated inodes.
+ */
+ for (i = 0; i < inosused; i++, inumber++) {
+ if (inumber < ROOTINO) {
+ (void)getnextinode(inumber);
+ continue;
+ }
+ checkinode(inumber, &idesc);
+ }
+ lastino += 1;
+ if (inosused < sblock.fs_ipg || inumber == lastino)
+ continue;
+ /*
+ * If we were not able to determine in advance which inodes
+ * were in use, then reduce the size of the inoinfo structure
+ * to the size necessary to describe the inodes that we
+ * really found.
+ */
+ if (lastino < (c * sblock.fs_ipg))
+ inosused = 0;
+ else
+ inosused = lastino - (c * sblock.fs_ipg);
+ inostathead[c].il_numalloced = inosused;
+ if (inosused == 0) {
+ free(inostathead[c].il_stat);
+ inostathead[c].il_stat = 0;
+ continue;
+ }
+ info = calloc((unsigned)inosused, sizeof(struct inostat));
+ if (info == NULL)
+ errx(EEXIT, "cannot alloc %u bytes for inoinfo",
+ (unsigned)(sizeof(struct inostat) * inosused));
+ memmove(info, inostathead[c].il_stat, inosused * sizeof(*info));
+ free(inostathead[c].il_stat);
+ inostathead[c].il_stat = info;
+ }
+ freeinodebuf();
+}
+
+static void
+checkinode(ino_t inumber, struct inodesc *idesc)
+{
+ union dinode *dp;
+ off_t kernmaxfilesize;
+ ufs2_daddr_t ndb;
+ mode_t mode;
+ int j, ret, offset;
+
+ dp = getnextinode(inumber);
+ mode = DIP(dp, di_mode) & IFMT;
+ if (mode == 0) {
+ if ((sblock.fs_magic == FS_UFS1_MAGIC &&
+ (memcmp(dp->dp1.di_db, ufs1_zino.di_db,
+ NDADDR * sizeof(ufs1_daddr_t)) ||
+ memcmp(dp->dp1.di_ib, ufs1_zino.di_ib,
+ NIADDR * sizeof(ufs1_daddr_t)) ||
+ dp->dp1.di_mode || dp->dp1.di_size)) ||
+ (sblock.fs_magic == FS_UFS2_MAGIC &&
+ (memcmp(dp->dp2.di_db, ufs2_zino.di_db,
+ NDADDR * sizeof(ufs2_daddr_t)) ||
+ memcmp(dp->dp2.di_ib, ufs2_zino.di_ib,
+ NIADDR * sizeof(ufs2_daddr_t)) ||
+ dp->dp2.di_mode || dp->dp2.di_size))) {
+ pfatal("PARTIALLY ALLOCATED INODE I=%lu",
+ (u_long)inumber);
+ if (reply("CLEAR") == 1) {
+ dp = ginode(inumber);
+ clearinode(dp);
+ inodirty();
+ }
+ }
+ inoinfo(inumber)->ino_state = USTATE;
+ return;
+ }
+ lastino = inumber;
+ /* This should match the file size limit in ffs_mountfs(). */
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ kernmaxfilesize = (off_t)0x40000000 * sblock.fs_bsize - 1;
+ else
+ kernmaxfilesize = sblock.fs_maxfilesize;
+ if (DIP(dp, di_size) > kernmaxfilesize ||
+ DIP(dp, di_size) > sblock.fs_maxfilesize ||
+ (mode == IFDIR && DIP(dp, di_size) > MAXDIRSIZE)) {
+ if (debug)
+ printf("bad size %ju:", (uintmax_t)DIP(dp, di_size));
+ goto unknown;
+ }
+ if (!preen && mode == IFMT && reply("HOLD BAD BLOCK") == 1) {
+ dp = ginode(inumber);
+ DIP_SET(dp, di_size, sblock.fs_fsize);
+ DIP_SET(dp, di_mode, IFREG|0600);
+ inodirty();
+ }
+ if ((mode == IFBLK || mode == IFCHR || mode == IFIFO ||
+ mode == IFSOCK) && DIP(dp, di_size) != 0) {
+ if (debug)
+ printf("bad special-file size %ju:",
+ (uintmax_t)DIP(dp, di_size));
+ goto unknown;
+ }
+ if ((mode == IFBLK || mode == IFCHR) &&
+ (dev_t)DIP(dp, di_rdev) == NODEV) {
+ if (debug)
+ printf("bad special-file rdev NODEV:");
+ goto unknown;
+ }
+ ndb = howmany(DIP(dp, di_size), sblock.fs_bsize);
+ if (ndb < 0) {
+ if (debug)
+ printf("bad size %ju ndb %ju:",
+ (uintmax_t)DIP(dp, di_size), (uintmax_t)ndb);
+ goto unknown;
+ }
+ if (mode == IFBLK || mode == IFCHR)
+ ndb++;
+ if (mode == IFLNK) {
+ /*
+ * Fake ndb value so direct/indirect block checks below
+ * will detect any garbage after symlink string.
+ */
+ if (DIP(dp, di_size) < (off_t)sblock.fs_maxsymlinklen) {
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ ndb = howmany(DIP(dp, di_size),
+ sizeof(ufs1_daddr_t));
+ else
+ ndb = howmany(DIP(dp, di_size),
+ sizeof(ufs2_daddr_t));
+ if (ndb > NDADDR) {
+ j = ndb - NDADDR;
+ for (ndb = 1; j > 1; j--)
+ ndb *= NINDIR(&sblock);
+ ndb += NDADDR;
+ }
+ }
+ }
+ for (j = ndb; ndb < NDADDR && j < NDADDR; j++)
+ if (DIP(dp, di_db[j]) != 0) {
+ if (debug)
+ printf("bad direct addr[%d]: %ju\n", j,
+ (uintmax_t)DIP(dp, di_db[j]));
+ goto unknown;
+ }
+ for (j = 0, ndb -= NDADDR; ndb > 0; j++)
+ ndb /= NINDIR(&sblock);
+ for (; j < NIADDR; j++)
+ if (DIP(dp, di_ib[j]) != 0) {
+ if (debug)
+ printf("bad indirect addr: %ju\n",
+ (uintmax_t)DIP(dp, di_ib[j]));
+ goto unknown;
+ }
+ if (ftypeok(dp) == 0)
+ goto unknown;
+ n_files++;
+ inoinfo(inumber)->ino_linkcnt = DIP(dp, di_nlink);
+ if (mode == IFDIR) {
+ if (DIP(dp, di_size) == 0)
+ inoinfo(inumber)->ino_state = DCLEAR;
+ else if (DIP(dp, di_nlink) <= 0)
+ inoinfo(inumber)->ino_state = DZLINK;
+ else
+ inoinfo(inumber)->ino_state = DSTATE;
+ cacheino(dp, inumber);
+ countdirs++;
+ } else if (DIP(dp, di_nlink) <= 0)
+ inoinfo(inumber)->ino_state = FZLINK;
+ else
+ inoinfo(inumber)->ino_state = FSTATE;
+ inoinfo(inumber)->ino_type = IFTODT(mode);
+ badblk = dupblk = 0;
+ idesc->id_number = inumber;
+ if (DIP(dp, di_flags) & SF_SNAPSHOT)
+ idesc->id_type = SNAP;
+ else
+ idesc->id_type = ADDR;
+ (void)ckinode(dp, idesc);
+ if (sblock.fs_magic == FS_UFS2_MAGIC && dp->dp2.di_extsize > 0) {
+ idesc->id_type = ADDR;
+ ndb = howmany(dp->dp2.di_extsize, sblock.fs_bsize);
+ for (j = 0; j < NXADDR; j++) {
+ if (--ndb == 0 &&
+ (offset = blkoff(&sblock, dp->dp2.di_extsize)) != 0)
+ idesc->id_numfrags = numfrags(&sblock,
+ fragroundup(&sblock, offset));
+ else
+ idesc->id_numfrags = sblock.fs_frag;
+ if (dp->dp2.di_extb[j] == 0)
+ continue;
+ idesc->id_blkno = dp->dp2.di_extb[j];
+ ret = (*idesc->id_func)(idesc);
+ if (ret & STOP)
+ break;
+ }
+ }
+ if (sblock.fs_magic == FS_UFS2_MAGIC)
+ eascan(idesc, &dp->dp2);
+ idesc->id_entryno *= btodb(sblock.fs_fsize);
+ if (DIP(dp, di_blocks) != idesc->id_entryno) {
+ pwarn("INCORRECT BLOCK COUNT I=%lu (%ju should be %ju)",
+ (u_long)inumber, (uintmax_t)DIP(dp, di_blocks),
+ (uintmax_t)idesc->id_entryno);
+ if (preen)
+ printf(" (CORRECTED)\n");
+ else if (reply("CORRECT") == 0)
+ return;
+ if (bkgrdflag == 0) {
+ dp = ginode(inumber);
+ DIP_SET(dp, di_blocks, idesc->id_entryno);
+ inodirty();
+ } else {
+ cmd.value = idesc->id_number;
+ cmd.size = idesc->id_entryno - DIP(dp, di_blocks);
+ if (debug)
+ printf("adjblkcnt ino %ju amount %lld\n",
+ (uintmax_t)cmd.value, (long long)cmd.size);
+ if (sysctl(adjblkcnt, MIBSIZE, 0, 0,
+ &cmd, sizeof cmd) == -1)
+ rwerror("ADJUST INODE BLOCK COUNT", cmd.value);
+ }
+ }
+ return;
+unknown:
+ pfatal("UNKNOWN FILE TYPE I=%lu", (u_long)inumber);
+ inoinfo(inumber)->ino_state = FCLEAR;
+ if (reply("CLEAR") == 1) {
+ inoinfo(inumber)->ino_state = USTATE;
+ dp = ginode(inumber);
+ clearinode(dp);
+ inodirty();
+ }
+}
+
+int
+pass1check(struct inodesc *idesc)
+{
+ int res = KEEPON;
+ int anyout, nfrags;
+ ufs2_daddr_t blkno = idesc->id_blkno;
+ struct dups *dlp;
+ struct dups *new;
+
+ if (idesc->id_type == SNAP) {
+ if (blkno == BLK_NOCOPY)
+ return (KEEPON);
+ if (idesc->id_number == cursnapshot) {
+ if (blkno == blkstofrags(&sblock, idesc->id_lbn))
+ return (KEEPON);
+ if (blkno == BLK_SNAP) {
+ blkno = blkstofrags(&sblock, idesc->id_lbn);
+ idesc->id_entryno -= idesc->id_numfrags;
+ }
+ } else {
+ if (blkno == BLK_SNAP)
+ return (KEEPON);
+ }
+ }
+ if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) {
+ blkerror(idesc->id_number, "BAD", blkno);
+ if (badblk++ >= MAXBAD) {
+ pwarn("EXCESSIVE BAD BLKS I=%lu",
+ (u_long)idesc->id_number);
+ if (preen)
+ printf(" (SKIPPING)\n");
+ else if (reply("CONTINUE") == 0) {
+ ckfini(0);
+ exit(EEXIT);
+ }
+ return (STOP);
+ }
+ }
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (anyout && chkrange(blkno, 1)) {
+ res = SKIP;
+ } else if (!testbmap(blkno)) {
+ n_blks++;
+ setbmap(blkno);
+ } else {
+ blkerror(idesc->id_number, "DUP", blkno);
+ if (dupblk++ >= MAXDUP) {
+ pwarn("EXCESSIVE DUP BLKS I=%lu",
+ (u_long)idesc->id_number);
+ if (preen)
+ printf(" (SKIPPING)\n");
+ else if (reply("CONTINUE") == 0) {
+ ckfini(0);
+ exit(EEXIT);
+ }
+ return (STOP);
+ }
+ new = (struct dups *)malloc(sizeof(struct dups));
+ if (new == NULL) {
+ pfatal("DUP TABLE OVERFLOW.");
+ if (reply("CONTINUE") == 0) {
+ ckfini(0);
+ exit(EEXIT);
+ }
+ return (STOP);
+ }
+ new->dup = blkno;
+ if (muldup == 0) {
+ duplist = muldup = new;
+ new->next = 0;
+ } else {
+ new->next = muldup->next;
+ muldup->next = new;
+ }
+ for (dlp = duplist; dlp != muldup; dlp = dlp->next)
+ if (dlp->dup == blkno)
+ break;
+ if (dlp == muldup && dlp->dup != blkno)
+ muldup = new;
+ }
+ /*
+ * count the number of blocks found in id_entryno
+ */
+ idesc->id_entryno++;
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ffs/pass1b.c b/sbin/fsck_ffs/pass1b.c
new file mode 100644
index 0000000..e635935
--- /dev/null
+++ b/sbin/fsck_ffs/pass1b.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 1980, 1986, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char sccsid[] = "@(#)pass1b.c 8.4 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <string.h>
+
+#include "fsck.h"
+
+static struct dups *duphead;
+static int pass1bcheck(struct inodesc *);
+
+void
+pass1b(void)
+{
+ int c, i;
+ union dinode *dp;
+ struct inodesc idesc;
+ ino_t inumber;
+
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass1bcheck;
+ duphead = duplist;
+ inumber = 0;
+ for (c = 0; c < sblock.fs_ncg; c++) {
+ if (got_siginfo) {
+ printf("%s: phase 1b: cyl group %d of %d (%d%%)\n",
+ cdevname, c, sblock.fs_ncg,
+ c * 100 / sblock.fs_ncg);
+ got_siginfo = 0;
+ }
+ if (got_sigalarm) {
+ setproctitle("%s p1b %d%%", cdevname,
+ c * 100 / sblock.fs_ncg);
+ got_sigalarm = 0;
+ }
+ for (i = 0; i < sblock.fs_ipg; i++, inumber++) {
+ if (inumber < ROOTINO)
+ continue;
+ dp = ginode(inumber);
+ if (dp == NULL)
+ continue;
+ idesc.id_number = inumber;
+ if (inoinfo(inumber)->ino_state != USTATE &&
+ (ckinode(dp, &idesc) & STOP))
+ return;
+ }
+ }
+}
+
+static int
+pass1bcheck(struct inodesc *idesc)
+{
+ struct dups *dlp;
+ int nfrags, res = KEEPON;
+ ufs2_daddr_t blkno = idesc->id_blkno;
+
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (chkrange(blkno, 1))
+ res = SKIP;
+ for (dlp = duphead; dlp; dlp = dlp->next) {
+ if (dlp->dup == blkno) {
+ blkerror(idesc->id_number, "DUP", blkno);
+ dlp->dup = duphead->dup;
+ duphead->dup = blkno;
+ duphead = duphead->next;
+ }
+ if (dlp == muldup)
+ break;
+ }
+ if (muldup == 0 || duphead == muldup->next)
+ return (STOP);
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ffs/pass2.c b/sbin/fsck_ffs/pass2.c
new file mode 100644
index 0000000..c4fdd2a
--- /dev/null
+++ b/sbin/fsck_ffs/pass2.c
@@ -0,0 +1,472 @@
+/*
+ * Copyright (c) 1980, 1986, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char sccsid[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "fsck.h"
+
+#define MINDIRSIZE (sizeof (struct dirtemplate))
+
+static int blksort(const void *, const void *);
+static int pass2check(struct inodesc *);
+
+void
+pass2(void)
+{
+ union dinode *dp;
+ struct inoinfo **inpp, *inp;
+ struct inoinfo **inpend;
+ struct inodesc curino;
+ union dinode dino;
+ int i;
+ char pathbuf[MAXPATHLEN + 1];
+
+ switch (inoinfo(ROOTINO)->ino_state) {
+
+ case USTATE:
+ pfatal("ROOT INODE UNALLOCATED");
+ if (reply("ALLOCATE") == 0) {
+ ckfini(0);
+ exit(EEXIT);
+ }
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
+ break;
+
+ case DCLEAR:
+ pfatal("DUPS/BAD IN ROOT INODE");
+ if (reply("REALLOCATE")) {
+ freeino(ROOTINO);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
+ break;
+ }
+ if (reply("CONTINUE") == 0) {
+ ckfini(0);
+ exit(EEXIT);
+ }
+ break;
+
+ case FSTATE:
+ case FCLEAR:
+ case FZLINK:
+ pfatal("ROOT INODE NOT DIRECTORY");
+ if (reply("REALLOCATE")) {
+ freeino(ROOTINO);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
+ break;
+ }
+ if (reply("FIX") == 0) {
+ ckfini(0);
+ exit(EEXIT);
+ }
+ dp = ginode(ROOTINO);
+ DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT);
+ DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR);
+ inodirty();
+ break;
+
+ case DSTATE:
+ case DZLINK:
+ break;
+
+ default:
+ errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
+ inoinfo(ROOTINO)->ino_state);
+ }
+ inoinfo(ROOTINO)->ino_state = DFOUND;
+ inoinfo(WINO)->ino_state = FSTATE;
+ inoinfo(WINO)->ino_type = DT_WHT;
+ /*
+ * Sort the directory list into disk block order.
+ */
+ qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
+ /*
+ * Check the integrity of each directory.
+ */
+ memset(&curino, 0, sizeof(struct inodesc));
+ curino.id_type = DATA;
+ curino.id_func = pass2check;
+ inpend = &inpsort[inplast];
+ for (inpp = inpsort; inpp < inpend; inpp++) {
+ if (got_siginfo) {
+ printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname,
+ inpp - inpsort, (int)inplast,
+ (int)((inpp - inpsort) * 100 / inplast));
+ got_siginfo = 0;
+ }
+ if (got_sigalarm) {
+ setproctitle("%s p2 %d%%", cdevname,
+ (int)((inpp - inpsort) * 100 / inplast));
+ got_sigalarm = 0;
+ }
+ inp = *inpp;
+ if (inp->i_isize == 0)
+ continue;
+ if (inp->i_isize < MINDIRSIZE) {
+ direrror(inp->i_number, "DIRECTORY TOO SHORT");
+ inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
+ if (reply("FIX") == 1) {
+ dp = ginode(inp->i_number);
+ DIP_SET(dp, di_size, inp->i_isize);
+ inodirty();
+ }
+ } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
+ getpathname(pathbuf, inp->i_number, inp->i_number);
+ if (usedsoftdep)
+ pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
+ "DIRECTORY", pathbuf,
+ (intmax_t)inp->i_isize, DIRBLKSIZ);
+ else
+ pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
+ "DIRECTORY", pathbuf,
+ (intmax_t)inp->i_isize, DIRBLKSIZ);
+ if (preen)
+ printf(" (ADJUSTED)\n");
+ inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
+ if (preen || reply("ADJUST") == 1) {
+ dp = ginode(inp->i_number);
+ DIP_SET(dp, di_size,
+ roundup(inp->i_isize, DIRBLKSIZ));
+ inodirty();
+ }
+ }
+ dp = &dino;
+ memset(dp, 0, sizeof(struct ufs2_dinode));
+ DIP_SET(dp, di_mode, IFDIR);
+ DIP_SET(dp, di_size, inp->i_isize);
+ for (i = 0;
+ i < (inp->i_numblks<NDADDR ? inp->i_numblks : NDADDR);
+ i++)
+ DIP_SET(dp, di_db[i], inp->i_blks[i]);
+ if (inp->i_numblks > NDADDR)
+ for (i = 0; i < NIADDR; i++)
+ DIP_SET(dp, di_ib[i], inp->i_blks[NDADDR + i]);
+ curino.id_number = inp->i_number;
+ curino.id_parent = inp->i_parent;
+ (void)ckinode(dp, &curino);
+ }
+ /*
+ * Now that the parents of all directories have been found,
+ * make another pass to verify the value of `..'
+ */
+ for (inpp = inpsort; inpp < inpend; inpp++) {
+ inp = *inpp;
+ if (inp->i_parent == 0 || inp->i_isize == 0)
+ continue;
+ if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
+ INO_IS_DUNFOUND(inp->i_number))
+ inoinfo(inp->i_number)->ino_state = DFOUND;
+ if (inp->i_dotdot == inp->i_parent ||
+ inp->i_dotdot == (ino_t)-1)
+ continue;
+ if (inp->i_dotdot == 0) {
+ inp->i_dotdot = inp->i_parent;
+ fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
+ if (reply("FIX") == 0)
+ continue;
+ (void)makeentry(inp->i_number, inp->i_parent, "..");
+ inoinfo(inp->i_parent)->ino_linkcnt--;
+ continue;
+ }
+ fileerror(inp->i_parent, inp->i_number,
+ "BAD INODE NUMBER FOR '..'");
+ if (reply("FIX") == 0)
+ continue;
+ inoinfo(inp->i_dotdot)->ino_linkcnt++;
+ inoinfo(inp->i_parent)->ino_linkcnt--;
+ inp->i_dotdot = inp->i_parent;
+ (void)changeino(inp->i_number, "..", inp->i_parent);
+ }
+ /*
+ * Mark all the directories that can be found from the root.
+ */
+ propagate();
+}
+
+static int
+pass2check(struct inodesc *idesc)
+{
+ struct direct *dirp = idesc->id_dirp;
+ struct inoinfo *inp;
+ int n, entrysize, ret = 0;
+ union dinode *dp;
+ const char *errmsg;
+ struct direct proto;
+ char namebuf[MAXPATHLEN + 1];
+ char pathbuf[MAXPATHLEN + 1];
+
+ /*
+ * check for "."
+ */
+ if (idesc->id_entryno != 0)
+ goto chk1;
+ if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
+ if (dirp->d_ino != idesc->id_number) {
+ direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
+ dirp->d_ino = idesc->id_number;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ if (dirp->d_type != DT_DIR) {
+ direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
+ dirp->d_type = DT_DIR;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ goto chk1;
+ }
+ direrror(idesc->id_number, "MISSING '.'");
+ proto.d_ino = idesc->id_number;
+ proto.d_type = DT_DIR;
+ proto.d_namlen = 1;
+ (void)strcpy(proto.d_name, ".");
+ entrysize = DIRSIZ(0, &proto);
+ if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
+ pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
+ dirp->d_name);
+ } else if (dirp->d_reclen < entrysize) {
+ pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
+ } else if (dirp->d_reclen < 2 * entrysize) {
+ proto.d_reclen = dirp->d_reclen;
+ memmove(dirp, &proto, (size_t)entrysize);
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ } else {
+ n = dirp->d_reclen - entrysize;
+ proto.d_reclen = entrysize;
+ memmove(dirp, &proto, (size_t)entrysize);
+ idesc->id_entryno++;
+ inoinfo(dirp->d_ino)->ino_linkcnt--;
+ dirp = (struct direct *)((char *)(dirp) + entrysize);
+ memset(dirp, 0, (size_t)n);
+ dirp->d_reclen = n;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+chk1:
+ if (idesc->id_entryno > 1)
+ goto chk2;
+ inp = getinoinfo(idesc->id_number);
+ proto.d_ino = inp->i_parent;
+ proto.d_type = DT_DIR;
+ proto.d_namlen = 2;
+ (void)strcpy(proto.d_name, "..");
+ entrysize = DIRSIZ(0, &proto);
+ if (idesc->id_entryno == 0) {
+ n = DIRSIZ(0, dirp);
+ if (dirp->d_reclen < n + entrysize)
+ goto chk2;
+ proto.d_reclen = dirp->d_reclen - n;
+ dirp->d_reclen = n;
+ idesc->id_entryno++;
+ inoinfo(dirp->d_ino)->ino_linkcnt--;
+ dirp = (struct direct *)((char *)(dirp) + n);
+ memset(dirp, 0, (size_t)proto.d_reclen);
+ dirp->d_reclen = proto.d_reclen;
+ }
+ if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
+ inp->i_dotdot = dirp->d_ino;
+ if (dirp->d_type != DT_DIR) {
+ direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
+ dirp->d_type = DT_DIR;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ goto chk2;
+ }
+ if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
+ fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+ pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
+ dirp->d_name);
+ inp->i_dotdot = (ino_t)-1;
+ } else if (dirp->d_reclen < entrysize) {
+ fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+ pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
+ inp->i_dotdot = (ino_t)-1;
+ } else if (inp->i_parent != 0) {
+ /*
+ * We know the parent, so fix now.
+ */
+ inp->i_dotdot = inp->i_parent;
+ fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+ proto.d_reclen = dirp->d_reclen;
+ memmove(dirp, &proto, (size_t)entrysize);
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ idesc->id_entryno++;
+ if (dirp->d_ino != 0)
+ inoinfo(dirp->d_ino)->ino_linkcnt--;
+ return (ret|KEEPON);
+chk2:
+ if (dirp->d_ino == 0)
+ return (ret|KEEPON);
+ if (dirp->d_namlen <= 2 &&
+ dirp->d_name[0] == '.' &&
+ idesc->id_entryno >= 2) {
+ if (dirp->d_namlen == 1) {
+ direrror(idesc->id_number, "EXTRA '.' ENTRY");
+ dirp->d_ino = 0;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ return (KEEPON | ret);
+ }
+ if (dirp->d_name[1] == '.') {
+ direrror(idesc->id_number, "EXTRA '..' ENTRY");
+ dirp->d_ino = 0;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ return (KEEPON | ret);
+ }
+ }
+ idesc->id_entryno++;
+ n = 0;
+ if (dirp->d_ino > maxino) {
+ fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
+ n = reply("REMOVE");
+ } else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
+ (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
+ fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
+ dirp->d_ino = WINO;
+ dirp->d_type = DT_WHT;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ } else {
+again:
+ switch (inoinfo(dirp->d_ino)->ino_state) {
+ case USTATE:
+ if (idesc->id_entryno <= 2)
+ break;
+ fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
+ n = reply("REMOVE");
+ break;
+
+ case DCLEAR:
+ case FCLEAR:
+ if (idesc->id_entryno <= 2)
+ break;
+ if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
+ errmsg = "DUP/BAD";
+ else if (!preen && !usedsoftdep)
+ errmsg = "ZERO LENGTH DIRECTORY";
+ else {
+ n = 1;
+ break;
+ }
+ fileerror(idesc->id_number, dirp->d_ino, errmsg);
+ if ((n = reply("REMOVE")) == 1)
+ break;
+ dp = ginode(dirp->d_ino);
+ inoinfo(dirp->d_ino)->ino_state =
+ (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
+ inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
+ goto again;
+
+ case DSTATE:
+ case DZLINK:
+ if (inoinfo(idesc->id_number)->ino_state == DFOUND)
+ inoinfo(dirp->d_ino)->ino_state = DFOUND;
+ /* FALLTHROUGH */
+
+ case DFOUND:
+ inp = getinoinfo(dirp->d_ino);
+ if (inp->i_parent != 0 && idesc->id_entryno > 2) {
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ getpathname(namebuf, dirp->d_ino, dirp->d_ino);
+ pwarn("%s%s%s %s %s\n", pathbuf,
+ (strcmp(pathbuf, "/") == 0 ? "" : "/"),
+ dirp->d_name,
+ "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
+ namebuf);
+ if (cursnapshot != 0)
+ break;
+ if (preen) {
+ printf(" (REMOVED)\n");
+ n = 1;
+ break;
+ }
+ if ((n = reply("REMOVE")) == 1)
+ break;
+ }
+ if (idesc->id_entryno > 2)
+ inp->i_parent = idesc->id_number;
+ /* FALLTHROUGH */
+
+ case FSTATE:
+ case FZLINK:
+ if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
+ fileerror(idesc->id_number, dirp->d_ino,
+ "BAD TYPE VALUE");
+ dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ inoinfo(dirp->d_ino)->ino_linkcnt--;
+ break;
+
+ default:
+ errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
+ inoinfo(dirp->d_ino)->ino_state, dirp->d_ino);
+ }
+ }
+ if (n == 0)
+ return (ret|KEEPON);
+ dirp->d_ino = 0;
+ return (ret|KEEPON|ALTERED);
+}
+
+/*
+ * Routine to sort disk blocks.
+ */
+static int
+blksort(const void *arg1, const void *arg2)
+{
+
+ return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
+ (*(struct inoinfo * const *)arg2)->i_blks[0]);
+}
diff --git a/sbin/fsck_ffs/pass3.c b/sbin/fsck_ffs/pass3.c
new file mode 100644
index 0000000..22309cb
--- /dev/null
+++ b/sbin/fsck_ffs/pass3.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1980, 1986, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char sccsid[] = "@(#)pass3.c 8.2 (Berkeley) 4/27/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+#include <string.h>
+
+#include "fsck.h"
+
+void
+pass3(void)
+{
+ struct inoinfo *inp;
+ int loopcnt, inpindex, state;
+ ino_t orphan;
+ struct inodesc idesc;
+ char namebuf[MAXNAMLEN+1];
+
+ for (inpindex = inplast - 1; inpindex >= 0; inpindex--) {
+ if (got_siginfo) {
+ printf("%s: phase 3: dir %d of %d (%d%%)\n", cdevname,
+ (int)(inplast - inpindex - 1), (int)inplast,
+ (int)((inplast - inpindex - 1) * 100 / inplast));
+ got_siginfo = 0;
+ }
+ if (got_sigalarm) {
+ setproctitle("%s p3 %d%%", cdevname,
+ (int)((inplast - inpindex - 1) * 100 / inplast));
+ got_sigalarm = 0;
+ }
+ inp = inpsort[inpindex];
+ state = inoinfo(inp->i_number)->ino_state;
+ if (inp->i_number == ROOTINO ||
+ (inp->i_parent != 0 && !S_IS_DUNFOUND(state)))
+ continue;
+ if (state == DCLEAR)
+ continue;
+ /*
+ * If we are running with soft updates and we come
+ * across unreferenced directories, we just leave
+ * them in DSTATE which will cause them to be pitched
+ * in pass 4.
+ */
+ if ((preen || bkgrdflag) &&
+ resolved && usedsoftdep && S_IS_DUNFOUND(state)) {
+ if (inp->i_dotdot >= ROOTINO)
+ inoinfo(inp->i_dotdot)->ino_linkcnt++;
+ continue;
+ }
+ for (loopcnt = 0; ; loopcnt++) {
+ orphan = inp->i_number;
+ if (inp->i_parent == 0 ||
+ !INO_IS_DUNFOUND(inp->i_parent) ||
+ loopcnt > countdirs)
+ break;
+ inp = getinoinfo(inp->i_parent);
+ }
+ if (loopcnt <= countdirs) {
+ if (linkup(orphan, inp->i_dotdot, NULL)) {
+ inp->i_parent = inp->i_dotdot = lfdir;
+ inoinfo(lfdir)->ino_linkcnt--;
+ }
+ inoinfo(orphan)->ino_state = DFOUND;
+ propagate();
+ continue;
+ }
+ pfatal("ORPHANED DIRECTORY LOOP DETECTED I=%lu",
+ (u_long)orphan);
+ if (reply("RECONNECT") == 0)
+ continue;
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = DATA;
+ idesc.id_number = inp->i_parent;
+ idesc.id_parent = orphan;
+ idesc.id_func = findname;
+ idesc.id_name = namebuf;
+ if ((ckinode(ginode(inp->i_parent), &idesc) & FOUND) == 0)
+ pfatal("COULD NOT FIND NAME IN PARENT DIRECTORY");
+ if (linkup(orphan, inp->i_parent, namebuf)) {
+ idesc.id_func = clearentry;
+ if (ckinode(ginode(inp->i_parent), &idesc) & FOUND)
+ inoinfo(orphan)->ino_linkcnt++;
+ inp->i_parent = inp->i_dotdot = lfdir;
+ inoinfo(lfdir)->ino_linkcnt--;
+ }
+ inoinfo(orphan)->ino_state = DFOUND;
+ propagate();
+ }
+}
diff --git a/sbin/fsck_ffs/pass4.c b/sbin/fsck_ffs/pass4.c
new file mode 100644
index 0000000..8a426ea
--- /dev/null
+++ b/sbin/fsck_ffs/pass4.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 1980, 1986, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char sccsid[] = "@(#)pass4.c 8.4 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <string.h>
+
+#include "fsck.h"
+
+void
+pass4(void)
+{
+ ino_t inumber;
+ union dinode *dp;
+ struct inodesc idesc;
+ int i, n, cg;
+
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ for (cg = 0; cg < sblock.fs_ncg; cg++) {
+ if (got_siginfo) {
+ printf("%s: phase 4: cyl group %d of %d (%d%%)\n",
+ cdevname, cg, sblock.fs_ncg,
+ cg * 100 / sblock.fs_ncg);
+ got_siginfo = 0;
+ }
+ if (got_sigalarm) {
+ setproctitle("%s p4 %d%%", cdevname,
+ cg * 100 / sblock.fs_ncg);
+ got_sigalarm = 0;
+ }
+ inumber = cg * sblock.fs_ipg;
+ for (i = 0; i < inostathead[cg].il_numalloced; i++, inumber++) {
+ if (inumber < ROOTINO)
+ continue;
+ idesc.id_number = inumber;
+ switch (inoinfo(inumber)->ino_state) {
+
+ case FZLINK:
+ case DZLINK:
+ if (inoinfo(inumber)->ino_linkcnt == 0) {
+ clri(&idesc, "UNREF", 1);
+ break;
+ }
+ /* fall through */
+
+ case FSTATE:
+ case DFOUND:
+ n = inoinfo(inumber)->ino_linkcnt;
+ if (n) {
+ adjust(&idesc, (short)n);
+ break;
+ }
+ break;
+
+ case DSTATE:
+ clri(&idesc, "UNREF", 1);
+ break;
+
+ case DCLEAR:
+ dp = ginode(inumber);
+ if (DIP(dp, di_size) == 0) {
+ clri(&idesc, "ZERO LENGTH", 1);
+ break;
+ }
+ /* fall through */
+ case FCLEAR:
+ clri(&idesc, "BAD/DUP", 1);
+ break;
+
+ case USTATE:
+ break;
+
+ default:
+ errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
+ inoinfo(inumber)->ino_state, inumber);
+ }
+ }
+ }
+}
+
+int
+pass4check(struct inodesc *idesc)
+{
+ struct dups *dlp;
+ int nfrags, res = KEEPON;
+ ufs2_daddr_t blkno = idesc->id_blkno;
+
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (chkrange(blkno, 1)) {
+ res = SKIP;
+ } else if (testbmap(blkno)) {
+ for (dlp = duplist; dlp; dlp = dlp->next) {
+ if (dlp->dup != blkno)
+ continue;
+ dlp->dup = duplist->dup;
+ dlp = duplist;
+ duplist = duplist->next;
+ free((char *)dlp);
+ break;
+ }
+ if (dlp == 0) {
+ clrbmap(blkno);
+ n_blks--;
+ }
+ }
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ffs/pass5.c b/sbin/fsck_ffs/pass5.c
new file mode 100644
index 0000000..693831f
--- /dev/null
+++ b/sbin/fsck_ffs/pass5.c
@@ -0,0 +1,532 @@
+/*
+ * Copyright (c) 1980, 1986, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char sccsid[] = "@(#)pass5.c 8.9 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <string.h>
+
+#include "fsck.h"
+
+static void check_maps(u_char *, u_char *, int, int, const char *, int *, int, int);
+
+void
+pass5(void)
+{
+ int c, i, j, blk, frags, basesize, mapsize;
+ int inomapsize, blkmapsize;
+ struct fs *fs = &sblock;
+ struct cg *cg = &cgrp;
+ ufs2_daddr_t d, dbase, dmax;
+ int excessdirs, rewritecg = 0;
+ struct csum *cs;
+ struct csum_total cstotal;
+ struct inodesc idesc[3];
+ char buf[MAXBSIZE];
+ struct cg *newcg = (struct cg *)buf;
+
+ inoinfo(WINO)->ino_state = USTATE;
+ memset(newcg, 0, (size_t)fs->fs_cgsize);
+ newcg->cg_niblk = fs->fs_ipg;
+ if (cvtlevel >= 3) {
+ if (fs->fs_maxcontig < 2 && fs->fs_contigsumsize > 0) {
+ if (preen)
+ pwarn("DELETING CLUSTERING MAPS\n");
+ if (preen || reply("DELETE CLUSTERING MAPS")) {
+ fs->fs_contigsumsize = 0;
+ rewritecg = 1;
+ sbdirty();
+ }
+ }
+ if (fs->fs_maxcontig > 1) {
+ const char *doit = 0;
+
+ if (fs->fs_contigsumsize < 1) {
+ doit = "CREAT";
+ } else if (fs->fs_contigsumsize < fs->fs_maxcontig &&
+ fs->fs_contigsumsize < FS_MAXCONTIG) {
+ doit = "EXPAND";
+ }
+ if (doit) {
+ i = fs->fs_contigsumsize;
+ fs->fs_contigsumsize =
+ MIN(fs->fs_maxcontig, FS_MAXCONTIG);
+ if (CGSIZE(fs) > (u_int)fs->fs_bsize) {
+ pwarn("CANNOT %s CLUSTER MAPS\n", doit);
+ fs->fs_contigsumsize = i;
+ } else if (preen ||
+ reply("CREATE CLUSTER MAPS")) {
+ if (preen)
+ pwarn("%sING CLUSTER MAPS\n",
+ doit);
+ fs->fs_cgsize =
+ fragroundup(fs, CGSIZE(fs));
+ rewritecg = 1;
+ sbdirty();
+ }
+ }
+ }
+ }
+ basesize = &newcg->cg_space[0] - (u_char *)(&newcg->cg_firstfield);
+ if (sblock.fs_magic == FS_UFS2_MAGIC) {
+ newcg->cg_iusedoff = basesize;
+ } else {
+ /*
+ * We reserve the space for the old rotation summary
+ * tables for the benefit of old kernels, but do not
+ * maintain them in modern kernels. In time, they can
+ * go away.
+ */
+ newcg->cg_old_btotoff = basesize;
+ newcg->cg_old_boff = newcg->cg_old_btotoff +
+ fs->fs_old_cpg * sizeof(int32_t);
+ newcg->cg_iusedoff = newcg->cg_old_boff +
+ fs->fs_old_cpg * fs->fs_old_nrpos * sizeof(u_int16_t);
+ memset(&newcg->cg_space[0], 0, newcg->cg_iusedoff - basesize);
+ }
+ inomapsize = howmany(fs->fs_ipg, CHAR_BIT);
+ newcg->cg_freeoff = newcg->cg_iusedoff + inomapsize;
+ blkmapsize = howmany(fs->fs_fpg, CHAR_BIT);
+ newcg->cg_nextfreeoff = newcg->cg_freeoff + blkmapsize;
+ if (fs->fs_contigsumsize > 0) {
+ newcg->cg_clustersumoff = newcg->cg_nextfreeoff -
+ sizeof(u_int32_t);
+ newcg->cg_clustersumoff =
+ roundup(newcg->cg_clustersumoff, sizeof(u_int32_t));
+ newcg->cg_clusteroff = newcg->cg_clustersumoff +
+ (fs->fs_contigsumsize + 1) * sizeof(u_int32_t);
+ newcg->cg_nextfreeoff = newcg->cg_clusteroff +
+ howmany(fragstoblks(fs, fs->fs_fpg), CHAR_BIT);
+ }
+ newcg->cg_magic = CG_MAGIC;
+ mapsize = newcg->cg_nextfreeoff - newcg->cg_iusedoff;
+ memset(&idesc[0], 0, sizeof idesc);
+ for (i = 0; i < 3; i++)
+ idesc[i].id_type = ADDR;
+ memset(&cstotal, 0, sizeof(struct csum_total));
+ dmax = blknum(fs, fs->fs_size + fs->fs_frag - 1);
+ for (d = fs->fs_size; d < dmax; d++)
+ setbmap(d);
+ for (c = 0; c < fs->fs_ncg; c++) {
+ if (got_siginfo) {
+ printf("%s: phase 5: cyl group %d of %d (%d%%)\n",
+ cdevname, c, sblock.fs_ncg,
+ c * 100 / sblock.fs_ncg);
+ got_siginfo = 0;
+ }
+ if (got_sigalarm) {
+ setproctitle("%s p5 %d%%", cdevname,
+ c * 100 / sblock.fs_ncg);
+ got_sigalarm = 0;
+ }
+ getblk(&cgblk, cgtod(fs, c), fs->fs_cgsize);
+ if (!cg_chkmagic(cg))
+ pfatal("CG %d: BAD MAGIC NUMBER\n", c);
+ newcg->cg_time = cg->cg_time;
+ newcg->cg_old_time = cg->cg_old_time;
+ newcg->cg_cgx = c;
+ dbase = cgbase(fs, c);
+ dmax = dbase + fs->fs_fpg;
+ if (dmax > fs->fs_size)
+ dmax = fs->fs_size;
+ newcg->cg_ndblk = dmax - dbase;
+ if (fs->fs_magic == FS_UFS1_MAGIC) {
+ if (c == fs->fs_ncg - 1)
+ newcg->cg_old_ncyl = howmany(newcg->cg_ndblk,
+ fs->fs_fpg / fs->fs_old_cpg);
+ else
+ newcg->cg_old_ncyl = fs->fs_old_cpg;
+ newcg->cg_old_niblk = fs->fs_ipg;
+ newcg->cg_niblk = 0;
+ }
+ if (fs->fs_contigsumsize > 0)
+ newcg->cg_nclusterblks = newcg->cg_ndblk / fs->fs_frag;
+ newcg->cg_cs.cs_ndir = 0;
+ newcg->cg_cs.cs_nffree = 0;
+ newcg->cg_cs.cs_nbfree = 0;
+ newcg->cg_cs.cs_nifree = fs->fs_ipg;
+ if (cg->cg_rotor >= 0 && cg->cg_rotor < newcg->cg_ndblk)
+ newcg->cg_rotor = cg->cg_rotor;
+ else
+ newcg->cg_rotor = 0;
+ if (cg->cg_frotor >= 0 && cg->cg_frotor < newcg->cg_ndblk)
+ newcg->cg_frotor = cg->cg_frotor;
+ else
+ newcg->cg_frotor = 0;
+ if (cg->cg_irotor >= 0 && cg->cg_irotor < fs->fs_ipg)
+ newcg->cg_irotor = cg->cg_irotor;
+ else
+ newcg->cg_irotor = 0;
+ if (fs->fs_magic == FS_UFS1_MAGIC) {
+ newcg->cg_initediblk = 0;
+ } else {
+ if ((unsigned)cg->cg_initediblk > fs->fs_ipg)
+ newcg->cg_initediblk = fs->fs_ipg;
+ else
+ newcg->cg_initediblk = cg->cg_initediblk;
+ }
+ memset(&newcg->cg_frsum[0], 0, sizeof newcg->cg_frsum);
+ memset(cg_inosused(newcg), 0, (size_t)(mapsize));
+ j = fs->fs_ipg * c;
+ for (i = 0; i < inostathead[c].il_numalloced; j++, i++) {
+ switch (inoinfo(j)->ino_state) {
+
+ case USTATE:
+ break;
+
+ case DSTATE:
+ case DCLEAR:
+ case DFOUND:
+ case DZLINK:
+ newcg->cg_cs.cs_ndir++;
+ /* FALLTHROUGH */
+
+ case FSTATE:
+ case FCLEAR:
+ case FZLINK:
+ newcg->cg_cs.cs_nifree--;
+ setbit(cg_inosused(newcg), i);
+ break;
+
+ default:
+ if (j < (int)ROOTINO)
+ break;
+ errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
+ inoinfo(j)->ino_state, j);
+ }
+ }
+ if (c == 0)
+ for (i = 0; i < (int)ROOTINO; i++) {
+ setbit(cg_inosused(newcg), i);
+ newcg->cg_cs.cs_nifree--;
+ }
+ for (i = 0, d = dbase;
+ d < dmax;
+ d += fs->fs_frag, i += fs->fs_frag) {
+ frags = 0;
+ for (j = 0; j < fs->fs_frag; j++) {
+ if (testbmap(d + j))
+ continue;
+ setbit(cg_blksfree(newcg), i + j);
+ frags++;
+ }
+ if (frags == fs->fs_frag) {
+ newcg->cg_cs.cs_nbfree++;
+ if (fs->fs_contigsumsize > 0)
+ setbit(cg_clustersfree(newcg),
+ i / fs->fs_frag);
+ } else if (frags > 0) {
+ newcg->cg_cs.cs_nffree += frags;
+ blk = blkmap(fs, cg_blksfree(newcg), i);
+ ffs_fragacct(fs, blk, newcg->cg_frsum, 1);
+ }
+ }
+ if (fs->fs_contigsumsize > 0) {
+ int32_t *sump = cg_clustersum(newcg);
+ u_char *mapp = cg_clustersfree(newcg);
+ int map = *mapp++;
+ int bit = 1;
+ int run = 0;
+
+ for (i = 0; i < newcg->cg_nclusterblks; i++) {
+ if ((map & bit) != 0) {
+ run++;
+ } else if (run != 0) {
+ if (run > fs->fs_contigsumsize)
+ run = fs->fs_contigsumsize;
+ sump[run]++;
+ run = 0;
+ }
+ if ((i & (CHAR_BIT - 1)) != (CHAR_BIT - 1)) {
+ bit <<= 1;
+ } else {
+ map = *mapp++;
+ bit = 1;
+ }
+ }
+ if (run != 0) {
+ if (run > fs->fs_contigsumsize)
+ run = fs->fs_contigsumsize;
+ sump[run]++;
+ }
+ }
+ cstotal.cs_nffree += newcg->cg_cs.cs_nffree;
+ cstotal.cs_nbfree += newcg->cg_cs.cs_nbfree;
+ cstotal.cs_nifree += newcg->cg_cs.cs_nifree;
+ cstotal.cs_ndir += newcg->cg_cs.cs_ndir;
+ cs = &fs->fs_cs(fs, c);
+ if (cursnapshot == 0 &&
+ memcmp(&newcg->cg_cs, cs, sizeof *cs) != 0 &&
+ dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) {
+ memmove(cs, &newcg->cg_cs, sizeof *cs);
+ sbdirty();
+ }
+ if (rewritecg) {
+ memmove(cg, newcg, (size_t)fs->fs_cgsize);
+ cgdirty();
+ continue;
+ }
+ if (cursnapshot == 0 &&
+ memcmp(newcg, cg, basesize) != 0 &&
+ dofix(&idesc[2], "SUMMARY INFORMATION BAD")) {
+ memmove(cg, newcg, (size_t)basesize);
+ cgdirty();
+ }
+ if (bkgrdflag != 0 || usedsoftdep || debug) {
+ excessdirs = cg->cg_cs.cs_ndir - newcg->cg_cs.cs_ndir;
+ if (excessdirs < 0) {
+ pfatal("LOST %d DIRECTORIES\n", -excessdirs);
+ excessdirs = 0;
+ }
+ if (excessdirs > 0)
+ check_maps(cg_inosused(newcg), cg_inosused(cg),
+ inomapsize, cg->cg_cgx * fs->fs_ipg, "DIR",
+ freedirs, 0, excessdirs);
+ check_maps(cg_inosused(newcg), cg_inosused(cg),
+ inomapsize, cg->cg_cgx * fs->fs_ipg, "FILE",
+ freefiles, excessdirs, fs->fs_ipg);
+ check_maps(cg_blksfree(cg), cg_blksfree(newcg),
+ blkmapsize, cg->cg_cgx * fs->fs_fpg, "FRAG",
+ freeblks, 0, fs->fs_fpg);
+ }
+ if (cursnapshot == 0 &&
+ memcmp(cg_inosused(newcg), cg_inosused(cg), mapsize) != 0 &&
+ dofix(&idesc[1], "BLK(S) MISSING IN BIT MAPS")) {
+ memmove(cg_inosused(cg), cg_inosused(newcg),
+ (size_t)mapsize);
+ cgdirty();
+ }
+ }
+ if (cursnapshot == 0 &&
+ memcmp(&cstotal, &fs->fs_cstotal, sizeof cstotal) != 0
+ && dofix(&idesc[0], "SUMMARY BLK COUNT(S) WRONG IN SUPERBLK")) {
+ memmove(&fs->fs_cstotal, &cstotal, sizeof cstotal);
+ fs->fs_ronly = 0;
+ fs->fs_fmod = 0;
+ sbdirty();
+ }
+
+ /*
+ * When doing background fsck on a snapshot, figure out whether
+ * the superblock summary is inaccurate and correct it when
+ * necessary.
+ */
+ if (cursnapshot != 0) {
+ cmd.size = 1;
+
+ cmd.value = cstotal.cs_ndir - fs->fs_cstotal.cs_ndir;
+ if (cmd.value != 0) {
+ if (debug)
+ printf("adjndir by %+" PRIi64 "\n", cmd.value);
+ if (bkgrdsumadj == 0 || sysctl(adjndir, MIBSIZE, 0, 0,
+ &cmd, sizeof cmd) == -1)
+ rwerror("ADJUST NUMBER OF DIRECTORIES", cmd.value);
+ }
+
+ cmd.value = cstotal.cs_nbfree - fs->fs_cstotal.cs_nbfree;
+ if (cmd.value != 0) {
+ if (debug)
+ printf("adjnbfree by %+" PRIi64 "\n", cmd.value);
+ if (bkgrdsumadj == 0 || sysctl(adjnbfree, MIBSIZE, 0, 0,
+ &cmd, sizeof cmd) == -1)
+ rwerror("ADJUST NUMBER OF FREE BLOCKS", cmd.value);
+ }
+
+ cmd.value = cstotal.cs_nifree - fs->fs_cstotal.cs_nifree;
+ if (cmd.value != 0) {
+ if (debug)
+ printf("adjnifree by %+" PRIi64 "\n", cmd.value);
+ if (bkgrdsumadj == 0 || sysctl(adjnifree, MIBSIZE, 0, 0,
+ &cmd, sizeof cmd) == -1)
+ rwerror("ADJUST NUMBER OF FREE INODES", cmd.value);
+ }
+
+ cmd.value = cstotal.cs_nffree - fs->fs_cstotal.cs_nffree;
+ if (cmd.value != 0) {
+ if (debug)
+ printf("adjnffree by %+" PRIi64 "\n", cmd.value);
+ if (bkgrdsumadj == 0 || sysctl(adjnffree, MIBSIZE, 0, 0,
+ &cmd, sizeof cmd) == -1)
+ rwerror("ADJUST NUMBER OF FREE FRAGS", cmd.value);
+ }
+
+ cmd.value = cstotal.cs_numclusters - fs->fs_cstotal.cs_numclusters;
+ if (cmd.value != 0) {
+ if (debug)
+ printf("adjnumclusters by %+" PRIi64 "\n", cmd.value);
+ if (bkgrdsumadj == 0 || sysctl(adjnumclusters, MIBSIZE, 0, 0,
+ &cmd, sizeof cmd) == -1)
+ rwerror("ADJUST NUMBER OF FREE CLUSTERS", cmd.value);
+ }
+ }
+}
+
+static void
+check_maps(
+ u_char *map1, /* map of claimed allocations */
+ u_char *map2, /* map of determined allocations */
+ int mapsize, /* size of above two maps */
+ int startvalue, /* resource value for first element in map */
+ const char *name, /* name of resource found in maps */
+ int *opcode, /* sysctl opcode to free resource */
+ int skip, /* number of entries to skip before starting to free */
+ int limit) /* limit on number of entries to free */
+{
+# define BUFSIZE 16
+ char buf[BUFSIZE];
+ long i, j, k, l, m, n, size;
+ int astart, aend, ustart, uend;
+ void (*msg)(const char *fmt, ...);
+
+ if (bkgrdflag)
+ msg = pfatal;
+ else
+ msg = pwarn;
+ astart = ustart = aend = uend = -1;
+ for (i = 0; i < mapsize; i++) {
+ j = *map1++;
+ k = *map2++;
+ if (j == k)
+ continue;
+ for (m = 0, l = 1; m < CHAR_BIT; m++, l <<= 1) {
+ if ((j & l) == (k & l))
+ continue;
+ n = startvalue + i * CHAR_BIT + m;
+ if ((j & l) != 0) {
+ if (astart == -1) {
+ astart = aend = n;
+ continue;
+ }
+ if (aend + 1 == n) {
+ aend = n;
+ continue;
+ }
+ if (astart == aend)
+ (*msg)("ALLOCATED %s %d MARKED FREE\n",
+ name, astart);
+ else
+ (*msg)("%s %sS %d-%d MARKED FREE\n",
+ "ALLOCATED", name, astart, aend);
+ astart = aend = n;
+ } else {
+ if (ustart == -1) {
+ ustart = uend = n;
+ continue;
+ }
+ if (uend + 1 == n) {
+ uend = n;
+ continue;
+ }
+ size = uend - ustart + 1;
+ if (size <= skip) {
+ skip -= size;
+ ustart = uend = n;
+ continue;
+ }
+ if (skip > 0) {
+ ustart += skip;
+ size -= skip;
+ skip = 0;
+ }
+ if (size > limit)
+ size = limit;
+ if (debug && size == 1)
+ pwarn("%s %s %d MARKED USED\n",
+ "UNALLOCATED", name, ustart);
+ else if (debug)
+ pwarn("%s %sS %d-%ld MARKED USED\n",
+ "UNALLOCATED", name, ustart,
+ ustart + size - 1);
+ if (bkgrdflag != 0) {
+ cmd.value = ustart;
+ cmd.size = size;
+ if (sysctl(opcode, MIBSIZE, 0, 0,
+ &cmd, sizeof cmd) == -1) {
+ snprintf(buf, BUFSIZE,
+ "FREE %s", name);
+ rwerror(buf, cmd.value);
+ }
+ }
+ limit -= size;
+ if (limit <= 0)
+ return;
+ ustart = uend = n;
+ }
+ }
+ }
+ if (astart != -1) {
+ if (astart == aend)
+ (*msg)("ALLOCATED %s %d MARKED FREE\n", name, astart);
+ else
+ (*msg)("ALLOCATED %sS %d-%d MARKED FREE\n",
+ name, astart, aend);
+ }
+ if (ustart != -1) {
+ size = uend - ustart + 1;
+ if (size <= skip)
+ return;
+ if (skip > 0) {
+ ustart += skip;
+ size -= skip;
+ }
+ if (size > limit)
+ size = limit;
+ if (debug) {
+ if (size == 1)
+ pwarn("UNALLOCATED %s %d MARKED USED\n",
+ name, ustart);
+ else
+ pwarn("UNALLOCATED %sS %d-%ld MARKED USED\n",
+ name, ustart, ustart + size - 1);
+ }
+ if (bkgrdflag != 0) {
+ cmd.value = ustart;
+ cmd.size = size;
+ if (sysctl(opcode, MIBSIZE, 0, 0, &cmd,
+ sizeof cmd) == -1) {
+ snprintf(buf, BUFSIZE, "FREE %s", name);
+ rwerror(buf, cmd.value);
+ }
+ }
+ }
+}
diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c
new file mode 100644
index 0000000..f6b736d
--- /dev/null
+++ b/sbin/fsck_ffs/setup.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright (c) 1980, 1986, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char sccsid[] = "@(#)setup.c 8.10 (Berkeley) 5/9/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#define FSTYPENAMES
+#include <sys/disklabel.h>
+#include <sys/file.h>
+#include <sys/sysctl.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "fsck.h"
+
+struct bufarea asblk;
+#define altsblock (*asblk.b_un.b_fs)
+#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
+
+static void badsb(int listerr, const char *s);
+static int calcsb(char *dev, int devfd, struct fs *fs);
+static struct disklabel *getdisklabel(char *s, int fd);
+
+/*
+ * Read in a superblock finding an alternate if necessary.
+ * Return 1 if successful, 0 if unsuccessful, -1 if file system
+ * is already clean (preen mode only).
+ */
+int
+setup(char *dev)
+{
+ long cg, asked, i, j;
+ long bmapsize;
+ struct stat statb;
+ struct fs proto;
+ size_t size;
+
+ havesb = 0;
+ fswritefd = -1;
+ cursnapshot = 0;
+ if (stat(dev, &statb) < 0) {
+ printf("Can't stat %s: %s\n", dev, strerror(errno));
+ if (bkgrdflag) {
+ unlink(snapname);
+ bkgrdflag = 0;
+ }
+ return (0);
+ }
+ if ((statb.st_mode & S_IFMT) != S_IFCHR &&
+ (statb.st_mode & S_IFMT) != S_IFBLK) {
+ if (bkgrdflag != 0 && (statb.st_flags & SF_SNAPSHOT) == 0) {
+ unlink(snapname);
+ printf("background fsck lacks a snapshot\n");
+ exit(EEXIT);
+ }
+ if ((statb.st_flags & SF_SNAPSHOT) != 0 && cvtlevel == 0) {
+ cursnapshot = statb.st_ino;
+ } else {
+ if (cvtlevel == 0 ||
+ (statb.st_flags & SF_SNAPSHOT) == 0) {
+ if (preen && bkgrdflag) {
+ unlink(snapname);
+ bkgrdflag = 0;
+ }
+ pfatal("%s is not a disk device", dev);
+ if (reply("CONTINUE") == 0) {
+ if (bkgrdflag) {
+ unlink(snapname);
+ bkgrdflag = 0;
+ }
+ return (0);
+ }
+ } else {
+ if (bkgrdflag) {
+ unlink(snapname);
+ bkgrdflag = 0;
+ }
+ pfatal("cannot convert a snapshot");
+ exit(EEXIT);
+ }
+ }
+ }
+ if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
+ if (bkgrdflag) {
+ unlink(snapname);
+ bkgrdflag = 0;
+ }
+ printf("Can't open %s: %s\n", dev, strerror(errno));
+ return (0);
+ }
+ if (bkgrdflag) {
+ unlink(snapname);
+ size = MIBSIZE;
+ if (sysctlnametomib("vfs.ffs.adjrefcnt", adjrefcnt, &size) < 0||
+ sysctlnametomib("vfs.ffs.adjblkcnt", adjblkcnt, &size) < 0||
+ sysctlnametomib("vfs.ffs.freefiles", freefiles, &size) < 0||
+ sysctlnametomib("vfs.ffs.freedirs", freedirs, &size) < 0 ||
+ sysctlnametomib("vfs.ffs.freeblks", freeblks, &size) < 0) {
+ pfatal("kernel lacks background fsck support\n");
+ exit(EEXIT);
+ }
+ /*
+ * When kernel is lack of runtime bgfsck superblock summary
+ * adjustment functionality, it does not mean we can not
+ * continue, as old kernels will recompute the summary at
+ * mount time. However, it will be an unexpected softupdates
+ * inconsistency if it turns out that the summary is still
+ * incorrect. Set a flag so subsequent operation can know
+ * this.
+ */
+ bkgrdsumadj = 1;
+ if (sysctlnametomib("vfs.ffs.adjndir", adjndir, &size) < 0 ||
+ sysctlnametomib("vfs.ffs.adjnbfree", adjnbfree, &size) < 0 ||
+ sysctlnametomib("vfs.ffs.adjnifree", adjnifree, &size) < 0 ||
+ sysctlnametomib("vfs.ffs.adjnffree", adjnffree, &size) < 0 ||
+ sysctlnametomib("vfs.ffs.adjnumclusters", adjnumclusters, &size) < 0) {
+ bkgrdsumadj = 0;
+ pwarn("kernel lacks runtime superblock summary adjustment support");
+ }
+ cmd.version = FFS_CMD_VERSION;
+ cmd.handle = fsreadfd;
+ fswritefd = -1;
+ }
+ if (preen == 0)
+ printf("** %s", dev);
+ if (bkgrdflag == 0 &&
+ (nflag || (fswritefd = open(dev, O_WRONLY)) < 0)) {
+ fswritefd = -1;
+ if (preen)
+ pfatal("NO WRITE ACCESS");
+ printf(" (NO WRITE)");
+ }
+ if (preen == 0)
+ printf("\n");
+ /*
+ * Read in the superblock, looking for alternates if necessary
+ */
+ if (readsb(1) == 0) {
+ skipclean = 0;
+ if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0)
+ return(0);
+ if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0)
+ return (0);
+ for (cg = 0; cg < proto.fs_ncg; cg++) {
+ bflag = fsbtodb(&proto, cgsblock(&proto, cg));
+ if (readsb(0) != 0)
+ break;
+ }
+ if (cg >= proto.fs_ncg) {
+ printf("%s %s\n%s %s\n%s %s\n",
+ "SEARCH FOR ALTERNATE SUPER-BLOCK",
+ "FAILED. YOU MUST USE THE",
+ "-b OPTION TO FSCK TO SPECIFY THE",
+ "LOCATION OF AN ALTERNATE",
+ "SUPER-BLOCK TO SUPPLY NEEDED",
+ "INFORMATION; SEE fsck(8).");
+ bflag = 0;
+ return(0);
+ }
+ pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag);
+ bflag = 0;
+ }
+ if (skipclean && preen && sblock.fs_clean) {
+ pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
+ return (-1);
+ }
+ maxfsblock = sblock.fs_size;
+ maxino = sblock.fs_ncg * sblock.fs_ipg;
+ /*
+ * Check and potentially fix certain fields in the super block.
+ */
+ if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) {
+ pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK");
+ if (reply("SET TO DEFAULT") == 1) {
+ sblock.fs_optim = FS_OPTTIME;
+ sbdirty();
+ }
+ }
+ if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) {
+ pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK",
+ sblock.fs_minfree);
+ if (reply("SET TO DEFAULT") == 1) {
+ sblock.fs_minfree = 10;
+ sbdirty();
+ }
+ }
+ if (sblock.fs_magic == FS_UFS1_MAGIC &&
+ sblock.fs_old_inodefmt < FS_44INODEFMT) {
+ pwarn("Format of file system is too old.\n");
+ pwarn("Must update to modern format using a version of fsck\n");
+ pfatal("from before 2002 with the command ``fsck -c 2''\n");
+ exit(EEXIT);
+ }
+ if (asblk.b_dirty && !bflag) {
+ memmove(&altsblock, &sblock, (size_t)sblock.fs_sbsize);
+ flush(fswritefd, &asblk);
+ }
+ /*
+ * read in the summary info.
+ */
+ asked = 0;
+ sblock.fs_csp = calloc(1, sblock.fs_cssize);
+ if (sblock.fs_csp == NULL) {
+ printf("cannot alloc %u bytes for cg summary info\n",
+ (unsigned)sblock.fs_cssize);
+ goto badsb;
+ }
+ for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
+ size = sblock.fs_cssize - i < sblock.fs_bsize ?
+ sblock.fs_cssize - i : sblock.fs_bsize;
+ if (bread(fsreadfd, (char *)sblock.fs_csp + i,
+ fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
+ size) != 0 && !asked) {
+ pfatal("BAD SUMMARY INFORMATION");
+ if (reply("CONTINUE") == 0) {
+ ckfini(0);
+ exit(EEXIT);
+ }
+ asked++;
+ }
+ }
+ /*
+ * allocate and initialize the necessary maps
+ */
+ bmapsize = roundup(howmany(maxfsblock, CHAR_BIT), sizeof(short));
+ blockmap = calloc((unsigned)bmapsize, sizeof (char));
+ if (blockmap == NULL) {
+ printf("cannot alloc %u bytes for blockmap\n",
+ (unsigned)bmapsize);
+ goto badsb;
+ }
+ inostathead = calloc((unsigned)(sblock.fs_ncg),
+ sizeof(struct inostatlist));
+ if (inostathead == NULL) {
+ printf("cannot alloc %u bytes for inostathead\n",
+ (unsigned)(sizeof(struct inostatlist) * (sblock.fs_ncg)));
+ goto badsb;
+ }
+ numdirs = MAX(sblock.fs_cstotal.cs_ndir, 128);
+ dirhash = numdirs;
+ inplast = 0;
+ listmax = numdirs + 10;
+ inpsort = (struct inoinfo **)calloc((unsigned)listmax,
+ sizeof(struct inoinfo *));
+ inphead = (struct inoinfo **)calloc((unsigned)numdirs,
+ sizeof(struct inoinfo *));
+ if (inpsort == NULL || inphead == NULL) {
+ printf("cannot alloc %ju bytes for inphead\n",
+ (uintmax_t)numdirs * sizeof(struct inoinfo *));
+ goto badsb;
+ }
+ bufinit();
+ if (sblock.fs_flags & FS_DOSOFTDEP)
+ usedsoftdep = 1;
+ else
+ usedsoftdep = 0;
+ return (1);
+
+badsb:
+ ckfini(0);
+ return (0);
+}
+
+/*
+ * Possible superblock locations ordered from most to least likely.
+ */
+static int sblock_try[] = SBLOCKSEARCH;
+
+#define BAD_MAGIC_MSG \
+"The previous newfs operation on this volume did not complete.\n" \
+"You must complete newfs before mounting this volume.\n"
+
+/*
+ * Read in the super block and its summary info.
+ */
+int
+readsb(int listerr)
+{
+ ufs2_daddr_t super;
+ int i;
+
+ if (bflag) {
+ super = bflag;
+ if ((bread(fsreadfd, (char *)&sblock, super, (long)SBLOCKSIZE)))
+ return (0);
+ if (sblock.fs_magic == FS_BAD_MAGIC) {
+ fprintf(stderr, BAD_MAGIC_MSG);
+ exit(11);
+ }
+ if (sblock.fs_magic != FS_UFS1_MAGIC &&
+ sblock.fs_magic != FS_UFS2_MAGIC) {
+ fprintf(stderr, "%d is not a file system superblock\n",
+ bflag);
+ return (0);
+ }
+ } else {
+ for (i = 0; sblock_try[i] != -1; i++) {
+ super = sblock_try[i] / dev_bsize;
+ if ((bread(fsreadfd, (char *)&sblock, super,
+ (long)SBLOCKSIZE)))
+ return (0);
+ if (sblock.fs_magic == FS_BAD_MAGIC) {
+ fprintf(stderr, BAD_MAGIC_MSG);
+ exit(11);
+ }
+ if ((sblock.fs_magic == FS_UFS1_MAGIC ||
+ (sblock.fs_magic == FS_UFS2_MAGIC &&
+ sblock.fs_sblockloc == sblock_try[i])) &&
+ sblock.fs_ncg >= 1 &&
+ sblock.fs_bsize >= MINBSIZE &&
+ sblock.fs_bsize >= sizeof(struct fs))
+ break;
+ }
+ if (sblock_try[i] == -1) {
+ fprintf(stderr, "Cannot find file system superblock\n");
+ return (0);
+ }
+ }
+ /*
+ * Compute block size that the file system is based on,
+ * according to fsbtodb, and adjust superblock block number
+ * so we can tell if this is an alternate later.
+ */
+ super *= dev_bsize;
+ dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
+ sblk.b_bno = super / dev_bsize;
+ sblk.b_size = SBLOCKSIZE;
+ if (bflag)
+ goto out;
+ /*
+ * Compare all fields that should not differ in alternate super block.
+ * When an alternate super-block is specified this check is skipped.
+ */
+ getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1), sblock.fs_sbsize);
+ if (asblk.b_errs)
+ return (0);
+ if (altsblock.fs_sblkno != sblock.fs_sblkno ||
+ altsblock.fs_cblkno != sblock.fs_cblkno ||
+ altsblock.fs_iblkno != sblock.fs_iblkno ||
+ altsblock.fs_dblkno != sblock.fs_dblkno ||
+ altsblock.fs_ncg != sblock.fs_ncg ||
+ altsblock.fs_bsize != sblock.fs_bsize ||
+ altsblock.fs_fsize != sblock.fs_fsize ||
+ altsblock.fs_frag != sblock.fs_frag ||
+ altsblock.fs_bmask != sblock.fs_bmask ||
+ altsblock.fs_fmask != sblock.fs_fmask ||
+ altsblock.fs_bshift != sblock.fs_bshift ||
+ altsblock.fs_fshift != sblock.fs_fshift ||
+ altsblock.fs_fragshift != sblock.fs_fragshift ||
+ altsblock.fs_fsbtodb != sblock.fs_fsbtodb ||
+ altsblock.fs_sbsize != sblock.fs_sbsize ||
+ altsblock.fs_nindir != sblock.fs_nindir ||
+ altsblock.fs_inopb != sblock.fs_inopb ||
+ altsblock.fs_cssize != sblock.fs_cssize ||
+ altsblock.fs_ipg != sblock.fs_ipg ||
+ altsblock.fs_fpg != sblock.fs_fpg ||
+ altsblock.fs_magic != sblock.fs_magic) {
+ badsb(listerr,
+ "VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN FIRST ALTERNATE");
+ return (0);
+ }
+out:
+ /*
+ * If not yet done, update UFS1 superblock with new wider fields.
+ */
+ if (sblock.fs_magic == FS_UFS1_MAGIC &&
+ sblock.fs_maxbsize != sblock.fs_bsize) {
+ sblock.fs_maxbsize = sblock.fs_bsize;
+ sblock.fs_time = sblock.fs_old_time;
+ sblock.fs_size = sblock.fs_old_size;
+ sblock.fs_dsize = sblock.fs_old_dsize;
+ sblock.fs_csaddr = sblock.fs_old_csaddr;
+ sblock.fs_cstotal.cs_ndir = sblock.fs_old_cstotal.cs_ndir;
+ sblock.fs_cstotal.cs_nbfree = sblock.fs_old_cstotal.cs_nbfree;
+ sblock.fs_cstotal.cs_nifree = sblock.fs_old_cstotal.cs_nifree;
+ sblock.fs_cstotal.cs_nffree = sblock.fs_old_cstotal.cs_nffree;
+ }
+ havesb = 1;
+ return (1);
+}
+
+static void
+badsb(int listerr, const char *s)
+{
+
+ if (!listerr)
+ return;
+ if (preen)
+ printf("%s: ", cdevname);
+ pfatal("BAD SUPER BLOCK: %s\n", s);
+}
+
+void
+sblock_init(void)
+{
+ struct disklabel *lp;
+
+ fswritefd = -1;
+ fsmodified = 0;
+ lfdir = 0;
+ initbarea(&sblk);
+ initbarea(&asblk);
+ sblk.b_un.b_buf = malloc(SBLOCKSIZE);
+ asblk.b_un.b_buf = malloc(SBLOCKSIZE);
+ if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL)
+ errx(EEXIT, "cannot allocate space for superblock");
+ if ((lp = getdisklabel(NULL, fsreadfd)))
+ dev_bsize = secsize = lp->d_secsize;
+ else
+ dev_bsize = secsize = DEV_BSIZE;
+}
+
+/*
+ * Calculate a prototype superblock based on information in the disk label.
+ * When done the cgsblock macro can be calculated and the fs_ncg field
+ * can be used. Do NOT attempt to use other macros without verifying that
+ * their needed information is available!
+ */
+static int
+calcsb(char *dev, int devfd, struct fs *fs)
+{
+ struct disklabel *lp;
+ struct partition *pp;
+ char *cp;
+ int i, nspf;
+
+ cp = strchr(dev, '\0') - 1;
+ if (cp == (char *)-1 || ((*cp < 'a' || *cp > 'h') && !isdigit(*cp))) {
+ pfatal("%s: CANNOT FIGURE OUT FILE SYSTEM PARTITION\n", dev);
+ return (0);
+ }
+ lp = getdisklabel(dev, devfd);
+ if (isdigit(*cp))
+ pp = &lp->d_partitions[0];
+ else
+ pp = &lp->d_partitions[*cp - 'a'];
+ if (pp->p_fstype != FS_BSDFFS) {
+ pfatal("%s: NOT LABELED AS A BSD FILE SYSTEM (%s)\n",
+ dev, pp->p_fstype < FSMAXTYPES ?
+ fstypenames[pp->p_fstype] : "unknown");
+ return (0);
+ }
+ if (pp->p_fsize == 0 || pp->p_frag == 0 ||
+ pp->p_cpg == 0 || pp->p_size == 0) {
+ pfatal("%s: %s: type %s fsize %d, frag %d, cpg %d, size %d\n",
+ dev, "INCOMPLETE LABEL", fstypenames[pp->p_fstype],
+ pp->p_fsize, pp->p_frag, pp->p_cpg, pp->p_size);
+ return (0);
+ }
+ memset(fs, 0, sizeof(struct fs));
+ fs->fs_fsize = pp->p_fsize;
+ fs->fs_frag = pp->p_frag;
+ fs->fs_size = pp->p_size;
+ fs->fs_sblkno = roundup(
+ howmany(lp->d_bbsize + lp->d_sbsize, fs->fs_fsize),
+ fs->fs_frag);
+ nspf = fs->fs_fsize / lp->d_secsize;
+ for (fs->fs_fsbtodb = 0, i = nspf; i > 1; i >>= 1)
+ fs->fs_fsbtodb++;
+ dev_bsize = lp->d_secsize;
+ if (fs->fs_magic == FS_UFS2_MAGIC) {
+ fs->fs_fpg = pp->p_cpg;
+ fs->fs_ncg = howmany(fs->fs_size, fs->fs_fpg);
+ } else /* if (fs->fs_magic == FS_UFS1_MAGIC) */ {
+ fs->fs_old_cpg = pp->p_cpg;
+ fs->fs_old_cgmask = 0xffffffff;
+ for (i = lp->d_ntracks; i > 1; i >>= 1)
+ fs->fs_old_cgmask <<= 1;
+ if (!POWEROF2(lp->d_ntracks))
+ fs->fs_old_cgmask <<= 1;
+ fs->fs_old_cgoffset = roundup(howmany(lp->d_nsectors, nspf),
+ fs->fs_frag);
+ fs->fs_fpg = (fs->fs_old_cpg * lp->d_secpercyl) / nspf;
+ fs->fs_ncg = howmany(fs->fs_size / lp->d_secpercyl,
+ fs->fs_old_cpg);
+ }
+ return (1);
+}
+
+static struct disklabel *
+getdisklabel(char *s, int fd)
+{
+ static struct disklabel lab;
+
+ if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) {
+ if (s == NULL)
+ return ((struct disklabel *)NULL);
+ pwarn("ioctl (GCINFO): %s\n", strerror(errno));
+ errx(EEXIT, "%s: can't read disk label", s);
+ }
+ return (&lab);
+}
diff --git a/sbin/fsck_ffs/utilities.c b/sbin/fsck_ffs/utilities.c
new file mode 100644
index 0000000..8e66820
--- /dev/null
+++ b/sbin/fsck_ffs/utilities.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 1980, 1986, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char sccsid[] = "@(#)utilities.c 8.6 (Berkeley) 5/19/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <fstab.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "fsck.h"
+
+
+char *
+blockcheck(char *origname)
+{
+ struct stat stblock;
+ char *newname, *cp;
+ struct fstab *fsinfo;
+ int retried = 0, len;
+ static char device[MAXPATHLEN];
+
+ newname = origname;
+ if (stat(newname, &stblock) < 0) {
+ cp = strrchr(newname, '/');
+ if (cp == 0) {
+ (void)snprintf(device, sizeof(device), "%s%s",
+ _PATH_DEV, newname);
+ newname = device;
+ }
+ }
+retry:
+ if (stat(newname, &stblock) < 0) {
+ printf("Can't stat %s: %s\n", newname, strerror(errno));
+ return (origname);
+ }
+ switch(stblock.st_mode & S_IFMT) {
+ case S_IFCHR:
+ case S_IFBLK:
+ return(newname);
+ case S_IFDIR:
+ if (retried)
+ break;
+
+ len = strlen(origname) - 1;
+ if (len > 0 && origname[len] == '/')
+ /* remove trailing slash */
+ origname[len] = '\0';
+ if ((fsinfo = getfsfile(origname)) == NULL) {
+ printf(
+ "Can't resolve %s to character special device.\n",
+ origname);
+ return (origname);
+ }
+ newname = fsinfo->fs_spec;
+ retried++;
+ goto retry;
+ }
+ /*
+ * Not a block or character device, just return name and
+ * let the user decide whether to use it.
+ */
+ return (origname);
+}
+
+void
+infohandler(int sig __unused)
+{
+ got_siginfo = 1;
+}
+
+void
+alarmhandler(int sig __unused)
+{
+ got_sigalarm = 1;
+}
diff --git a/sbin/fsck_msdosfs/Makefile b/sbin/fsck_msdosfs/Makefile
new file mode 100644
index 0000000..fd539b9
--- /dev/null
+++ b/sbin/fsck_msdosfs/Makefile
@@ -0,0 +1,14 @@
+# $NetBSD: Makefile,v 1.6 1997/05/08 21:11:11 gwr Exp $
+# $FreeBSD$
+
+FSCK= ${.CURDIR}/../fsck
+.PATH: ${FSCK}
+
+PROG= fsck_msdosfs
+MAN= fsck_msdosfs.8
+SRCS= main.c check.c boot.c fat.c dir.c fsutil.c
+
+CFLAGS+= -I${FSCK}
+WARNS?= 0
+
+.include <bsd.prog.mk>
diff --git a/sbin/fsck_msdosfs/boot.c b/sbin/fsck_msdosfs/boot.c
new file mode 100644
index 0000000..32ed863
--- /dev/null
+++ b/sbin/fsck_msdosfs/boot.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 1995, 1997 Wolfgang Solfrank
+ * Copyright (c) 1995 Martin Husemann
+ *
+ * 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 Martin Husemann
+ * and Wolfgang Solfrank.
+ * 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 THE AUTHORS ``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 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>
+#ifndef lint
+__RCSID("$NetBSD: boot.c,v 1.9 2003/07/24 19:25:46 ws Exp $");
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "ext.h"
+#include "fsutil.h"
+
+int
+readboot(dosfs, boot)
+ int dosfs;
+ struct bootblock *boot;
+{
+ u_char block[DOSBOOTBLOCKSIZE];
+ u_char fsinfo[2 * DOSBOOTBLOCKSIZE];
+ u_char backup[DOSBOOTBLOCKSIZE];
+ int ret = FSOK;
+
+ if (read(dosfs, block, sizeof block) < sizeof block) {
+ perror("could not read boot block");
+ return FSFATAL;
+ }
+
+ if (block[510] != 0x55 || block[511] != 0xaa) {
+ pfatal("Invalid signature in boot block: %02x%02x", block[511], block[510]);
+ return FSFATAL;
+ }
+
+ memset(boot, 0, sizeof *boot);
+ boot->ValidFat = -1;
+
+ /* decode bios parameter block */
+ boot->BytesPerSec = block[11] + (block[12] << 8);
+ boot->SecPerClust = block[13];
+ boot->ResSectors = block[14] + (block[15] << 8);
+ boot->FATs = block[16];
+ boot->RootDirEnts = block[17] + (block[18] << 8);
+ boot->Sectors = block[19] + (block[20] << 8);
+ boot->Media = block[21];
+ boot->FATsmall = block[22] + (block[23] << 8);
+ boot->SecPerTrack = block[24] + (block[25] << 8);
+ boot->Heads = block[26] + (block[27] << 8);
+ boot->HiddenSecs = block[28] + (block[29] << 8) + (block[30] << 16) + (block[31] << 24);
+ boot->HugeSectors = block[32] + (block[33] << 8) + (block[34] << 16) + (block[35] << 24);
+
+ boot->FATsecs = boot->FATsmall;
+
+ if (!boot->RootDirEnts)
+ boot->flags |= FAT32;
+ if (boot->flags & FAT32) {
+ boot->FATsecs = block[36] + (block[37] << 8)
+ + (block[38] << 16) + (block[39] << 24);
+ if (block[40] & 0x80)
+ boot->ValidFat = block[40] & 0x0f;
+
+ /* check version number: */
+ if (block[42] || block[43]) {
+ /* Correct? XXX */
+ pfatal("Unknown file system version: %x.%x",
+ block[43], block[42]);
+ return FSFATAL;
+ }
+ boot->RootCl = block[44] + (block[45] << 8)
+ + (block[46] << 16) + (block[47] << 24);
+ boot->FSInfo = block[48] + (block[49] << 8);
+ boot->Backup = block[50] + (block[51] << 8);
+
+ if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
+ != boot->FSInfo * boot->BytesPerSec
+ || read(dosfs, fsinfo, sizeof fsinfo)
+ != sizeof fsinfo) {
+ perror("could not read fsinfo block");
+ return FSFATAL;
+ }
+ if (memcmp(fsinfo, "RRaA", 4)
+ || memcmp(fsinfo + 0x1e4, "rrAa", 4)
+ || fsinfo[0x1fc]
+ || fsinfo[0x1fd]
+ || fsinfo[0x1fe] != 0x55
+ || fsinfo[0x1ff] != 0xaa
+ || fsinfo[0x3fc]
+ || fsinfo[0x3fd]
+ || fsinfo[0x3fe] != 0x55
+ || fsinfo[0x3ff] != 0xaa) {
+ pwarn("Invalid signature in fsinfo block");
+ if (ask(0, "fix")) {
+ memcpy(fsinfo, "RRaA", 4);
+ memcpy(fsinfo + 0x1e4, "rrAa", 4);
+ fsinfo[0x1fc] = fsinfo[0x1fd] = 0;
+ fsinfo[0x1fe] = 0x55;
+ fsinfo[0x1ff] = 0xaa;
+ fsinfo[0x3fc] = fsinfo[0x3fd] = 0;
+ fsinfo[0x3fe] = 0x55;
+ fsinfo[0x3ff] = 0xaa;
+ if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
+ != boot->FSInfo * boot->BytesPerSec
+ || write(dosfs, fsinfo, sizeof fsinfo)
+ != sizeof fsinfo) {
+ perror("Unable to write FSInfo");
+ return FSFATAL;
+ }
+ ret = FSBOOTMOD;
+ } else
+ boot->FSInfo = 0;
+ }
+ if (boot->FSInfo) {
+ boot->FSFree = fsinfo[0x1e8] + (fsinfo[0x1e9] << 8)
+ + (fsinfo[0x1ea] << 16)
+ + (fsinfo[0x1eb] << 24);
+ boot->FSNext = fsinfo[0x1ec] + (fsinfo[0x1ed] << 8)
+ + (fsinfo[0x1ee] << 16)
+ + (fsinfo[0x1ef] << 24);
+ }
+
+ if (lseek(dosfs, boot->Backup * boot->BytesPerSec, SEEK_SET)
+ != boot->Backup * boot->BytesPerSec
+ || read(dosfs, backup, sizeof backup) != sizeof backup) {
+ perror("could not read backup bootblock");
+ return FSFATAL;
+ }
+ backup[65] = block[65]; /* XXX */
+ if (memcmp(block + 11, backup + 11, 79)) {
+ /* Correct? XXX */
+ pfatal("backup doesn't compare to primary bootblock");
+ if (alwaysno)
+ pfatal("\n");
+ else
+ return FSFATAL;
+ }
+ /* Check backup FSInfo? XXX */
+ }
+
+ boot->ClusterOffset = (boot->RootDirEnts * 32 + boot->BytesPerSec - 1)
+ / boot->BytesPerSec
+ + boot->ResSectors
+ + boot->FATs * boot->FATsecs
+ - CLUST_FIRST * boot->SecPerClust;
+
+ if (boot->BytesPerSec % DOSBOOTBLOCKSIZE != 0) {
+ pfatal("Invalid sector size: %u", boot->BytesPerSec);
+ return FSFATAL;
+ }
+ if (boot->SecPerClust == 0) {
+ pfatal("Invalid cluster size: %u", boot->SecPerClust);
+ return FSFATAL;
+ }
+ if (boot->Sectors) {
+ boot->HugeSectors = 0;
+ boot->NumSectors = boot->Sectors;
+ } else
+ boot->NumSectors = boot->HugeSectors;
+ boot->NumClusters = (boot->NumSectors - boot->ClusterOffset) / boot->SecPerClust;
+
+ if (boot->flags&FAT32)
+ boot->ClustMask = CLUST32_MASK;
+ else if (boot->NumClusters < (CLUST_RSRVD&CLUST12_MASK))
+ boot->ClustMask = CLUST12_MASK;
+ else if (boot->NumClusters < (CLUST_RSRVD&CLUST16_MASK))
+ boot->ClustMask = CLUST16_MASK;
+ else {
+ pfatal("Filesystem too big (%u clusters) for non-FAT32 partition",
+ boot->NumClusters);
+ return FSFATAL;
+ }
+
+ switch (boot->ClustMask) {
+ case CLUST32_MASK:
+ boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec) / 4;
+ break;
+ case CLUST16_MASK:
+ boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec) / 2;
+ break;
+ default:
+ boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec * 2) / 3;
+ break;
+ }
+
+ if (boot->NumFatEntries < boot->NumClusters) {
+ pfatal("FAT size too small, %u entries won't fit into %u sectors\n",
+ boot->NumClusters, boot->FATsecs);
+ return FSFATAL;
+ }
+ boot->ClusterSize = boot->BytesPerSec * boot->SecPerClust;
+
+ boot->NumFiles = 1;
+ boot->NumFree = 0;
+
+ return ret;
+}
+
+int
+writefsinfo(dosfs, boot)
+ int dosfs;
+ struct bootblock *boot;
+{
+ u_char fsinfo[2 * DOSBOOTBLOCKSIZE];
+
+ if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
+ != boot->FSInfo * boot->BytesPerSec
+ || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) {
+ perror("could not read fsinfo block");
+ return FSFATAL;
+ }
+ fsinfo[0x1e8] = (u_char)boot->FSFree;
+ fsinfo[0x1e9] = (u_char)(boot->FSFree >> 8);
+ fsinfo[0x1ea] = (u_char)(boot->FSFree >> 16);
+ fsinfo[0x1eb] = (u_char)(boot->FSFree >> 24);
+ fsinfo[0x1ec] = (u_char)boot->FSNext;
+ fsinfo[0x1ed] = (u_char)(boot->FSNext >> 8);
+ fsinfo[0x1ee] = (u_char)(boot->FSNext >> 16);
+ fsinfo[0x1ef] = (u_char)(boot->FSNext >> 24);
+ if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
+ != boot->FSInfo * boot->BytesPerSec
+ || write(dosfs, fsinfo, sizeof fsinfo)
+ != sizeof fsinfo) {
+ perror("Unable to write FSInfo");
+ return FSFATAL;
+ }
+ /*
+ * Technically, we should return FSBOOTMOD here.
+ *
+ * However, since Win95 OSR2 (the first M$ OS that has
+ * support for FAT32) doesn't maintain the FSINFO block
+ * correctly, it has to be fixed pretty often.
+ *
+ * Therefor, we handle the FSINFO block only informally,
+ * fixing it if necessary, but otherwise ignoring the
+ * fact that it was incorrect.
+ */
+ return 0;
+}
diff --git a/sbin/fsck_msdosfs/check.c b/sbin/fsck_msdosfs/check.c
new file mode 100644
index 0000000..48ae5ca
--- /dev/null
+++ b/sbin/fsck_msdosfs/check.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
+ * Copyright (c) 1995 Martin Husemann
+ *
+ * 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 Martin Husemann
+ * and Wolfgang Solfrank.
+ * 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 THE AUTHORS ``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 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>
+#ifndef lint
+__RCSID("$NetBSD: check.c,v 1.10 2000/04/25 23:02:51 jdolecek Exp $");
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "ext.h"
+#include "fsutil.h"
+
+int
+checkfilesys(const char *fname)
+{
+ int dosfs;
+ struct bootblock boot;
+ struct fatEntry *fat = NULL;
+ int i, finish_dosdirsection=0;
+ int mod = 0;
+ int ret = 8;
+
+ rdonly = alwaysno;
+ if (!preen)
+ printf("** %s", fname);
+
+ dosfs = open(fname, rdonly ? O_RDONLY : O_RDWR, 0);
+ if (dosfs < 0 && !rdonly) {
+ dosfs = open(fname, O_RDONLY, 0);
+ if (dosfs >= 0)
+ pwarn(" (NO WRITE)\n");
+ else if (!preen)
+ printf("\n");
+ rdonly = 1;
+ } else if (!preen)
+ printf("\n");
+
+ if (dosfs < 0) {
+ perror("Can't open");
+ return 8;
+ }
+
+ if (readboot(dosfs, &boot) != FSOK) {
+ close(dosfs);
+ printf("\n");
+ return 8;
+ }
+
+ if (skipclean && preen && checkdirty(dosfs, &boot)) {
+ printf("%s: ", fname);
+ printf("FILESYSTEM CLEAN; SKIPPING CHECKS\n");
+ ret = 0;
+ goto out;
+ }
+
+ if (!preen) {
+ if (boot.ValidFat < 0)
+ printf("** Phase 1 - Read and Compare FATs\n");
+ else
+ printf("** Phase 1 - Read FAT\n");
+ }
+
+ mod |= readfat(dosfs, &boot, boot.ValidFat >= 0 ? boot.ValidFat : 0, &fat);
+ if (mod & FSFATAL) {
+ close(dosfs);
+ return 8;
+ }
+
+ if (boot.ValidFat < 0)
+ for (i = 1; i < (int)boot.FATs; i++) {
+ struct fatEntry *currentFat;
+
+ mod |= readfat(dosfs, &boot, i, &currentFat);
+
+ if (mod & FSFATAL)
+ goto out;
+
+ mod |= comparefat(&boot, fat, currentFat, i);
+ free(currentFat);
+ if (mod & FSFATAL)
+ goto out;
+ }
+
+ if (!preen)
+ printf("** Phase 2 - Check Cluster Chains\n");
+
+ mod |= checkfat(&boot, fat);
+ if (mod & FSFATAL)
+ goto out;
+ /* delay writing FATs */
+
+ if (!preen)
+ printf("** Phase 3 - Checking Directories\n");
+
+ mod |= resetDosDirSection(&boot, fat);
+ finish_dosdirsection = 1;
+ if (mod & FSFATAL)
+ goto out;
+ /* delay writing FATs */
+
+ mod |= handleDirTree(dosfs, &boot, fat);
+ if (mod & FSFATAL)
+ goto out;
+
+ if (!preen)
+ printf("** Phase 4 - Checking for Lost Files\n");
+
+ mod |= checklost(dosfs, &boot, fat);
+ if (mod & FSFATAL)
+ goto out;
+
+ /* now write the FATs */
+ if (mod & FSFATMOD) {
+ if (ask(1, "Update FATs")) {
+ mod |= writefat(dosfs, &boot, fat, mod & FSFIXFAT);
+ if (mod & FSFATAL)
+ goto out;
+ } else
+ mod |= FSERROR;
+ }
+
+ if (boot.NumBad)
+ pwarn("%d files, %d free (%d clusters), %d bad (%d clusters)\n",
+ boot.NumFiles,
+ boot.NumFree * boot.ClusterSize / 1024, boot.NumFree,
+ boot.NumBad * boot.ClusterSize / 1024, boot.NumBad);
+ else
+ pwarn("%d files, %d free (%d clusters)\n",
+ boot.NumFiles,
+ boot.NumFree * boot.ClusterSize / 1024, boot.NumFree);
+
+ if (mod && (mod & FSERROR) == 0) {
+ if (mod & FSDIRTY) {
+ if (ask(1, "MARK FILE SYSTEM CLEAN") == 0)
+ mod &= ~FSDIRTY;
+
+ if (mod & FSDIRTY) {
+ pwarn("MARKING FILE SYSTEM CLEAN\n");
+ mod |= writefat(dosfs, &boot, fat, 1);
+ } else {
+ pwarn("\n***** FILE SYSTEM IS LEFT MARKED AS DIRTY *****\n");
+ mod |= FSERROR; /* file system not clean */
+ }
+ }
+ }
+
+ if (mod & (FSFATAL | FSERROR))
+ goto out;
+
+ ret = 0;
+
+ out:
+ if (finish_dosdirsection)
+ finishDosDirSection();
+ free(fat);
+ close(dosfs);
+
+ if (mod & (FSFATMOD|FSDIRMOD))
+ pwarn("\n***** FILE SYSTEM WAS MODIFIED *****\n");
+
+ return ret;
+}
diff --git a/sbin/fsck_msdosfs/dir.c b/sbin/fsck_msdosfs/dir.c
new file mode 100644
index 0000000..b87a916
--- /dev/null
+++ b/sbin/fsck_msdosfs/dir.c
@@ -0,0 +1,982 @@
+/*
+ * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
+ * Copyright (c) 1995 Martin Husemann
+ * Some structure declaration borrowed from Paul Popelka
+ * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference.
+ *
+ * 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 Martin Husemann
+ * and Wolfgang Solfrank.
+ * 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 THE AUTHORS ``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 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>
+#ifndef lint
+__RCSID("$NetBSD: dir.c,v 1.14 1998/08/25 19:18:15 ross Exp $");
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <sys/param.h>
+
+#include "ext.h"
+#include "fsutil.h"
+
+#define SLOT_EMPTY 0x00 /* slot has never been used */
+#define SLOT_E5 0x05 /* the real value is 0xe5 */
+#define SLOT_DELETED 0xe5 /* file in this slot deleted */
+
+#define ATTR_NORMAL 0x00 /* normal file */
+#define ATTR_READONLY 0x01 /* file is readonly */
+#define ATTR_HIDDEN 0x02 /* file is hidden */
+#define ATTR_SYSTEM 0x04 /* file is a system file */
+#define ATTR_VOLUME 0x08 /* entry is a volume label */
+#define ATTR_DIRECTORY 0x10 /* entry is a directory name */
+#define ATTR_ARCHIVE 0x20 /* file is new or modified */
+
+#define ATTR_WIN95 0x0f /* long name record */
+
+/*
+ * This is the format of the contents of the deTime field in the direntry
+ * structure.
+ * We don't use bitfields because we don't know how compilers for
+ * arbitrary machines will lay them out.
+ */
+#define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */
+#define DT_2SECONDS_SHIFT 0
+#define DT_MINUTES_MASK 0x7E0 /* minutes */
+#define DT_MINUTES_SHIFT 5
+#define DT_HOURS_MASK 0xF800 /* hours */
+#define DT_HOURS_SHIFT 11
+
+/*
+ * This is the format of the contents of the deDate field in the direntry
+ * structure.
+ */
+#define DD_DAY_MASK 0x1F /* day of month */
+#define DD_DAY_SHIFT 0
+#define DD_MONTH_MASK 0x1E0 /* month */
+#define DD_MONTH_SHIFT 5
+#define DD_YEAR_MASK 0xFE00 /* year - 1980 */
+#define DD_YEAR_SHIFT 9
+
+
+/* dir.c */
+static struct dosDirEntry *newDosDirEntry(void);
+static void freeDosDirEntry(struct dosDirEntry *);
+static struct dirTodoNode *newDirTodo(void);
+static void freeDirTodo(struct dirTodoNode *);
+static char *fullpath(struct dosDirEntry *);
+static u_char calcShortSum(u_char *);
+static int delete(int, struct bootblock *, struct fatEntry *, cl_t, int,
+ cl_t, int, int);
+static int removede(int, struct bootblock *, struct fatEntry *, u_char *,
+ u_char *, cl_t, cl_t, cl_t, char *, int);
+static int checksize(struct bootblock *, struct fatEntry *, u_char *,
+ struct dosDirEntry *);
+static int readDosDirSection(int, struct bootblock *, struct fatEntry *,
+ struct dosDirEntry *);
+
+/*
+ * Manage free dosDirEntry structures.
+ */
+static struct dosDirEntry *freede;
+
+static struct dosDirEntry *
+newDosDirEntry(void)
+{
+ struct dosDirEntry *de;
+
+ if (!(de = freede)) {
+ if (!(de = (struct dosDirEntry *)malloc(sizeof *de)))
+ return 0;
+ } else
+ freede = de->next;
+ return de;
+}
+
+static void
+freeDosDirEntry(struct dosDirEntry *de)
+{
+ de->next = freede;
+ freede = de;
+}
+
+/*
+ * The same for dirTodoNode structures.
+ */
+static struct dirTodoNode *freedt;
+
+static struct dirTodoNode *
+newDirTodo(void)
+{
+ struct dirTodoNode *dt;
+
+ if (!(dt = freedt)) {
+ if (!(dt = (struct dirTodoNode *)malloc(sizeof *dt)))
+ return 0;
+ } else
+ freedt = dt->next;
+ return dt;
+}
+
+static void
+freeDirTodo(struct dirTodoNode *dt)
+{
+ dt->next = freedt;
+ freedt = dt;
+}
+
+/*
+ * The stack of unread directories
+ */
+struct dirTodoNode *pendingDirectories = NULL;
+
+/*
+ * Return the full pathname for a directory entry.
+ */
+static char *
+fullpath(struct dosDirEntry *dir)
+{
+ static char namebuf[MAXPATHLEN + 1];
+ char *cp, *np;
+ int nl;
+
+ cp = namebuf + sizeof namebuf - 1;
+ *cp = '\0';
+ do {
+ np = dir->lname[0] ? dir->lname : dir->name;
+ nl = strlen(np);
+ if ((cp -= nl) <= namebuf + 1)
+ break;
+ memcpy(cp, np, nl);
+ *--cp = '/';
+ } while ((dir = dir->parent) != NULL);
+ if (dir)
+ *--cp = '?';
+ else
+ cp++;
+ return cp;
+}
+
+/*
+ * Calculate a checksum over an 8.3 alias name
+ */
+static u_char
+calcShortSum(u_char *p)
+{
+ u_char sum = 0;
+ int i;
+
+ for (i = 0; i < 11; i++) {
+ sum = (sum << 7)|(sum >> 1); /* rotate right */
+ sum += p[i];
+ }
+
+ return sum;
+}
+
+/*
+ * Global variables temporarily used during a directory scan
+ */
+static char longName[DOSLONGNAMELEN] = "";
+static u_char *buffer = NULL;
+static u_char *delbuf = NULL;
+
+struct dosDirEntry *rootDir;
+static struct dosDirEntry *lostDir;
+
+/*
+ * Init internal state for a new directory scan.
+ */
+int
+resetDosDirSection(struct bootblock *boot, struct fatEntry *fat)
+{
+ int b1, b2;
+ cl_t cl;
+ int ret = FSOK;
+
+ b1 = boot->RootDirEnts * 32;
+ b2 = boot->SecPerClust * boot->BytesPerSec;
+
+ if (!(buffer = malloc(b1 > b2 ? b1 : b2))
+ || !(delbuf = malloc(b2))
+ || !(rootDir = newDosDirEntry())) {
+ perror("No space for directory");
+ return FSFATAL;
+ }
+ memset(rootDir, 0, sizeof *rootDir);
+ if (boot->flags & FAT32) {
+ if (boot->RootCl < CLUST_FIRST || boot->RootCl >= boot->NumClusters) {
+ pfatal("Root directory starts with cluster out of range(%u)",
+ boot->RootCl);
+ return FSFATAL;
+ }
+ cl = fat[boot->RootCl].next;
+ if (cl < CLUST_FIRST
+ || (cl >= CLUST_RSRVD && cl< CLUST_EOFS)
+ || fat[boot->RootCl].head != boot->RootCl) {
+ if (cl == CLUST_FREE)
+ pwarn("Root directory starts with free cluster\n");
+ else if (cl >= CLUST_RSRVD)
+ pwarn("Root directory starts with cluster marked %s\n",
+ rsrvdcltype(cl));
+ else {
+ pfatal("Root directory doesn't start a cluster chain");
+ return FSFATAL;
+ }
+ if (ask(1, "Fix")) {
+ fat[boot->RootCl].next = CLUST_FREE;
+ ret = FSFATMOD;
+ } else
+ ret = FSFATAL;
+ }
+
+ fat[boot->RootCl].flags |= FAT_USED;
+ rootDir->head = boot->RootCl;
+ }
+
+ return ret;
+}
+
+/*
+ * Cleanup after a directory scan
+ */
+void
+finishDosDirSection(void)
+{
+ struct dirTodoNode *p, *np;
+ struct dosDirEntry *d, *nd;
+
+ for (p = pendingDirectories; p; p = np) {
+ np = p->next;
+ freeDirTodo(p);
+ }
+ pendingDirectories = 0;
+ for (d = rootDir; d; d = nd) {
+ if ((nd = d->child) != NULL) {
+ d->child = 0;
+ continue;
+ }
+ if (!(nd = d->next))
+ nd = d->parent;
+ freeDosDirEntry(d);
+ }
+ rootDir = lostDir = NULL;
+ free(buffer);
+ free(delbuf);
+ buffer = NULL;
+ delbuf = NULL;
+}
+
+/*
+ * Delete directory entries between startcl, startoff and endcl, endoff.
+ */
+static int
+delete(int f, struct bootblock *boot, struct fatEntry *fat, cl_t startcl,
+ int startoff, cl_t endcl, int endoff, int notlast)
+{
+ u_char *s, *e;
+ off_t off;
+ int clsz = boot->SecPerClust * boot->BytesPerSec;
+
+ s = delbuf + startoff;
+ e = delbuf + clsz;
+ while (startcl >= CLUST_FIRST && startcl < boot->NumClusters) {
+ if (startcl == endcl) {
+ if (notlast)
+ break;
+ e = delbuf + endoff;
+ }
+ off = startcl * boot->SecPerClust + boot->ClusterOffset;
+ off *= boot->BytesPerSec;
+ if (lseek(f, off, SEEK_SET) != off
+ || read(f, delbuf, clsz) != clsz) {
+ perror("Unable to read directory");
+ return FSFATAL;
+ }
+ while (s < e) {
+ *s = SLOT_DELETED;
+ s += 32;
+ }
+ if (lseek(f, off, SEEK_SET) != off
+ || write(f, delbuf, clsz) != clsz) {
+ perror("Unable to write directory");
+ return FSFATAL;
+ }
+ if (startcl == endcl)
+ break;
+ startcl = fat[startcl].next;
+ s = delbuf;
+ }
+ return FSOK;
+}
+
+static int
+removede(int f, struct bootblock *boot, struct fatEntry *fat, u_char *start,
+ u_char *end, cl_t startcl, cl_t endcl, cl_t curcl, char *path, int type)
+{
+ switch (type) {
+ case 0:
+ pwarn("Invalid long filename entry for %s\n", path);
+ break;
+ case 1:
+ pwarn("Invalid long filename entry at end of directory %s\n", path);
+ break;
+ case 2:
+ pwarn("Invalid long filename entry for volume label\n");
+ break;
+ }
+ if (ask(0, "Remove")) {
+ if (startcl != curcl) {
+ if (delete(f, boot, fat,
+ startcl, start - buffer,
+ endcl, end - buffer,
+ endcl == curcl) == FSFATAL)
+ return FSFATAL;
+ start = buffer;
+ }
+ if (endcl == curcl)
+ for (; start < end; start += 32)
+ *start = SLOT_DELETED;
+ return FSDIRMOD;
+ }
+ return FSERROR;
+}
+
+/*
+ * Check an in-memory file entry
+ */
+static int
+checksize(struct bootblock *boot, struct fatEntry *fat, u_char *p,
+ struct dosDirEntry *dir)
+{
+ /*
+ * Check size on ordinary files
+ */
+ int32_t physicalSize;
+
+ if (dir->head == CLUST_FREE)
+ physicalSize = 0;
+ else {
+ if (dir->head < CLUST_FIRST || dir->head >= boot->NumClusters)
+ return FSERROR;
+ physicalSize = fat[dir->head].length * boot->ClusterSize;
+ }
+ if (physicalSize < dir->size) {
+ pwarn("size of %s is %u, should at most be %u\n",
+ fullpath(dir), dir->size, physicalSize);
+ if (ask(1, "Truncate")) {
+ dir->size = physicalSize;
+ p[28] = (u_char)physicalSize;
+ p[29] = (u_char)(physicalSize >> 8);
+ p[30] = (u_char)(physicalSize >> 16);
+ p[31] = (u_char)(physicalSize >> 24);
+ return FSDIRMOD;
+ } else
+ return FSERROR;
+ } else if (physicalSize - dir->size >= boot->ClusterSize) {
+ pwarn("%s has too many clusters allocated\n",
+ fullpath(dir));
+ if (ask(1, "Drop superfluous clusters")) {
+ cl_t cl;
+ u_int32_t sz = 0;
+
+ for (cl = dir->head; (sz += boot->ClusterSize) < dir->size;)
+ cl = fat[cl].next;
+ clearchain(boot, fat, fat[cl].next);
+ fat[cl].next = CLUST_EOF;
+ return FSFATMOD;
+ } else
+ return FSERROR;
+ }
+ return FSOK;
+}
+
+/*
+ * Read a directory and
+ * - resolve long name records
+ * - enter file and directory records into the parent's list
+ * - push directories onto the todo-stack
+ */
+static int
+readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat,
+ struct dosDirEntry *dir)
+{
+ struct dosDirEntry dirent, *d;
+ u_char *p, *vallfn, *invlfn, *empty;
+ off_t off;
+ int i, j, k, last;
+ cl_t cl, valcl = ~0, invcl = ~0, empcl = ~0;
+ char *t;
+ u_int lidx = 0;
+ int shortSum;
+ int mod = FSOK;
+#define THISMOD 0x8000 /* Only used within this routine */
+
+ cl = dir->head;
+ if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) {
+ /*
+ * Already handled somewhere else.
+ */
+ return FSOK;
+ }
+ shortSum = -1;
+ vallfn = invlfn = empty = NULL;
+ do {
+ if (!(boot->flags & FAT32) && !dir->parent) {
+ last = boot->RootDirEnts * 32;
+ off = boot->ResSectors + boot->FATs * boot->FATsecs;
+ } else {
+ last = boot->SecPerClust * boot->BytesPerSec;
+ off = cl * boot->SecPerClust + boot->ClusterOffset;
+ }
+
+ off *= boot->BytesPerSec;
+ if (lseek(f, off, SEEK_SET) != off
+ || read(f, buffer, last) != last) {
+ perror("Unable to read directory");
+ return FSFATAL;
+ }
+ last /= 32;
+ /*
+ * Check `.' and `..' entries here? XXX
+ */
+ for (p = buffer, i = 0; i < last; i++, p += 32) {
+ if (dir->fsckflags & DIREMPWARN) {
+ *p = SLOT_EMPTY;
+ continue;
+ }
+
+ if (*p == SLOT_EMPTY || *p == SLOT_DELETED) {
+ if (*p == SLOT_EMPTY) {
+ dir->fsckflags |= DIREMPTY;
+ empty = p;
+ empcl = cl;
+ }
+ continue;
+ }
+
+ if (dir->fsckflags & DIREMPTY) {
+ if (!(dir->fsckflags & DIREMPWARN)) {
+ pwarn("%s has entries after end of directory\n",
+ fullpath(dir));
+ if (ask(1, "Extend")) {
+ u_char *q;
+
+ dir->fsckflags &= ~DIREMPTY;
+ if (delete(f, boot, fat,
+ empcl, empty - buffer,
+ cl, p - buffer, 1) == FSFATAL)
+ return FSFATAL;
+ q = empcl == cl ? empty : buffer;
+ for (; q < p; q += 32)
+ *q = SLOT_DELETED;
+ mod |= THISMOD|FSDIRMOD;
+ } else if (ask(0, "Truncate"))
+ dir->fsckflags |= DIREMPWARN;
+ }
+ if (dir->fsckflags & DIREMPWARN) {
+ *p = SLOT_DELETED;
+ mod |= THISMOD|FSDIRMOD;
+ continue;
+ } else if (dir->fsckflags & DIREMPTY)
+ mod |= FSERROR;
+ empty = NULL;
+ }
+
+ if (p[11] == ATTR_WIN95) {
+ if (*p & LRFIRST) {
+ if (shortSum != -1) {
+ if (!invlfn) {
+ invlfn = vallfn;
+ invcl = valcl;
+ }
+ }
+ memset(longName, 0, sizeof longName);
+ shortSum = p[13];
+ vallfn = p;
+ valcl = cl;
+ } else if (shortSum != p[13]
+ || lidx != (*p & LRNOMASK)) {
+ if (!invlfn) {
+ invlfn = vallfn;
+ invcl = valcl;
+ }
+ if (!invlfn) {
+ invlfn = p;
+ invcl = cl;
+ }
+ vallfn = NULL;
+ }
+ lidx = *p & LRNOMASK;
+ t = longName + --lidx * 13;
+ for (k = 1; k < 11 && t < longName + sizeof(longName); k += 2) {
+ if (!p[k] && !p[k + 1])
+ break;
+ *t++ = p[k];
+ /*
+ * Warn about those unusable chars in msdosfs here? XXX
+ */
+ if (p[k + 1])
+ t[-1] = '?';
+ }
+ if (k >= 11)
+ for (k = 14; k < 26 && t < longName + sizeof(longName); k += 2) {
+ if (!p[k] && !p[k + 1])
+ break;
+ *t++ = p[k];
+ if (p[k + 1])
+ t[-1] = '?';
+ }
+ if (k >= 26)
+ for (k = 28; k < 32 && t < longName + sizeof(longName); k += 2) {
+ if (!p[k] && !p[k + 1])
+ break;
+ *t++ = p[k];
+ if (p[k + 1])
+ t[-1] = '?';
+ }
+ if (t >= longName + sizeof(longName)) {
+ pwarn("long filename too long\n");
+ if (!invlfn) {
+ invlfn = vallfn;
+ invcl = valcl;
+ }
+ vallfn = NULL;
+ }
+ if (p[26] | (p[27] << 8)) {
+ pwarn("long filename record cluster start != 0\n");
+ if (!invlfn) {
+ invlfn = vallfn;
+ invcl = cl;
+ }
+ vallfn = NULL;
+ }
+ continue; /* long records don't carry further
+ * information */
+ }
+
+ /*
+ * This is a standard msdosfs directory entry.
+ */
+ memset(&dirent, 0, sizeof dirent);
+
+ /*
+ * it's a short name record, but we need to know
+ * more, so get the flags first.
+ */
+ dirent.flags = p[11];
+
+ /*
+ * Translate from 850 to ISO here XXX
+ */
+ for (j = 0; j < 8; j++)
+ dirent.name[j] = p[j];
+ dirent.name[8] = '\0';
+ for (k = 7; k >= 0 && dirent.name[k] == ' '; k--)
+ dirent.name[k] = '\0';
+ if (dirent.name[k] != '\0')
+ k++;
+ if (dirent.name[0] == SLOT_E5)
+ dirent.name[0] = 0xe5;
+
+ if (dirent.flags & ATTR_VOLUME) {
+ if (vallfn || invlfn) {
+ mod |= removede(f, boot, fat,
+ invlfn ? invlfn : vallfn, p,
+ invlfn ? invcl : valcl, -1, 0,
+ fullpath(dir), 2);
+ vallfn = NULL;
+ invlfn = NULL;
+ }
+ continue;
+ }
+
+ if (p[8] != ' ')
+ dirent.name[k++] = '.';
+ for (j = 0; j < 3; j++)
+ dirent.name[k++] = p[j+8];
+ dirent.name[k] = '\0';
+ for (k--; k >= 0 && dirent.name[k] == ' '; k--)
+ dirent.name[k] = '\0';
+
+ if (vallfn && shortSum != calcShortSum(p)) {
+ if (!invlfn) {
+ invlfn = vallfn;
+ invcl = valcl;
+ }
+ vallfn = NULL;
+ }
+ dirent.head = p[26] | (p[27] << 8);
+ if (boot->ClustMask == CLUST32_MASK)
+ dirent.head |= (p[20] << 16) | (p[21] << 24);
+ dirent.size = p[28] | (p[29] << 8) | (p[30] << 16) | (p[31] << 24);
+ if (vallfn) {
+ strcpy(dirent.lname, longName);
+ longName[0] = '\0';
+ shortSum = -1;
+ }
+
+ dirent.parent = dir;
+ dirent.next = dir->child;
+
+ if (invlfn) {
+ mod |= k = removede(f, boot, fat,
+ invlfn, vallfn ? vallfn : p,
+ invcl, vallfn ? valcl : cl, cl,
+ fullpath(&dirent), 0);
+ if (mod & FSFATAL)
+ return FSFATAL;
+ if (vallfn
+ ? (valcl == cl && vallfn != buffer)
+ : p != buffer)
+ if (k & FSDIRMOD)
+ mod |= THISMOD;
+ }
+
+ vallfn = NULL; /* not used any longer */
+ invlfn = NULL;
+
+ if (dirent.size == 0 && !(dirent.flags & ATTR_DIRECTORY)) {
+ if (dirent.head != 0) {
+ pwarn("%s has clusters, but size 0\n",
+ fullpath(&dirent));
+ if (ask(1, "Drop allocated clusters")) {
+ p[26] = p[27] = 0;
+ if (boot->ClustMask == CLUST32_MASK)
+ p[20] = p[21] = 0;
+ clearchain(boot, fat, dirent.head);
+ dirent.head = 0;
+ mod |= THISMOD|FSDIRMOD|FSFATMOD;
+ } else
+ mod |= FSERROR;
+ }
+ } else if (dirent.head == 0
+ && !strcmp(dirent.name, "..")
+ && dir->parent /* XXX */
+ && !dir->parent->parent) {
+ /*
+ * Do nothing, the parent is the root
+ */
+ } else if (dirent.head < CLUST_FIRST
+ || dirent.head >= boot->NumClusters
+ || fat[dirent.head].next == CLUST_FREE
+ || (fat[dirent.head].next >= CLUST_RSRVD
+ && fat[dirent.head].next < CLUST_EOFS)
+ || fat[dirent.head].head != dirent.head) {
+ if (dirent.head == 0)
+ pwarn("%s has no clusters\n",
+ fullpath(&dirent));
+ else if (dirent.head < CLUST_FIRST
+ || dirent.head >= boot->NumClusters)
+ pwarn("%s starts with cluster out of range(%u)\n",
+ fullpath(&dirent),
+ dirent.head);
+ else if (fat[dirent.head].next == CLUST_FREE)
+ pwarn("%s starts with free cluster\n",
+ fullpath(&dirent));
+ else if (fat[dirent.head].next >= CLUST_RSRVD)
+ pwarn("%s starts with cluster marked %s\n",
+ fullpath(&dirent),
+ rsrvdcltype(fat[dirent.head].next));
+ else
+ pwarn("%s doesn't start a cluster chain\n",
+ fullpath(&dirent));
+ if (dirent.flags & ATTR_DIRECTORY) {
+ if (ask(0, "Remove")) {
+ *p = SLOT_DELETED;
+ mod |= THISMOD|FSDIRMOD;
+ } else
+ mod |= FSERROR;
+ continue;
+ } else {
+ if (ask(1, "Truncate")) {
+ p[28] = p[29] = p[30] = p[31] = 0;
+ p[26] = p[27] = 0;
+ if (boot->ClustMask == CLUST32_MASK)
+ p[20] = p[21] = 0;
+ dirent.size = 0;
+ mod |= THISMOD|FSDIRMOD;
+ } else
+ mod |= FSERROR;
+ }
+ }
+
+ if (dirent.head >= CLUST_FIRST && dirent.head < boot->NumClusters)
+ fat[dirent.head].flags |= FAT_USED;
+
+ if (dirent.flags & ATTR_DIRECTORY) {
+ /*
+ * gather more info for directories
+ */
+ struct dirTodoNode *n;
+
+ if (dirent.size) {
+ pwarn("Directory %s has size != 0\n",
+ fullpath(&dirent));
+ if (ask(1, "Correct")) {
+ p[28] = p[29] = p[30] = p[31] = 0;
+ dirent.size = 0;
+ mod |= THISMOD|FSDIRMOD;
+ } else
+ mod |= FSERROR;
+ }
+ /*
+ * handle `.' and `..' specially
+ */
+ if (strcmp(dirent.name, ".") == 0) {
+ if (dirent.head != dir->head) {
+ pwarn("`.' entry in %s has incorrect start cluster\n",
+ fullpath(dir));
+ if (ask(1, "Correct")) {
+ dirent.head = dir->head;
+ p[26] = (u_char)dirent.head;
+ p[27] = (u_char)(dirent.head >> 8);
+ if (boot->ClustMask == CLUST32_MASK) {
+ p[20] = (u_char)(dirent.head >> 16);
+ p[21] = (u_char)(dirent.head >> 24);
+ }
+ mod |= THISMOD|FSDIRMOD;
+ } else
+ mod |= FSERROR;
+ }
+ continue;
+ }
+ if (strcmp(dirent.name, "..") == 0) {
+ if (dir->parent) { /* XXX */
+ if (!dir->parent->parent) {
+ if (dirent.head) {
+ pwarn("`..' entry in %s has non-zero start cluster\n",
+ fullpath(dir));
+ if (ask(1, "Correct")) {
+ dirent.head = 0;
+ p[26] = p[27] = 0;
+ if (boot->ClustMask == CLUST32_MASK)
+ p[20] = p[21] = 0;
+ mod |= THISMOD|FSDIRMOD;
+ } else
+ mod |= FSERROR;
+ }
+ } else if (dirent.head != dir->parent->head) {
+ pwarn("`..' entry in %s has incorrect start cluster\n",
+ fullpath(dir));
+ if (ask(1, "Correct")) {
+ dirent.head = dir->parent->head;
+ p[26] = (u_char)dirent.head;
+ p[27] = (u_char)(dirent.head >> 8);
+ if (boot->ClustMask == CLUST32_MASK) {
+ p[20] = (u_char)(dirent.head >> 16);
+ p[21] = (u_char)(dirent.head >> 24);
+ }
+ mod |= THISMOD|FSDIRMOD;
+ } else
+ mod |= FSERROR;
+ }
+ }
+ continue;
+ }
+
+ /* create directory tree node */
+ if (!(d = newDosDirEntry())) {
+ perror("No space for directory");
+ return FSFATAL;
+ }
+ memcpy(d, &dirent, sizeof(struct dosDirEntry));
+ /* link it into the tree */
+ dir->child = d;
+
+ /* Enter this directory into the todo list */
+ if (!(n = newDirTodo())) {
+ perror("No space for todo list");
+ return FSFATAL;
+ }
+ n->next = pendingDirectories;
+ n->dir = d;
+ pendingDirectories = n;
+ } else {
+ mod |= k = checksize(boot, fat, p, &dirent);
+ if (k & FSDIRMOD)
+ mod |= THISMOD;
+ }
+ boot->NumFiles++;
+ }
+ if (mod & THISMOD) {
+ last *= 32;
+ if (lseek(f, off, SEEK_SET) != off
+ || write(f, buffer, last) != last) {
+ perror("Unable to write directory");
+ return FSFATAL;
+ }
+ mod &= ~THISMOD;
+ }
+ } while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters);
+ if (invlfn || vallfn)
+ mod |= removede(f, boot, fat,
+ invlfn ? invlfn : vallfn, p,
+ invlfn ? invcl : valcl, -1, 0,
+ fullpath(dir), 1);
+ return mod & ~THISMOD;
+}
+
+int
+handleDirTree(int dosfs, struct bootblock *boot, struct fatEntry *fat)
+{
+ int mod;
+
+ mod = readDosDirSection(dosfs, boot, fat, rootDir);
+ if (mod & FSFATAL)
+ return FSFATAL;
+
+ /*
+ * process the directory todo list
+ */
+ while (pendingDirectories) {
+ struct dosDirEntry *dir = pendingDirectories->dir;
+ struct dirTodoNode *n = pendingDirectories->next;
+
+ /*
+ * remove TODO entry now, the list might change during
+ * directory reads
+ */
+ freeDirTodo(pendingDirectories);
+ pendingDirectories = n;
+
+ /*
+ * handle subdirectory
+ */
+ mod |= readDosDirSection(dosfs, boot, fat, dir);
+ if (mod & FSFATAL)
+ return FSFATAL;
+ }
+
+ return mod;
+}
+
+/*
+ * Try to reconnect a FAT chain into dir
+ */
+static u_char *lfbuf;
+static cl_t lfcl;
+static off_t lfoff;
+
+int
+reconnect(int dosfs, struct bootblock *boot, struct fatEntry *fat, cl_t head)
+{
+ struct dosDirEntry d;
+ u_char *p;
+
+ if (!ask(1, "Reconnect"))
+ return FSERROR;
+
+ if (!lostDir) {
+ for (lostDir = rootDir->child; lostDir; lostDir = lostDir->next) {
+ if (!strcmp(lostDir->name, LOSTDIR))
+ break;
+ }
+ if (!lostDir) { /* Create LOSTDIR? XXX */
+ pwarn("No %s directory\n", LOSTDIR);
+ return FSERROR;
+ }
+ }
+ if (!lfbuf) {
+ lfbuf = malloc(boot->ClusterSize);
+ if (!lfbuf) {
+ perror("No space for buffer");
+ return FSFATAL;
+ }
+ p = NULL;
+ } else
+ p = lfbuf;
+ while (1) {
+ if (p)
+ for (; p < lfbuf + boot->ClusterSize; p += 32)
+ if (*p == SLOT_EMPTY
+ || *p == SLOT_DELETED)
+ break;
+ if (p && p < lfbuf + boot->ClusterSize)
+ break;
+ lfcl = p ? fat[lfcl].next : lostDir->head;
+ if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) {
+ /* Extend LOSTDIR? XXX */
+ pwarn("No space in %s\n", LOSTDIR);
+ return FSERROR;
+ }
+ lfoff = lfcl * boot->ClusterSize
+ + boot->ClusterOffset * boot->BytesPerSec;
+ if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
+ || read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
+ perror("could not read LOST.DIR");
+ return FSFATAL;
+ }
+ p = lfbuf;
+ }
+
+ boot->NumFiles++;
+ /* Ensure uniqueness of entry here! XXX */
+ memset(&d, 0, sizeof d);
+ (void)snprintf(d.name, sizeof(d.name), "%u", head);
+ d.flags = 0;
+ d.head = head;
+ d.size = fat[head].length * boot->ClusterSize;
+
+ memset(p, 0, 32);
+ memset(p, ' ', 11);
+ memcpy(p, d.name, strlen(d.name));
+ p[26] = (u_char)d.head;
+ p[27] = (u_char)(d.head >> 8);
+ if (boot->ClustMask == CLUST32_MASK) {
+ p[20] = (u_char)(d.head >> 16);
+ p[21] = (u_char)(d.head >> 24);
+ }
+ p[28] = (u_char)d.size;
+ p[29] = (u_char)(d.size >> 8);
+ p[30] = (u_char)(d.size >> 16);
+ p[31] = (u_char)(d.size >> 24);
+ fat[head].flags |= FAT_USED;
+ if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
+ || write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
+ perror("could not write LOST.DIR");
+ return FSFATAL;
+ }
+ return FSDIRMOD;
+}
+
+void
+finishlf(void)
+{
+ if (lfbuf)
+ free(lfbuf);
+ lfbuf = NULL;
+}
diff --git a/sbin/fsck_msdosfs/dosfs.h b/sbin/fsck_msdosfs/dosfs.h
new file mode 100644
index 0000000..3a5d439
--- /dev/null
+++ b/sbin/fsck_msdosfs/dosfs.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
+ * Copyright (c) 1995 Martin Husemann
+ * Some structure declaration borrowed from Paul Popelka
+ * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference.
+ *
+ * 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 Martin Husemann
+ * and Wolfgang Solfrank.
+ * 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 THE AUTHORS ``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 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.
+ * $NetBSD: dosfs.h,v 1.4 1997/01/03 14:32:48 ws Exp $
+ * $FreeBSD$
+ */
+
+#ifndef DOSFS_H
+#define DOSFS_H
+
+#define DOSBOOTBLOCKSIZE 512
+
+typedef u_int32_t cl_t; /* type holding a cluster number */
+
+/*
+ * architecture independent description of all the info stored in a
+ * FAT boot block.
+ */
+struct bootblock {
+ u_int BytesPerSec; /* bytes per sector */
+ u_int SecPerClust; /* sectors per cluster */
+ u_int ResSectors; /* number of reserved sectors */
+ u_int FATs; /* number of FATs */
+ u_int RootDirEnts; /* number of root directory entries */
+ u_int Media; /* media descriptor */
+ u_int FATsmall; /* number of sectors per FAT */
+ u_int SecPerTrack; /* sectors per track */
+ u_int Heads; /* number of heads */
+ u_int32_t Sectors; /* total number of sectors */
+ u_int32_t HiddenSecs; /* # of hidden sectors */
+ u_int32_t HugeSectors; /* # of sectors if bpbSectors == 0 */
+ u_int FSInfo; /* FSInfo sector */
+ u_int Backup; /* Backup of Bootblocks */
+ cl_t RootCl; /* Start of Root Directory */
+ cl_t FSFree; /* Number of free clusters acc. FSInfo */
+ cl_t FSNext; /* Next free cluster acc. FSInfo */
+
+ /* and some more calculated values */
+ u_int flags; /* some flags: */
+#define FAT32 1 /* this is a FAT32 file system */
+ /*
+ * Maybe, we should separate out
+ * various parts of FAT32? XXX
+ */
+ int ValidFat; /* valid fat if FAT32 non-mirrored */
+ cl_t ClustMask; /* mask for entries in FAT */
+ cl_t NumClusters; /* # of entries in a FAT */
+ u_int32_t NumSectors; /* how many sectors are there */
+ u_int32_t FATsecs; /* how many sectors are in FAT */
+ u_int32_t NumFatEntries; /* how many entries really are there */
+ u_int ClusterOffset; /* at what sector would sector 0 start */
+ u_int ClusterSize; /* Cluster size in bytes */
+
+ /* Now some statistics: */
+ u_int NumFiles; /* # of plain files */
+ u_int NumFree; /* # of free clusters */
+ u_int NumBad; /* # of bad clusters */
+};
+
+struct fatEntry {
+ cl_t next; /* pointer to next cluster */
+ cl_t head; /* pointer to start of chain */
+ u_int32_t length; /* number of clusters on chain */
+ int flags; /* see below */
+};
+
+#define CLUST_FREE 0 /* 0 means cluster is free */
+#define CLUST_FIRST 2 /* 2 is the minimum valid cluster number */
+#define CLUST_RSRVD 0xfffffff6 /* start of reserved clusters */
+#define CLUST_BAD 0xfffffff7 /* a cluster with a defect */
+#define CLUST_EOFS 0xfffffff8 /* start of EOF indicators */
+#define CLUST_EOF 0xffffffff /* standard value for last cluster */
+
+/*
+ * Masks for cluster values
+ */
+#define CLUST12_MASK 0xfff
+#define CLUST16_MASK 0xffff
+#define CLUST32_MASK 0xfffffff
+
+#define FAT_USED 1 /* This fat chain is used in a file */
+
+#define DOSLONGNAMELEN 256 /* long name maximal length */
+#define LRFIRST 0x40 /* first long name record */
+#define LRNOMASK 0x1f /* mask to extract long record
+ * sequence number */
+
+/*
+ * Architecture independent description of a directory entry
+ */
+struct dosDirEntry {
+ struct dosDirEntry
+ *parent, /* previous tree level */
+ *next, /* next brother */
+ *child; /* if this is a directory */
+ char name[8+1+3+1]; /* alias name first part */
+ char lname[DOSLONGNAMELEN]; /* real name */
+ uint flags; /* attributes */
+ cl_t head; /* cluster no */
+ u_int32_t size; /* filesize in bytes */
+ uint fsckflags; /* flags during fsck */
+};
+/* Flags in fsckflags: */
+#define DIREMPTY 1
+#define DIREMPWARN 2
+
+/*
+ * TODO-list of unread directories
+ */
+struct dirTodoNode {
+ struct dosDirEntry *dir;
+ struct dirTodoNode *next;
+};
+
+#endif
diff --git a/sbin/fsck_msdosfs/ext.h b/sbin/fsck_msdosfs/ext.h
new file mode 100644
index 0000000..53127cf
--- /dev/null
+++ b/sbin/fsck_msdosfs/ext.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
+ * Copyright (c) 1995 Martin Husemann
+ *
+ * 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 Martin Husemann
+ * and Wolfgang Solfrank.
+ * 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 THE AUTHORS ``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 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.
+ * $NetBSD: ext.h,v 1.6 2000/04/25 23:02:51 jdolecek Exp $
+ * $FreeBSD$
+ */
+
+#ifndef EXT_H
+#define EXT_H
+
+#include <sys/types.h>
+
+#include "dosfs.h"
+
+#define LOSTDIR "LOST.DIR"
+
+/*
+ * Options:
+ */
+extern int alwaysno; /* assume "no" for all questions */
+extern int alwaysyes; /* assume "yes" for all questions */
+extern int preen; /* we are preening */
+extern int rdonly; /* device is opened read only (supersedes above) */
+extern int skipclean; /* skip clean file systems if preening */
+
+extern struct dosDirEntry *rootDir;
+
+/*
+ * function declarations
+ */
+int ask(int, const char *, ...) __printflike(2, 3);
+
+/*
+ * Check the dirty flag. If the file system is clean, then return 1.
+ * Otherwise, return 0 (this includes the case of FAT12 file systems --
+ * they have no dirty flag, so they must be assumed to be unclean).
+ */
+int checkdirty(int, struct bootblock *);
+
+/*
+ * Check file system given as arg
+ */
+int checkfilesys(const char *);
+
+/*
+ * Return values of various functions
+ */
+#define FSOK 0 /* Check was OK */
+#define FSBOOTMOD 1 /* Boot block was modified */
+#define FSDIRMOD 2 /* Some directory was modified */
+#define FSFATMOD 4 /* The FAT was modified */
+#define FSERROR 8 /* Some unrecovered error remains */
+#define FSFATAL 16 /* Some unrecoverable error occured */
+#define FSDIRTY 32 /* File system is dirty */
+#define FSFIXFAT 64 /* Fix file system FAT */
+
+/*
+ * read a boot block in a machine independend fashion and translate
+ * it into our struct bootblock.
+ */
+int readboot(int, struct bootblock *);
+
+/*
+ * Correct the FSInfo block.
+ */
+int writefsinfo(int, struct bootblock *);
+
+/*
+ * Read one of the FAT copies and return a pointer to the new
+ * allocated array holding our description of it.
+ */
+int readfat(int, struct bootblock *, int, struct fatEntry **);
+
+/*
+ * Check two FAT copies for consistency and merge changes into the
+ * first if neccessary.
+ */
+int comparefat(struct bootblock *, struct fatEntry *, struct fatEntry *, int);
+
+/*
+ * Check a FAT
+ */
+int checkfat(struct bootblock *, struct fatEntry *);
+
+/*
+ * Write back FAT entries
+ */
+int writefat(int, struct bootblock *, struct fatEntry *, int);
+
+/*
+ * Read a directory
+ */
+int resetDosDirSection(struct bootblock *, struct fatEntry *);
+void finishDosDirSection(void);
+int handleDirTree(int, struct bootblock *, struct fatEntry *);
+
+/*
+ * Cross-check routines run after everything is completely in memory
+ */
+/*
+ * Check for lost cluster chains
+ */
+int checklost(int, struct bootblock *, struct fatEntry *);
+/*
+ * Try to reconnect a lost cluster chain
+ */
+int reconnect(int, struct bootblock *, struct fatEntry *, cl_t);
+void finishlf(void);
+
+/*
+ * Small helper functions
+ */
+/*
+ * Return the type of a reserved cluster as text
+ */
+char *rsrvdcltype(cl_t);
+
+/*
+ * Clear a cluster chain in a FAT
+ */
+void clearchain(struct bootblock *, struct fatEntry *, cl_t);
+
+#endif
diff --git a/sbin/fsck_msdosfs/fat.c b/sbin/fsck_msdosfs/fat.c
new file mode 100644
index 0000000..0b0bd9e
--- /dev/null
+++ b/sbin/fsck_msdosfs/fat.c
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
+ * Copyright (c) 1995 Martin Husemann
+ *
+ * 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 Martin Husemann
+ * and Wolfgang Solfrank.
+ * 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 THE AUTHORS ``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 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>
+#ifndef lint
+__RCSID("$NetBSD: fat.c,v 1.12 2000/10/10 20:24:52 is Exp $");
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "ext.h"
+#include "fsutil.h"
+
+static int checkclnum(struct bootblock *, int, cl_t, cl_t *);
+static int clustdiffer(cl_t, cl_t *, cl_t *, int);
+static int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *);
+static int _readfat(int, struct bootblock *, int, u_char **);
+
+/*-
+ * The first 2 FAT entries contain pseudo-cluster numbers with the following
+ * layout:
+ *
+ * 31...... ........ ........ .......0
+ * rrrr1111 11111111 11111111 mmmmmmmm FAT32 entry 0
+ * rrrrsh11 11111111 11111111 11111xxx FAT32 entry 1
+ *
+ * 11111111 mmmmmmmm FAT16 entry 0
+ * sh111111 11111xxx FAT16 entry 1
+ *
+ * r = reserved
+ * m = BPB media ID byte
+ * s = clean flag (1 = dismounted; 0 = still mounted)
+ * h = hard error flag (1 = ok; 0 = I/O error)
+ * x = any value ok
+ */
+
+int
+checkdirty(int fs, struct bootblock *boot)
+{
+ off_t off;
+ u_char *buffer;
+ int ret = 0;
+
+ if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK)
+ return 0;
+
+ off = boot->ResSectors;
+ off *= boot->BytesPerSec;
+
+ buffer = malloc(boot->BytesPerSec);
+ if (buffer == NULL) {
+ perror("No space for FAT");
+ return 1;
+ }
+
+ if (lseek(fs, off, SEEK_SET) != off) {
+ perror("Unable to read FAT");
+ goto err;
+ }
+
+ if (read(fs, buffer, boot->BytesPerSec) != boot->BytesPerSec) {
+ perror("Unable to read FAT");
+ goto err;
+ }
+
+ /*
+ * If we don't understand the FAT, then the file system must be
+ * assumed to be unclean.
+ */
+ if (buffer[0] != boot->Media || buffer[1] != 0xff)
+ goto err;
+ if (boot->ClustMask == CLUST16_MASK) {
+ if ((buffer[2] & 0xf8) != 0xf8 || (buffer[3] & 0x3f) != 0x3f)
+ goto err;
+ } else {
+ if (buffer[2] != 0xff || (buffer[3] & 0x0f) != 0x0f
+ || (buffer[4] & 0xf8) != 0xf8 || buffer[5] != 0xff
+ || buffer[6] != 0xff || (buffer[7] & 0x03) != 0x03)
+ goto err;
+ }
+
+ /*
+ * Now check the actual clean flag (and the no-error flag).
+ */
+ if (boot->ClustMask == CLUST16_MASK) {
+ if ((buffer[3] & 0xc0) == 0xc0)
+ ret = 1;
+ } else {
+ if ((buffer[7] & 0x0c) == 0x0c)
+ ret = 1;
+ }
+
+err:
+ free(buffer);
+ return ret;
+}
+
+/*
+ * Check a cluster number for valid value
+ */
+static int
+checkclnum(struct bootblock *boot, int fat, cl_t cl, cl_t *next)
+{
+ if (*next >= (CLUST_RSRVD&boot->ClustMask))
+ *next |= ~boot->ClustMask;
+ if (*next == CLUST_FREE) {
+ boot->NumFree++;
+ return FSOK;
+ }
+ if (*next == CLUST_BAD) {
+ boot->NumBad++;
+ return FSOK;
+ }
+ if (*next < CLUST_FIRST
+ || (*next >= boot->NumClusters && *next < CLUST_EOFS)) {
+ pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n",
+ cl, fat,
+ *next < CLUST_RSRVD ? "out of range" : "reserved",
+ *next&boot->ClustMask);
+ if (ask(0, "Truncate")) {
+ *next = CLUST_EOF;
+ return FSFATMOD;
+ }
+ return FSERROR;
+ }
+ return FSOK;
+}
+
+/*
+ * Read a FAT from disk. Returns 1 if successful, 0 otherwise.
+ */
+static int
+_readfat(int fs, struct bootblock *boot, int no, u_char **buffer)
+{
+ off_t off;
+
+ *buffer = malloc(boot->FATsecs * boot->BytesPerSec);
+ if (*buffer == NULL) {
+ perror("No space for FAT");
+ return 0;
+ }
+
+ off = boot->ResSectors + no * boot->FATsecs;
+ off *= boot->BytesPerSec;
+
+ if (lseek(fs, off, SEEK_SET) != off) {
+ perror("Unable to read FAT");
+ goto err;
+ }
+
+ if (read(fs, *buffer, boot->FATsecs * boot->BytesPerSec)
+ != boot->FATsecs * boot->BytesPerSec) {
+ perror("Unable to read FAT");
+ goto err;
+ }
+
+ return 1;
+
+ err:
+ free(*buffer);
+ return 0;
+}
+
+/*
+ * Read a FAT and decode it into internal format
+ */
+int
+readfat(int fs, struct bootblock *boot, int no, struct fatEntry **fp)
+{
+ struct fatEntry *fat;
+ u_char *buffer, *p;
+ cl_t cl;
+ int ret = FSOK;
+
+ boot->NumFree = boot->NumBad = 0;
+
+ if (!_readfat(fs, boot, no, &buffer))
+ return FSFATAL;
+
+ fat = calloc(boot->NumClusters, sizeof(struct fatEntry));
+ if (fat == NULL) {
+ perror("No space for FAT");
+ free(buffer);
+ return FSFATAL;
+ }
+
+ if (buffer[0] != boot->Media
+ || buffer[1] != 0xff || buffer[2] != 0xff
+ || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff)
+ || (boot->ClustMask == CLUST32_MASK
+ && ((buffer[3]&0x0f) != 0x0f
+ || buffer[4] != 0xff || buffer[5] != 0xff
+ || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) {
+
+ /* Windows 95 OSR2 (and possibly any later) changes
+ * the FAT signature to 0xXXffff7f for FAT16 and to
+ * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the
+ * file system is dirty if it doesn't reboot cleanly.
+ * Check this special condition before errorring out.
+ */
+ if (buffer[0] == boot->Media && buffer[1] == 0xff
+ && buffer[2] == 0xff
+ && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f)
+ || (boot->ClustMask == CLUST32_MASK
+ && buffer[3] == 0x0f && buffer[4] == 0xff
+ && buffer[5] == 0xff && buffer[6] == 0xff
+ && buffer[7] == 0x07)))
+ ret |= FSDIRTY;
+ else {
+ /* just some odd byte sequence in FAT */
+
+ switch (boot->ClustMask) {
+ case CLUST32_MASK:
+ pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
+ "FAT starts with odd byte sequence",
+ buffer[0], buffer[1], buffer[2], buffer[3],
+ buffer[4], buffer[5], buffer[6], buffer[7]);
+ break;
+ case CLUST16_MASK:
+ pwarn("%s (%02x%02x%02x%02x)\n",
+ "FAT starts with odd byte sequence",
+ buffer[0], buffer[1], buffer[2], buffer[3]);
+ break;
+ default:
+ pwarn("%s (%02x%02x%02x)\n",
+ "FAT starts with odd byte sequence",
+ buffer[0], buffer[1], buffer[2]);
+ break;
+ }
+
+
+ if (ask(1, "Correct"))
+ ret |= FSFIXFAT;
+ }
+ }
+ switch (boot->ClustMask) {
+ case CLUST32_MASK:
+ p = buffer + 8;
+ break;
+ case CLUST16_MASK:
+ p = buffer + 4;
+ break;
+ default:
+ p = buffer + 3;
+ break;
+ }
+ for (cl = CLUST_FIRST; cl < boot->NumClusters;) {
+ switch (boot->ClustMask) {
+ case CLUST32_MASK:
+ fat[cl].next = p[0] + (p[1] << 8)
+ + (p[2] << 16) + (p[3] << 24);
+ fat[cl].next &= boot->ClustMask;
+ ret |= checkclnum(boot, no, cl, &fat[cl].next);
+ cl++;
+ p += 4;
+ break;
+ case CLUST16_MASK:
+ fat[cl].next = p[0] + (p[1] << 8);
+ ret |= checkclnum(boot, no, cl, &fat[cl].next);
+ cl++;
+ p += 2;
+ break;
+ default:
+ fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff;
+ ret |= checkclnum(boot, no, cl, &fat[cl].next);
+ cl++;
+ if (cl >= boot->NumClusters)
+ break;
+ fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff;
+ ret |= checkclnum(boot, no, cl, &fat[cl].next);
+ cl++;
+ p += 3;
+ break;
+ }
+ }
+
+ free(buffer);
+ *fp = fat;
+ return ret;
+}
+
+/*
+ * Get type of reserved cluster
+ */
+char *
+rsrvdcltype(cl_t cl)
+{
+ if (cl == CLUST_FREE)
+ return "free";
+ if (cl < CLUST_BAD)
+ return "reserved";
+ if (cl > CLUST_BAD)
+ return "as EOF";
+ return "bad";
+}
+
+static int
+clustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, int fatnum)
+{
+ if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) {
+ if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
+ if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD
+ && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD)
+ || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) {
+ pwarn("Cluster %u is marked %s with different indicators, ",
+ cl, rsrvdcltype(*cp1));
+ if (ask(1, "fix")) {
+ *cp2 = *cp1;
+ return FSFATMOD;
+ }
+ return FSFATAL;
+ }
+ pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %d\n",
+ cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
+ if (ask(0, "use FAT 0's entry")) {
+ *cp2 = *cp1;
+ return FSFATMOD;
+ }
+ if (ask(0, "use FAT %d's entry", fatnum)) {
+ *cp1 = *cp2;
+ return FSFATMOD;
+ }
+ return FSFATAL;
+ }
+ pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n",
+ cl, rsrvdcltype(*cp1), *cp2, fatnum);
+ if (ask(0, "Use continuation from FAT %d", fatnum)) {
+ *cp1 = *cp2;
+ return FSFATMOD;
+ }
+ if (ask(0, "Use mark from FAT 0")) {
+ *cp2 = *cp1;
+ return FSFATMOD;
+ }
+ return FSFATAL;
+ }
+ if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
+ pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %d\n",
+ cl, *cp1, rsrvdcltype(*cp2), fatnum);
+ if (ask(0, "Use continuation from FAT 0")) {
+ *cp2 = *cp1;
+ return FSFATMOD;
+ }
+ if (ask(0, "Use mark from FAT %d", fatnum)) {
+ *cp1 = *cp2;
+ return FSFATMOD;
+ }
+ return FSERROR;
+ }
+ pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %d\n",
+ cl, *cp1, *cp2, fatnum);
+ if (ask(0, "Use continuation from FAT 0")) {
+ *cp2 = *cp1;
+ return FSFATMOD;
+ }
+ if (ask(0, "Use continuation from FAT %d", fatnum)) {
+ *cp1 = *cp2;
+ return FSFATMOD;
+ }
+ return FSERROR;
+}
+
+/*
+ * Compare two FAT copies in memory. Resolve any conflicts and merge them
+ * into the first one.
+ */
+int
+comparefat(struct bootblock *boot, struct fatEntry *first,
+ struct fatEntry *second, int fatnum)
+{
+ cl_t cl;
+ int ret = FSOK;
+
+ for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++)
+ if (first[cl].next != second[cl].next)
+ ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum);
+ return ret;
+}
+
+void
+clearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head)
+{
+ cl_t p, q;
+
+ for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) {
+ if (fat[p].head != head)
+ break;
+ q = fat[p].next;
+ fat[p].next = fat[p].head = CLUST_FREE;
+ fat[p].length = 0;
+ }
+}
+
+int
+tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *trunc)
+{
+ if (ask(0, "Clear chain starting at %u", head)) {
+ clearchain(boot, fat, head);
+ return FSFATMOD;
+ } else if (ask(0, "Truncate")) {
+ *trunc = CLUST_EOF;
+ return FSFATMOD;
+ } else
+ return FSERROR;
+}
+
+/*
+ * Check a complete FAT in-memory for crosslinks
+ */
+int
+checkfat(struct bootblock *boot, struct fatEntry *fat)
+{
+ cl_t head, p, h, n;
+ u_int len;
+ int ret = 0;
+ int conf;
+
+ /*
+ * pass 1: figure out the cluster chains.
+ */
+ for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
+ /* find next untravelled chain */
+ if (fat[head].head != 0 /* cluster already belongs to some chain */
+ || fat[head].next == CLUST_FREE
+ || fat[head].next == CLUST_BAD)
+ continue; /* skip it. */
+
+ /* follow the chain and mark all clusters on the way */
+ for (len = 0, p = head;
+ p >= CLUST_FIRST && p < boot->NumClusters;
+ p = fat[p].next) {
+ fat[p].head = head;
+ len++;
+ }
+
+ /* the head record gets the length */
+ fat[head].length = fat[head].next == CLUST_FREE ? 0 : len;
+ }
+
+ /*
+ * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because
+ * we didn't know the real start of the chain then - would have treated partial
+ * chains as interlinked with their main chain)
+ */
+ for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
+ /* find next untravelled chain */
+ if (fat[head].head != head)
+ continue;
+
+ /* follow the chain to its end (hopefully) */
+ for (p = head;
+ (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters;
+ p = n)
+ if (fat[n].head != head)
+ break;
+ if (n >= CLUST_EOFS)
+ continue;
+
+ if (n == CLUST_FREE || n >= CLUST_RSRVD) {
+ pwarn("Cluster chain starting at %u ends with cluster marked %s\n",
+ head, rsrvdcltype(n));
+ ret |= tryclear(boot, fat, head, &fat[p].next);
+ continue;
+ }
+ if (n < CLUST_FIRST || n >= boot->NumClusters) {
+ pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n",
+ head, n);
+ ret |= tryclear(boot, fat, head, &fat[p].next);
+ continue;
+ }
+ pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n",
+ head, fat[n].head, n);
+ conf = tryclear(boot, fat, head, &fat[p].next);
+ if (ask(0, "Clear chain starting at %u", h = fat[n].head)) {
+ if (conf == FSERROR) {
+ /*
+ * Transfer the common chain to the one not cleared above.
+ */
+ for (p = n;
+ p >= CLUST_FIRST && p < boot->NumClusters;
+ p = fat[p].next) {
+ if (h != fat[p].head) {
+ /*
+ * Have to reexamine this chain.
+ */
+ head--;
+ break;
+ }
+ fat[p].head = head;
+ }
+ }
+ clearchain(boot, fat, h);
+ conf |= FSFATMOD;
+ }
+ ret |= conf;
+ }
+
+ return ret;
+}
+
+/*
+ * Write out FATs encoding them from the internal format
+ */
+int
+writefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat)
+{
+ u_char *buffer, *p;
+ cl_t cl;
+ int i;
+ u_int32_t fatsz;
+ off_t off;
+ int ret = FSOK;
+
+ buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec);
+ if (buffer == NULL) {
+ perror("No space for FAT");
+ return FSFATAL;
+ }
+ memset(buffer, 0, fatsz);
+ boot->NumFree = 0;
+ p = buffer;
+ if (correct_fat) {
+ *p++ = (u_char)boot->Media;
+ *p++ = 0xff;
+ *p++ = 0xff;
+ switch (boot->ClustMask) {
+ case CLUST16_MASK:
+ *p++ = 0xff;
+ break;
+ case CLUST32_MASK:
+ *p++ = 0x0f;
+ *p++ = 0xff;
+ *p++ = 0xff;
+ *p++ = 0xff;
+ *p++ = 0x0f;
+ break;
+ }
+ } else {
+ /* use same FAT signature as the old FAT has */
+ int count;
+ u_char *old_fat;
+
+ switch (boot->ClustMask) {
+ case CLUST32_MASK:
+ count = 8;
+ break;
+ case CLUST16_MASK:
+ count = 4;
+ break;
+ default:
+ count = 3;
+ break;
+ }
+
+ if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0,
+ &old_fat)) {
+ free(buffer);
+ return FSFATAL;
+ }
+
+ memcpy(p, old_fat, count);
+ free(old_fat);
+ p += count;
+ }
+
+ for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) {
+ switch (boot->ClustMask) {
+ case CLUST32_MASK:
+ if (fat[cl].next == CLUST_FREE)
+ boot->NumFree++;
+ *p++ = (u_char)fat[cl].next;
+ *p++ = (u_char)(fat[cl].next >> 8);
+ *p++ = (u_char)(fat[cl].next >> 16);
+ *p &= 0xf0;
+ *p++ |= (fat[cl].next >> 24)&0x0f;
+ break;
+ case CLUST16_MASK:
+ if (fat[cl].next == CLUST_FREE)
+ boot->NumFree++;
+ *p++ = (u_char)fat[cl].next;
+ *p++ = (u_char)(fat[cl].next >> 8);
+ break;
+ default:
+ if (fat[cl].next == CLUST_FREE)
+ boot->NumFree++;
+ if (cl + 1 < boot->NumClusters
+ && fat[cl + 1].next == CLUST_FREE)
+ boot->NumFree++;
+ *p++ = (u_char)fat[cl].next;
+ *p++ = (u_char)((fat[cl].next >> 8) & 0xf)
+ |(u_char)(fat[cl+1].next << 4);
+ *p++ = (u_char)(fat[++cl].next >> 4);
+ break;
+ }
+ }
+ for (i = 0; i < boot->FATs; i++) {
+ off = boot->ResSectors + i * boot->FATsecs;
+ off *= boot->BytesPerSec;
+ if (lseek(fs, off, SEEK_SET) != off
+ || write(fs, buffer, fatsz) != fatsz) {
+ perror("Unable to write FAT");
+ ret = FSFATAL; /* Return immediately? XXX */
+ }
+ }
+ free(buffer);
+ return ret;
+}
+
+/*
+ * Check a complete in-memory FAT for lost cluster chains
+ */
+int
+checklost(int dosfs, struct bootblock *boot, struct fatEntry *fat)
+{
+ cl_t head;
+ int mod = FSOK;
+ int ret;
+
+ for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
+ /* find next untravelled chain */
+ if (fat[head].head != head
+ || fat[head].next == CLUST_FREE
+ || (fat[head].next >= CLUST_RSRVD
+ && fat[head].next < CLUST_EOFS)
+ || (fat[head].flags & FAT_USED))
+ continue;
+
+ pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n",
+ head, fat[head].length);
+ mod |= ret = reconnect(dosfs, boot, fat, head);
+ if (mod & FSFATAL)
+ break;
+ if (ret == FSERROR && ask(0, "Clear")) {
+ clearchain(boot, fat, head);
+ mod |= FSFATMOD;
+ }
+ }
+ finishlf();
+
+ if (boot->FSInfo) {
+ ret = 0;
+ if (boot->FSFree != boot->NumFree) {
+ pwarn("Free space in FSInfo block (%d) not correct (%d)\n",
+ boot->FSFree, boot->NumFree);
+ if (ask(1, "fix")) {
+ boot->FSFree = boot->NumFree;
+ ret = 1;
+ }
+ }
+ if (boot->NumFree && fat[boot->FSNext].next != CLUST_FREE) {
+ pwarn("Next free cluster in FSInfo block (%u) not free\n",
+ boot->FSNext);
+ if (ask(1, "fix"))
+ for (head = CLUST_FIRST; head < boot->NumClusters; head++)
+ if (fat[head].next == CLUST_FREE) {
+ boot->FSNext = head;
+ ret = 1;
+ break;
+ }
+ }
+ if (ret)
+ mod |= writefsinfo(dosfs, boot);
+ }
+
+ return mod;
+}
diff --git a/sbin/fsck_msdosfs/fsck_msdosfs.8 b/sbin/fsck_msdosfs/fsck_msdosfs.8
new file mode 100644
index 0000000..8631210
--- /dev/null
+++ b/sbin/fsck_msdosfs/fsck_msdosfs.8
@@ -0,0 +1,126 @@
+.\" $NetBSD: fsck_msdos.8,v 1.9 1997/10/17 11:19:58 ws Exp $
+.\"
+.\" Copyright (C) 1995 Wolfgang Solfrank
+.\" Copyright (c) 1995 Martin Husemann
+.\"
+.\" 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 Martin Husemann
+.\" and Wolfgang Solfrank.
+.\" 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 THE AUTHORS ``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 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 13, 1995
+.Dt FSCK_MSDOSFS 8
+.Os
+.Sh NAME
+.Nm fsck_msdosfs
+.Nd DOS/Windows (FAT) file system consistency checker
+.Sh SYNOPSIS
+.Nm
+.Fl p
+.Op Fl f
+.Ar filesystem ...
+.Nm
+.Op Fl ny
+.Ar filesystem ...
+.Sh DESCRIPTION
+The
+.Nm
+utility verifies and repairs
+.Tn FAT
+file systems (more commonly known
+as
+.Tn DOS
+file systems).
+.Pp
+The first form of
+.Nm
+preens the specified file systems.
+It is normally started by
+.Xr fsck 8
+run from
+.Pa /etc/rc
+during automatic reboot, when a FAT file system is detected.
+When preening file systems,
+.Nm
+will fix common inconsistencies non-interactively.
+If more serious problems are found,
+.Nm
+does not try to fix them, indicates that it was not
+successful, and exits.
+.Pp
+The second form of
+.Nm
+checks the specified file systems and tries to repair all
+detected inconsistencies, requesting confirmation before
+making any changes.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl F
+Compatibility with the wrapper
+.Xr fsck 8
+which seeks to determine whether the file system needs to be cleaned
+immediately in foreground, or if its cleaning can be deferred to background.
+FAT (MS-DOS) file systems must always be cleaned in the foreground.
+A non-zero exit code is always returned for this option.
+.It Fl f
+Force
+.Nm
+to check
+.Dq clean
+file systems when preening.
+.It Fl n
+Causes
+.Nm
+to assume
+.Dq Li no
+as the answer to all operator
+questions, except
+.Dq Li CONTINUE? .
+.It Fl p
+Preen the specified file systems.
+.It Fl y
+Causes
+.Nm
+to assume
+.Dq Li yes
+as the answer to all operator questions.
+.El
+.Sh SEE ALSO
+.Xr fsck 8 ,
+.Xr fsck_ffs 8 ,
+.Xr mount_msdosfs 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 4.4 .
+.Sh BUGS
+The
+.Nm
+utility is
+.Ud .
diff --git a/sbin/fsck_msdosfs/main.c b/sbin/fsck_msdosfs/main.c
new file mode 100644
index 0000000..eacc792
--- /dev/null
+++ b/sbin/fsck_msdosfs/main.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 1995 Wolfgang Solfrank
+ * Copyright (c) 1995 Martin Husemann
+ *
+ * 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 Martin Husemann
+ * and Wolfgang Solfrank.
+ * 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 THE AUTHORS ``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 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>
+#ifndef lint
+__RCSID("$NetBSD: main.c,v 1.10 1997/10/01 02:18:14 enami Exp $");
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include "fsutil.h"
+#include "ext.h"
+
+int alwaysno; /* assume "no" for all questions */
+int alwaysyes; /* assume "yes" for all questions */
+int preen; /* set when preening */
+int rdonly; /* device is opened read only (supersedes above) */
+int skipclean; /* skip clean file systems if preening */
+
+static void usage(void) __dead2;
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "%s\n%s\n",
+ "usage: fsck_msdosfs -p [-f] filesystem ...",
+ " fsck_msdosfs [-ny] filesystem ...");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ret = 0, erg;
+ int ch;
+
+ skipclean = 1;
+ while ((ch = getopt(argc, argv, "fFnpy")) != -1) {
+ switch (ch) {
+ case 'f':
+ skipclean = 0;
+ break;
+ case 'F':
+ /*
+ * We can never run in the background. We must exit
+ * silently with a nonzero exit code so that fsck(8)
+ * can probe our support for -F. The exit code
+ * doesn't really matter, but we use an unusual one
+ * in case someone tries -F directly. The -F flag
+ * is intentionally left out of the usage message.
+ */
+ exit(5);
+ case 'n':
+ alwaysno = 1;
+ alwaysyes = preen = 0;
+ break;
+ case 'y':
+ alwaysyes = 1;
+ alwaysno = preen = 0;
+ break;
+
+ case 'p':
+ preen = 1;
+ alwaysyes = alwaysno = 0;
+ break;
+
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!argc)
+ usage();
+
+ while (--argc >= 0) {
+ setcdevname(*argv, preen);
+ erg = checkfilesys(*argv++);
+ if (erg > ret)
+ ret = erg;
+ }
+
+ return ret;
+}
+
+
+/*VARARGS*/
+int
+ask(int def, const char *fmt, ...)
+{
+ va_list ap;
+
+ char prompt[256];
+ int c;
+
+ if (preen) {
+ if (rdonly)
+ def = 0;
+ if (def)
+ printf("FIXED\n");
+ return def;
+ }
+
+ va_start(ap, fmt);
+ vsnprintf(prompt, sizeof(prompt), fmt, ap);
+ if (alwaysyes || rdonly) {
+ printf("%s? %s\n", prompt, rdonly ? "no" : "yes");
+ return !rdonly;
+ }
+ do {
+ printf("%s? [yn] ", prompt);
+ fflush(stdout);
+ c = getchar();
+ while (c != '\n' && getchar() != '\n')
+ if (feof(stdin))
+ return 0;
+ } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
+ return c == 'y' || c == 'Y';
+}
diff --git a/sbin/fsdb/Makefile b/sbin/fsdb/Makefile
new file mode 100644
index 0000000..65333ef
--- /dev/null
+++ b/sbin/fsdb/Makefile
@@ -0,0 +1,16 @@
+# $NetBSD: Makefile,v 1.1.1.1 1995/10/08 23:08:36 thorpej Exp $
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= fsdb
+MAN= fsdb.8
+SRCS= fsdb.c fsdbutil.c \
+ dir.c ea.c fsutil.c inode.c pass1.c pass1b.c pass2.c pass3.c pass4.c \
+ pass5.c setup.c utilities.c ffs_subr.c ffs_tables.c
+CFLAGS+= -I${.CURDIR}/../fsck_ffs
+WARNS?= 2
+LDADD= -ledit -ltermcap
+DPADD= ${LIBEDIT} ${LIBTERMCAP}
+.PATH: ${.CURDIR}/../fsck_ffs ${.CURDIR}/../../sys/ufs/ffs
+
+.include <bsd.prog.mk>
diff --git a/sbin/fsdb/fsdb.8 b/sbin/fsdb/fsdb.8
new file mode 100644
index 0000000..71d8b4e
--- /dev/null
+++ b/sbin/fsdb/fsdb.8
@@ -0,0 +1,258 @@
+.\" $NetBSD: fsdb.8,v 1.2 1995/10/08 23:18:08 thorpej Exp $
+.\"
+.\" Copyright (c) 1995 John T. Kohl
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 14, 1995
+.Dt FSDB 8
+.Os
+.Sh NAME
+.Nm fsdb
+.Nd FFS debugging/editing tool
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl f
+.Op Fl r
+.Ar fsname
+.Sh DESCRIPTION
+The
+.Nm
+utility opens
+.Ar fsname
+(usually a raw disk partition) and runs a command loop
+allowing manipulation of the file system's inode data.
+You are prompted
+to enter a command with
+.Ic "fsdb (inum X)>"
+where
+.Va X
+is the currently selected i-number.
+The initial selected inode is the
+root of the file system (i-number 2).
+The command processor uses the
+.Xr editline 3
+library, so you can use command line editing to reduce typing if desired.
+When you exit the command loop, the file system superblock is marked
+dirty and any buffered blocks are written to the file system.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Enable additional debugging output (which comes primarily from
+.Xr fsck 8 Ns -derived
+code).
+.It Fl f
+Left for historical reasons and has no meaning.
+.It Fl r
+Open the file system read/only, and disables all commands that would
+write to it.
+.El
+.Sh COMMANDS
+Besides the built-in
+.Xr editline 3
+commands,
+.Nm
+supports these commands:
+.Pp
+.Bl -tag -width indent -compact
+.It Cm help
+Print out the list of accepted commands.
+.Pp
+.It Cm inode Ar i-number
+Select inode
+.Ar i-number
+as the new current inode.
+.Pp
+.It Cm back
+Revert to the previously current inode.
+.Pp
+.It Cm clri Ar i-number
+Clear
+.Ar i-number .
+.Pp
+.It Cm lookup Ar name
+.It Cm cd Ar name
+Find
+.Ar name
+in the current directory and make its inode the current inode.
+.Ar Name
+may be a multi-component name or may begin with slash to indicate that
+the root inode should be used to start the lookup.
+If some component
+along the pathname is not found, the last valid directory encountered is
+left as the active inode.
+This command is valid only if the starting inode is a directory.
+.Pp
+.It Cm active
+.It Cm print
+Print out the active inode.
+.Pp
+.It Cm blocks
+Print out the block list of the active inode.
+Note that the printout can become long for large files, since all
+indirect block pointers will also be printed.
+.It Cm uplink
+Increment the active inode's link count.
+.Pp
+.It Cm downlink
+Decrement the active inode's link count.
+.Pp
+.It Cm linkcount Ar number
+Set the active inode's link count to
+.Ar number .
+.Pp
+.It Cm ls
+List the current inode's directory entries.
+This command is valid only
+if the current inode is a directory.
+.Pp
+.It Cm rm Ar name
+.It Cm del Ar name
+Remove the entry
+.Ar name
+from the current directory inode.
+This command is valid only
+if the current inode is a directory.
+.Pp
+.It Cm ln Ar ino Ar name
+Create a link to inode
+.Ar ino
+under the name
+.Ar name
+in the current directory inode.
+This command is valid only
+if the current inode is a directory.
+.Pp
+.It Cm chinum Ar dirslot Ar inum
+Change the i-number in directory entry
+.Ar dirslot
+to
+.Ar inum .
+.Pp
+.It Cm chname Ar dirslot Ar name
+Change the name in directory entry
+.Ar dirslot
+to
+.Ar name .
+This command cannot expand a directory entry.
+You can only rename an
+entry if the name will fit into the existing directory slot.
+.Pp
+.It Cm chtype Ar type
+Change the type of the current inode to
+.Ar type .
+.Ar Type
+may be one of:
+.Em file ,
+.Em dir ,
+.Em socket ,
+or
+.Em fifo .
+.Pp
+.It Cm chmod Ar mode
+Change the mode bits of the current inode to
+.Ar mode .
+You cannot change the file type with this subcommand; use
+.Ic chtype
+to do that.
+.Pp
+.It Cm chflags Ar flags
+Change the file flags of the current inode to
+.Ar flags .
+.Pp
+.It Cm chown Ar uid
+Change the owner of the current inode to
+.Ar uid .
+.Pp
+.It Cm chgrp Ar gid
+Change the group of the current inode to
+.Ar gid .
+.Pp
+.It Cm chgen Ar gen
+Change the generation number of the current inode to
+.Ar gen .
+.Pp
+.It Cm mtime Ar time
+.It Cm ctime Ar time
+.It Cm atime Ar time
+Change the modification, change, or access time (respectively) on the
+current inode to
+.Ar time .
+.Ar Time
+should be in the format
+.Em YYYYMMDDHHMMSS[.nsec]
+where
+.Em nsec
+is an optional nanosecond specification.
+If no nanoseconds are specified, the
+.Va mtimensec ,
+.Va ctimensec ,
+or
+.Va atimensec
+field will be set to zero.
+.Pp
+.It Cm quit , q , exit , Em <EOF>
+Exit the program.
+.El
+.Sh SEE ALSO
+.Xr editline 3 ,
+.Xr fs 5 ,
+.Xr clri 8 ,
+.Xr fsck 8
+.Sh HISTORY
+The
+.Nm
+utility uses the source code for
+.Xr fsck 8
+to implement most of the file system manipulation code.
+The remainder of
+.Nm
+first appeared in
+.Nx ,
+written by
+.An John T. Kohl .
+.Pp
+.An Peter Wemm
+ported it to
+.Fx .
+.Sh BUGS
+Manipulation of ``short'' symlinks has no effect.
+In particular, one should not
+try changing a symlink's type.
+.Pp
+You must specify modes as numbers rather than symbolic names.
+.Pp
+There are a bunch of other things that you might want to do which
+.Nm
+does not implement.
+.Sh WARNING
+Use this tool with extreme caution--you can damage an FFS file system
+beyond what
+.Xr fsck 8
+can repair.
diff --git a/sbin/fsdb/fsdb.c b/sbin/fsdb/fsdb.c
new file mode 100644
index 0000000..d9828bd
--- /dev/null
+++ b/sbin/fsdb/fsdb.c
@@ -0,0 +1,918 @@
+/* $NetBSD: fsdb.c,v 1.2 1995/10/08 23:18:10 thorpej Exp $ */
+
+/*
+ * Copyright (c) 1995 John T. Kohl
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <err.h>
+#include <grp.h>
+#include <histedit.h>
+#include <pwd.h>
+#include <string.h>
+#include <timeconv.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+#include "fsdb.h"
+#include "fsck.h"
+
+static void usage(void) __dead2;
+int cmdloop(void);
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: fsdb [-d] [-f] [-r] fsname\n");
+ exit(1);
+}
+
+int returntosingle;
+char nflag;
+
+/*
+ * We suck in lots of fsck code, and just pick & choose the stuff we want.
+ *
+ * fsreadfd is set up to read from the file system, fswritefd to write to
+ * the file system.
+ */
+int
+main(int argc, char *argv[])
+{
+ int ch, rval;
+ char *fsys = NULL;
+
+ while (-1 != (ch = getopt(argc, argv, "fdr"))) {
+ switch (ch) {
+ case 'f':
+ /* The -f option is left for historical
+ * reasons and has no meaning.
+ */
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 'r':
+ nflag++; /* "no" in fsck, readonly for us */
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ usage();
+ else
+ fsys = argv[0];
+
+ sblock_init();
+ if (!setup(fsys))
+ errx(1, "cannot set up file system `%s'", fsys);
+ printf("%s file system `%s'\nLast Mounted on %s\n",
+ nflag? "Examining": "Editing", fsys, sblock.fs_fsmnt);
+ rval = cmdloop();
+ if (!nflag) {
+ sblock.fs_clean = 0; /* mark it dirty */
+ sbdirty();
+ ckfini(0);
+ printf("*** FILE SYSTEM MARKED DIRTY\n");
+ printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n");
+ printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n");
+ }
+ exit(rval);
+}
+
+#define CMDFUNC(func) int func(int argc, char *argv[])
+#define CMDFUNCSTART(func) int func(int argc, char *argv[])
+
+CMDFUNC(helpfn);
+CMDFUNC(focus); /* focus on inode */
+CMDFUNC(active); /* print active inode */
+CMDFUNC(blocks); /* print blocks for active inode */
+CMDFUNC(focusname); /* focus by name */
+CMDFUNC(zapi); /* clear inode */
+CMDFUNC(uplink); /* incr link */
+CMDFUNC(downlink); /* decr link */
+CMDFUNC(linkcount); /* set link count */
+CMDFUNC(quit); /* quit */
+CMDFUNC(ls); /* list directory */
+CMDFUNC(rm); /* remove name */
+CMDFUNC(ln); /* add name */
+CMDFUNC(newtype); /* change type */
+CMDFUNC(chmode); /* change mode */
+CMDFUNC(chlen); /* change length */
+CMDFUNC(chaflags); /* change flags */
+CMDFUNC(chgen); /* change generation */
+CMDFUNC(chowner); /* change owner */
+CMDFUNC(chgroup); /* Change group */
+CMDFUNC(back); /* pop back to last ino */
+CMDFUNC(chmtime); /* Change mtime */
+CMDFUNC(chctime); /* Change ctime */
+CMDFUNC(chatime); /* Change atime */
+CMDFUNC(chinum); /* Change inode # of dirent */
+CMDFUNC(chname); /* Change dirname of dirent */
+
+struct cmdtable cmds[] = {
+ { "help", "Print out help", 1, 1, FL_RO, helpfn },
+ { "?", "Print out help", 1, 1, FL_RO, helpfn },
+ { "inode", "Set active inode to INUM", 2, 2, FL_RO, focus },
+ { "clri", "Clear inode INUM", 2, 2, FL_WR, zapi },
+ { "lookup", "Set active inode by looking up NAME", 2, 2, FL_RO, focusname },
+ { "cd", "Set active inode by looking up NAME", 2, 2, FL_RO, focusname },
+ { "back", "Go to previous active inode", 1, 1, FL_RO, back },
+ { "active", "Print active inode", 1, 1, FL_RO, active },
+ { "print", "Print active inode", 1, 1, FL_RO, active },
+ { "blocks", "Print block numbers of active inode", 1, 1, FL_RO, blocks },
+ { "uplink", "Increment link count", 1, 1, FL_WR, uplink },
+ { "downlink", "Decrement link count", 1, 1, FL_WR, downlink },
+ { "linkcount", "Set link count to COUNT", 2, 2, FL_WR, linkcount },
+ { "ls", "List current inode as directory", 1, 1, FL_RO, ls },
+ { "rm", "Remove NAME from current inode directory", 2, 2, FL_WR, rm },
+ { "del", "Remove NAME from current inode directory", 2, 2, FL_WR, rm },
+ { "ln", "Hardlink INO into current inode directory as NAME", 3, 3, FL_WR, ln },
+ { "chinum", "Change dir entry number INDEX to INUM", 3, 3, FL_WR, chinum },
+ { "chname", "Change dir entry number INDEX to NAME", 3, 3, FL_WR, chname },
+ { "chtype", "Change type of current inode to TYPE", 2, 2, FL_WR, newtype },
+ { "chmod", "Change mode of current inode to MODE", 2, 2, FL_WR, chmode },
+ { "chlen", "Change length of current inode to LENGTH", 2, 2, FL_WR, chlen },
+ { "chown", "Change owner of current inode to OWNER", 2, 2, FL_WR, chowner },
+ { "chgrp", "Change group of current inode to GROUP", 2, 2, FL_WR, chgroup },
+ { "chflags", "Change flags of current inode to FLAGS", 2, 2, FL_WR, chaflags },
+ { "chgen", "Change generation number of current inode to GEN", 2, 2, FL_WR, chgen },
+ { "mtime", "Change mtime of current inode to MTIME", 2, 2, FL_WR, chmtime },
+ { "ctime", "Change ctime of current inode to CTIME", 2, 2, FL_WR, chctime },
+ { "atime", "Change atime of current inode to ATIME", 2, 2, FL_WR, chatime },
+ { "quit", "Exit", 1, 1, FL_RO, quit },
+ { "q", "Exit", 1, 1, FL_RO, quit },
+ { "exit", "Exit", 1, 1, FL_RO, quit },
+ { NULL, 0, 0, 0, 0, NULL },
+};
+
+int
+helpfn(int argc, char *argv[])
+{
+ struct cmdtable *cmdtp;
+
+ printf("Commands are:\n%-10s %5s %5s %s\n",
+ "command", "min argc", "max argc", "what");
+
+ for (cmdtp = cmds; cmdtp->cmd; cmdtp++)
+ printf("%-10s %5u %5u %s\n",
+ cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt);
+ return 0;
+}
+
+char *
+prompt(EditLine *el)
+{
+ static char pstring[64];
+ snprintf(pstring, sizeof(pstring), "fsdb (inum: %d)> ", curinum);
+ return pstring;
+}
+
+
+int
+cmdloop(void)
+{
+ char *line;
+ const char *elline;
+ int cmd_argc, rval = 0, known;
+#define scratch known
+ char **cmd_argv;
+ struct cmdtable *cmdp;
+ History *hist;
+ EditLine *elptr;
+ HistEvent he;
+
+ curinode = ginode(ROOTINO);
+ curinum = ROOTINO;
+ printactive(0);
+
+ hist = history_init();
+ history(hist, &he, H_SETSIZE, 100); /* 100 elt history buffer */
+
+ elptr = el_init("fsdb", stdin, stdout, stderr);
+ el_set(elptr, EL_EDITOR, "emacs");
+ el_set(elptr, EL_PROMPT, prompt);
+ el_set(elptr, EL_HIST, history, hist);
+ el_source(elptr, NULL);
+
+ while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) {
+ if (debug)
+ printf("command `%s'\n", elline);
+
+ history(hist, &he, H_ENTER, elline);
+
+ line = strdup(elline);
+ cmd_argv = crack(line, &cmd_argc);
+ /*
+ * el_parse returns -1 to signal that it's not been handled
+ * internally.
+ */
+ if (el_parse(elptr, cmd_argc, (const char **)cmd_argv) != -1)
+ continue;
+ if (cmd_argc) {
+ known = 0;
+ for (cmdp = cmds; cmdp->cmd; cmdp++) {
+ if (!strcmp(cmdp->cmd, cmd_argv[0])) {
+ if ((cmdp->flags & FL_WR) == FL_WR && nflag)
+ warnx("`%s' requires write access", cmd_argv[0]),
+ rval = 1;
+ else if (cmd_argc >= cmdp->minargc &&
+ cmd_argc <= cmdp->maxargc)
+ rval = (*cmdp->handler)(cmd_argc, cmd_argv);
+ else if (cmd_argc >= cmdp->minargc) {
+ strcpy(line, elline);
+ cmd_argv = recrack(line, &cmd_argc, cmdp->maxargc);
+ rval = (*cmdp->handler)(cmd_argc, cmd_argv);
+ } else
+ rval = argcount(cmdp, cmd_argc, cmd_argv);
+ known = 1;
+ break;
+ }
+ }
+ if (!known)
+ warnx("unknown command `%s'", cmd_argv[0]), rval = 1;
+ } else
+ rval = 0;
+ free(line);
+ if (rval < 0)
+ /* user typed "quit" */
+ return 0;
+ if (rval)
+ warnx("rval was %d", rval);
+ }
+ el_end(elptr);
+ history_end(hist);
+ return rval;
+}
+
+union dinode *curinode;
+ino_t curinum, ocurrent;
+
+#define GETINUM(ac,inum) inum = strtoul(argv[ac], &cp, 0); \
+ if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \
+ printf("inode %d out of range; range is [%d,%d]\n", \
+ inum, ROOTINO, maxino); \
+ return 1; \
+ }
+
+/*
+ * Focus on given inode number
+ */
+CMDFUNCSTART(focus)
+{
+ ino_t inum;
+ char *cp;
+
+ GETINUM(1,inum);
+ curinode = ginode(inum);
+ ocurrent = curinum;
+ curinum = inum;
+ printactive(0);
+ return 0;
+}
+
+CMDFUNCSTART(back)
+{
+ curinum = ocurrent;
+ curinode = ginode(curinum);
+ printactive(0);
+ return 0;
+}
+
+CMDFUNCSTART(zapi)
+{
+ ino_t inum;
+ union dinode *dp;
+ char *cp;
+
+ GETINUM(1,inum);
+ dp = ginode(inum);
+ clearinode(dp);
+ inodirty();
+ if (curinode) /* re-set after potential change */
+ curinode = ginode(curinum);
+ return 0;
+}
+
+CMDFUNCSTART(active)
+{
+ printactive(0);
+ return 0;
+}
+
+CMDFUNCSTART(blocks)
+{
+ printactive(1);
+ return 0;
+}
+
+CMDFUNCSTART(quit)
+{
+ return -1;
+}
+
+CMDFUNCSTART(uplink)
+{
+ if (!checkactive())
+ return 1;
+ DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) + 1);
+ printf("inode %d link count now %d\n", curinum, DIP(curinode, di_nlink));
+ inodirty();
+ return 0;
+}
+
+CMDFUNCSTART(downlink)
+{
+ if (!checkactive())
+ return 1;
+ DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) - 1);
+ printf("inode %d link count now %d\n", curinum, DIP(curinode, di_nlink));
+ inodirty();
+ return 0;
+}
+
+const char *typename[] = {
+ "unknown",
+ "fifo",
+ "char special",
+ "unregistered #3",
+ "directory",
+ "unregistered #5",
+ "blk special",
+ "unregistered #7",
+ "regular",
+ "unregistered #9",
+ "symlink",
+ "unregistered #11",
+ "socket",
+ "unregistered #13",
+ "whiteout",
+};
+
+int slot;
+
+int
+scannames(struct inodesc *idesc)
+{
+ struct direct *dirp = idesc->id_dirp;
+
+ printf("slot %d ino %d reclen %d: %s, `%.*s'\n",
+ slot++, dirp->d_ino, dirp->d_reclen, typename[dirp->d_type],
+ dirp->d_namlen, dirp->d_name);
+ return (KEEPON);
+}
+
+CMDFUNCSTART(ls)
+{
+ struct inodesc idesc;
+ checkactivedir(); /* let it go on anyway */
+
+ slot = 0;
+ idesc.id_number = curinum;
+ idesc.id_func = scannames;
+ idesc.id_type = DATA;
+ idesc.id_fix = IGNORE;
+ ckinode(curinode, &idesc);
+ curinode = ginode(curinum);
+
+ return 0;
+}
+
+int findino(struct inodesc *idesc); /* from fsck */
+static int dolookup(char *name);
+
+static int
+dolookup(char *name)
+{
+ struct inodesc idesc;
+
+ if (!checkactivedir())
+ return 0;
+ idesc.id_number = curinum;
+ idesc.id_func = findino;
+ idesc.id_name = name;
+ idesc.id_type = DATA;
+ idesc.id_fix = IGNORE;
+ if (ckinode(curinode, &idesc) & FOUND) {
+ curinum = idesc.id_parent;
+ curinode = ginode(curinum);
+ printactive(0);
+ return 1;
+ } else {
+ warnx("name `%s' not found in current inode directory", name);
+ return 0;
+ }
+}
+
+CMDFUNCSTART(focusname)
+{
+ char *p, *val;
+
+ if (!checkactive())
+ return 1;
+
+ ocurrent = curinum;
+
+ if (argv[1][0] == '/') {
+ curinum = ROOTINO;
+ curinode = ginode(ROOTINO);
+ } else {
+ if (!checkactivedir())
+ return 1;
+ }
+ for (p = argv[1]; p != NULL;) {
+ while ((val = strsep(&p, "/")) != NULL && *val == '\0');
+ if (val) {
+ printf("component `%s': ", val);
+ fflush(stdout);
+ if (!dolookup(val)) {
+ curinode = ginode(curinum);
+ return(1);
+ }
+ }
+ }
+ return 0;
+}
+
+CMDFUNCSTART(ln)
+{
+ ino_t inum;
+ int rval;
+ char *cp;
+
+ GETINUM(1,inum);
+
+ if (!checkactivedir())
+ return 1;
+ rval = makeentry(curinum, inum, argv[2]);
+ if (rval)
+ printf("Ino %d entered as `%s'\n", inum, argv[2]);
+ else
+ printf("could not enter name? weird.\n");
+ curinode = ginode(curinum);
+ return rval;
+}
+
+CMDFUNCSTART(rm)
+{
+ int rval;
+
+ if (!checkactivedir())
+ return 1;
+ rval = changeino(curinum, argv[1], 0);
+ if (rval & ALTERED) {
+ printf("Name `%s' removed\n", argv[1]);
+ return 0;
+ } else {
+ printf("could not remove name ('%s')? weird.\n", argv[1]);
+ return 1;
+ }
+}
+
+long slotcount, desired;
+
+int
+chinumfunc(struct inodesc *idesc)
+{
+ struct direct *dirp = idesc->id_dirp;
+
+ if (slotcount++ == desired) {
+ dirp->d_ino = idesc->id_parent;
+ return STOP|ALTERED|FOUND;
+ }
+ return KEEPON;
+}
+
+CMDFUNCSTART(chinum)
+{
+ char *cp;
+ ino_t inum;
+ struct inodesc idesc;
+
+ slotcount = 0;
+ if (!checkactivedir())
+ return 1;
+ GETINUM(2,inum);
+
+ desired = strtol(argv[1], &cp, 0);
+ if (cp == argv[1] || *cp != '\0' || desired < 0) {
+ printf("invalid slot number `%s'\n", argv[1]);
+ return 1;
+ }
+
+ idesc.id_number = curinum;
+ idesc.id_func = chinumfunc;
+ idesc.id_fix = IGNORE;
+ idesc.id_type = DATA;
+ idesc.id_parent = inum; /* XXX convenient hiding place */
+
+ if (ckinode(curinode, &idesc) & FOUND)
+ return 0;
+ else {
+ warnx("no %sth slot in current directory", argv[1]);
+ return 1;
+ }
+}
+
+int
+chnamefunc(struct inodesc *idesc)
+{
+ struct direct *dirp = idesc->id_dirp;
+ struct direct testdir;
+
+ if (slotcount++ == desired) {
+ /* will name fit? */
+ testdir.d_namlen = strlen(idesc->id_name);
+ if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) {
+ dirp->d_namlen = testdir.d_namlen;
+ strcpy(dirp->d_name, idesc->id_name);
+ return STOP|ALTERED|FOUND;
+ } else
+ return STOP|FOUND; /* won't fit, so give up */
+ }
+ return KEEPON;
+}
+
+CMDFUNCSTART(chname)
+{
+ int rval;
+ char *cp;
+ struct inodesc idesc;
+
+ slotcount = 0;
+ if (!checkactivedir())
+ return 1;
+
+ desired = strtoul(argv[1], &cp, 0);
+ if (cp == argv[1] || *cp != '\0') {
+ printf("invalid slot number `%s'\n", argv[1]);
+ return 1;
+ }
+
+ idesc.id_number = curinum;
+ idesc.id_func = chnamefunc;
+ idesc.id_fix = IGNORE;
+ idesc.id_type = DATA;
+ idesc.id_name = argv[2];
+
+ rval = ckinode(curinode, &idesc);
+ if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED))
+ return 0;
+ else if (rval & FOUND) {
+ warnx("new name `%s' does not fit in slot %s\n", argv[2], argv[1]);
+ return 1;
+ } else {
+ warnx("no %sth slot in current directory", argv[1]);
+ return 1;
+ }
+}
+
+struct typemap {
+ const char *typename;
+ int typebits;
+} typenamemap[] = {
+ {"file", IFREG},
+ {"dir", IFDIR},
+ {"socket", IFSOCK},
+ {"fifo", IFIFO},
+};
+
+CMDFUNCSTART(newtype)
+{
+ int type;
+ struct typemap *tp;
+
+ if (!checkactive())
+ return 1;
+ type = DIP(curinode, di_mode) & IFMT;
+ for (tp = typenamemap;
+ tp < &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)];
+ tp++) {
+ if (!strcmp(argv[1], tp->typename)) {
+ printf("setting type to %s\n", tp->typename);
+ type = tp->typebits;
+ break;
+ }
+ }
+ if (tp == &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)]) {
+ warnx("type `%s' not known", argv[1]);
+ warnx("try one of `file', `dir', `socket', `fifo'");
+ return 1;
+ }
+ DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~IFMT);
+ DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | type);
+ inodirty();
+ printactive(0);
+ return 0;
+}
+
+CMDFUNCSTART(chlen)
+{
+ int rval = 1;
+ long len;
+ char *cp;
+
+ if (!checkactive())
+ return 1;
+
+ len = strtol(argv[1], &cp, 0);
+ if (cp == argv[1] || *cp != '\0' || len < 0) {
+ warnx("bad length `%s'", argv[1]);
+ return 1;
+ }
+
+ DIP_SET(curinode, di_size, len);
+ inodirty();
+ printactive(0);
+ return rval;
+}
+
+CMDFUNCSTART(chmode)
+{
+ int rval = 1;
+ long modebits;
+ char *cp;
+
+ if (!checkactive())
+ return 1;
+
+ modebits = strtol(argv[1], &cp, 8);
+ if (cp == argv[1] || *cp != '\0' || (modebits & ~07777)) {
+ warnx("bad modebits `%s'", argv[1]);
+ return 1;
+ }
+
+ DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~07777);
+ DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | modebits);
+ inodirty();
+ printactive(0);
+ return rval;
+}
+
+CMDFUNCSTART(chaflags)
+{
+ int rval = 1;
+ u_long flags;
+ char *cp;
+
+ if (!checkactive())
+ return 1;
+
+ flags = strtoul(argv[1], &cp, 0);
+ if (cp == argv[1] || *cp != '\0' ) {
+ warnx("bad flags `%s'", argv[1]);
+ return 1;
+ }
+
+ if (flags > UINT_MAX) {
+ warnx("flags set beyond 32-bit range of field (%lx)\n", flags);
+ return(1);
+ }
+ DIP_SET(curinode, di_flags, flags);
+ inodirty();
+ printactive(0);
+ return rval;
+}
+
+CMDFUNCSTART(chgen)
+{
+ int rval = 1;
+ long gen;
+ char *cp;
+
+ if (!checkactive())
+ return 1;
+
+ gen = strtol(argv[1], &cp, 0);
+ if (cp == argv[1] || *cp != '\0' ) {
+ warnx("bad gen `%s'", argv[1]);
+ return 1;
+ }
+
+ if (gen > INT_MAX || gen < INT_MIN) {
+ warnx("gen set beyond 32-bit range of field (%lx)\n", gen);
+ return(1);
+ }
+ DIP_SET(curinode, di_gen, gen);
+ inodirty();
+ printactive(0);
+ return rval;
+}
+
+CMDFUNCSTART(linkcount)
+{
+ int rval = 1;
+ int lcnt;
+ char *cp;
+
+ if (!checkactive())
+ return 1;
+
+ lcnt = strtol(argv[1], &cp, 0);
+ if (cp == argv[1] || *cp != '\0' ) {
+ warnx("bad link count `%s'", argv[1]);
+ return 1;
+ }
+ if (lcnt > USHRT_MAX || lcnt < 0) {
+ warnx("max link count is %d\n", USHRT_MAX);
+ return 1;
+ }
+
+ DIP_SET(curinode, di_nlink, lcnt);
+ inodirty();
+ printactive(0);
+ return rval;
+}
+
+CMDFUNCSTART(chowner)
+{
+ int rval = 1;
+ unsigned long uid;
+ char *cp;
+ struct passwd *pwd;
+
+ if (!checkactive())
+ return 1;
+
+ uid = strtoul(argv[1], &cp, 0);
+ if (cp == argv[1] || *cp != '\0' ) {
+ /* try looking up name */
+ if ((pwd = getpwnam(argv[1]))) {
+ uid = pwd->pw_uid;
+ } else {
+ warnx("bad uid `%s'", argv[1]);
+ return 1;
+ }
+ }
+
+ DIP_SET(curinode, di_uid, uid);
+ inodirty();
+ printactive(0);
+ return rval;
+}
+
+CMDFUNCSTART(chgroup)
+{
+ int rval = 1;
+ unsigned long gid;
+ char *cp;
+ struct group *grp;
+
+ if (!checkactive())
+ return 1;
+
+ gid = strtoul(argv[1], &cp, 0);
+ if (cp == argv[1] || *cp != '\0' ) {
+ if ((grp = getgrnam(argv[1]))) {
+ gid = grp->gr_gid;
+ } else {
+ warnx("bad gid `%s'", argv[1]);
+ return 1;
+ }
+ }
+
+ DIP_SET(curinode, di_gid, gid);
+ inodirty();
+ printactive(0);
+ return rval;
+}
+
+int
+dotime(char *name, time_t *secp, int32_t *nsecp)
+{
+ char *p, *val;
+ struct tm t;
+ int32_t nsec;
+ p = strchr(name, '.');
+ if (p) {
+ *p = '\0';
+ nsec = strtoul(++p, &val, 0);
+ if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) {
+ warnx("invalid nanoseconds");
+ goto badformat;
+ }
+ } else
+ nsec = 0;
+ if (strlen(name) != 14) {
+badformat:
+ warnx("date format: YYYYMMDDHHMMSS[.nsec]");
+ return 1;
+ }
+ *nsecp = nsec;
+
+ for (p = name; *p; p++)
+ if (*p < '0' || *p > '9')
+ goto badformat;
+
+ p = name;
+#define VAL() ((*p++) - '0')
+ t.tm_year = VAL();
+ t.tm_year = VAL() + t.tm_year * 10;
+ t.tm_year = VAL() + t.tm_year * 10;
+ t.tm_year = VAL() + t.tm_year * 10 - 1900;
+ t.tm_mon = VAL();
+ t.tm_mon = VAL() + t.tm_mon * 10 - 1;
+ t.tm_mday = VAL();
+ t.tm_mday = VAL() + t.tm_mday * 10;
+ t.tm_hour = VAL();
+ t.tm_hour = VAL() + t.tm_hour * 10;
+ t.tm_min = VAL();
+ t.tm_min = VAL() + t.tm_min * 10;
+ t.tm_sec = VAL();
+ t.tm_sec = VAL() + t.tm_sec * 10;
+ t.tm_isdst = -1;
+
+ *secp = mktime(&t);
+ if (*secp == -1) {
+ warnx("date/time out of range");
+ return 1;
+ }
+ return 0;
+}
+
+CMDFUNCSTART(chmtime)
+{
+ time_t secs;
+ int32_t nsecs;
+
+ if (dotime(argv[1], &secs, &nsecs))
+ return 1;
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ curinode->dp1.di_mtime = _time_to_time32(secs);
+ else
+ curinode->dp2.di_mtime = _time_to_time64(secs);
+ DIP_SET(curinode, di_mtimensec, nsecs);
+ inodirty();
+ printactive(0);
+ return 0;
+}
+
+CMDFUNCSTART(chatime)
+{
+ time_t secs;
+ int32_t nsecs;
+
+ if (dotime(argv[1], &secs, &nsecs))
+ return 1;
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ curinode->dp1.di_atime = _time_to_time32(secs);
+ else
+ curinode->dp2.di_atime = _time_to_time64(secs);
+ DIP_SET(curinode, di_atimensec, nsecs);
+ inodirty();
+ printactive(0);
+ return 0;
+}
+
+CMDFUNCSTART(chctime)
+{
+ time_t secs;
+ int32_t nsecs;
+
+ if (dotime(argv[1], &secs, &nsecs))
+ return 1;
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ curinode->dp1.di_ctime = _time_to_time32(secs);
+ else
+ curinode->dp2.di_ctime = _time_to_time64(secs);
+ DIP_SET(curinode, di_ctimensec, nsecs);
+ inodirty();
+ printactive(0);
+ return 0;
+}
diff --git a/sbin/fsdb/fsdb.h b/sbin/fsdb/fsdb.h
new file mode 100644
index 0000000..a739c47
--- /dev/null
+++ b/sbin/fsdb/fsdb.h
@@ -0,0 +1,62 @@
+/* $NetBSD: fsdb.h,v 1.2 1995/10/08 23:18:11 thorpej Exp $ */
+
+/*
+ * Copyright (c) 1995 John T. Kohl
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+extern int bread(int fd, char *buf, ufs2_daddr_t blk, long size);
+extern void bwrite(int fd, char *buf, ufs2_daddr_t blk, long size);
+extern void rwerror(const char *mesg, ufs2_daddr_t blk);
+extern int reply(const char *question);
+
+extern long dev_bsize;
+extern long secsize;
+extern int fsmodified;
+extern int fsfd;
+
+struct cmdtable {
+ const char *cmd;
+ const char *helptxt;
+ unsigned int minargc;
+ unsigned int maxargc;
+ unsigned int flags;
+#define FL_RO 0x0000 /* for symmetry */
+#define FL_WR 0x0001 /* wants to write */
+ int (*handler)(int argc, char *argv[]);
+};
+extern union dinode *curinode;
+extern ino_t curinum;
+
+int argcount(struct cmdtable *cmdp, int argc, char *argv[]);
+char **crack(char *line, int *argc);
+char **recrack(char *line, int *argc, int argc_max);
+void printstat(const char *cp, ino_t inum, union dinode *dp);
+int printactive(int doblocks);
+int checkactive(void);
+int checkactivedir(void);
diff --git a/sbin/fsdb/fsdbutil.c b/sbin/fsdb/fsdbutil.c
new file mode 100644
index 0000000..b000619
--- /dev/null
+++ b/sbin/fsdb/fsdbutil.c
@@ -0,0 +1,371 @@
+/* $NetBSD: fsdbutil.c,v 1.2 1995/10/08 23:18:12 thorpej Exp $ */
+
+/*
+ * Copyright (c) 1995 John T. Kohl
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <ctype.h>
+#include <err.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+#include <timeconv.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <sys/ioctl.h>
+
+#include "fsdb.h"
+#include "fsck.h"
+
+static int charsperline(void);
+static int printindir(ufs2_daddr_t blk, int level, char *bufp);
+static void printblocks(ino_t inum, union dinode *dp);
+
+char **
+crack(char *line, int *argc)
+{
+ static char *argv[8];
+ int i;
+ char *p, *val;
+ for (p = line, i = 0; p != NULL && i < 8; i++) {
+ while ((val = strsep(&p, " \t\n")) != NULL && *val == '\0')
+ /**/;
+ if (val)
+ argv[i] = val;
+ else
+ break;
+ }
+ *argc = i;
+ return argv;
+}
+
+char **
+recrack(char *line, int *argc, int argc_max)
+{
+ static char *argv[8];
+ int i;
+ char *p, *val;
+ for (p = line, i = 0; p != NULL && i < 8 && i < argc_max - 1; i++) {
+ while ((val = strsep(&p, " \t\n")) != NULL && *val == '\0')
+ /**/;
+ if (val)
+ argv[i] = val;
+ else
+ break;
+ }
+ argv[i] = argv[i - 1] + strlen(argv[i - 1]) + 1;
+ argv[i][strcspn(argv[i], "\n")] = '\0';
+ *argc = i + 1;
+ return argv;
+}
+
+int
+argcount(struct cmdtable *cmdp, int argc, char *argv[])
+{
+ if (cmdp->minargc == cmdp->maxargc)
+ warnx("command `%s' takes %u arguments, got %u", cmdp->cmd,
+ cmdp->minargc-1, argc);
+ else
+ warnx("command `%s' takes from %u to %u arguments",
+ cmdp->cmd, cmdp->minargc-1, cmdp->maxargc-1);
+
+ warnx("usage: %s: %s", cmdp->cmd, cmdp->helptxt);
+ return 1;
+}
+
+void
+printstat(const char *cp, ino_t inum, union dinode *dp)
+{
+ struct group *grp;
+ struct passwd *pw;
+ ufs2_daddr_t blocks;
+ int64_t gen;
+ char *p;
+ time_t t;
+
+ printf("%s: ", cp);
+ switch (DIP(dp, di_mode) & IFMT) {
+ case IFDIR:
+ puts("directory");
+ break;
+ case IFREG:
+ puts("regular file");
+ break;
+ case IFBLK:
+ printf("block special (%d,%d)",
+ major(DIP(dp, di_rdev)), minor(DIP(dp, di_rdev)));
+ break;
+ case IFCHR:
+ printf("character special (%d,%d)",
+ major(DIP(dp, di_rdev)), minor(DIP(dp, di_rdev)));
+ break;
+ case IFLNK:
+ fputs("symlink",stdout);
+ if (DIP(dp, di_size) > 0 &&
+ DIP(dp, di_size) < sblock.fs_maxsymlinklen &&
+ DIP(dp, di_blocks) == 0) {
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ p = (caddr_t)dp->dp1.di_db;
+ else
+ p = (caddr_t)dp->dp2.di_db;
+ printf(" to `%.*s'\n", (int) DIP(dp, di_size), p);
+ } else {
+ putchar('\n');
+ }
+ break;
+ case IFSOCK:
+ puts("socket");
+ break;
+ case IFIFO:
+ puts("fifo");
+ break;
+ }
+ printf("I=%lu MODE=%o SIZE=%ju", (u_long)inum, DIP(dp, di_mode),
+ (uintmax_t)DIP(dp, di_size));
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ t = _time32_to_time(dp->dp1.di_mtime);
+ else
+ t = _time64_to_time(dp->dp2.di_mtime);
+ p = ctime(&t);
+ printf("\n\tMTIME=%15.15s %4.4s [%d nsec]", &p[4], &p[20],
+ DIP(dp, di_mtimensec));
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ t = _time32_to_time(dp->dp1.di_ctime);
+ else
+ t = _time64_to_time(dp->dp2.di_ctime);
+ p = ctime(&t);
+ printf("\n\tCTIME=%15.15s %4.4s [%d nsec]", &p[4], &p[20],
+ DIP(dp, di_ctimensec));
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ t = _time32_to_time(dp->dp1.di_atime);
+ else
+ t = _time64_to_time(dp->dp2.di_atime);
+ p = ctime(&t);
+ printf("\n\tATIME=%15.15s %4.4s [%d nsec]\n", &p[4], &p[20],
+ DIP(dp, di_atimensec));
+
+ if ((pw = getpwuid(DIP(dp, di_uid))))
+ printf("OWNER=%s ", pw->pw_name);
+ else
+ printf("OWNUID=%u ", DIP(dp, di_uid));
+ if ((grp = getgrgid(DIP(dp, di_gid))))
+ printf("GRP=%s ", grp->gr_name);
+ else
+ printf("GID=%u ", DIP(dp, di_gid));
+
+ blocks = DIP(dp, di_blocks);
+ gen = DIP(dp, di_gen);
+ printf("LINKCNT=%hd FLAGS=%#x BLKCNT=%jx GEN=%jx\n", DIP(dp, di_nlink),
+ DIP(dp, di_flags), (intmax_t)blocks, (intmax_t)gen);
+}
+
+
+/*
+ * Determine the number of characters in a
+ * single line.
+ */
+
+static int
+charsperline(void)
+{
+ int columns;
+ char *cp;
+ struct winsize ws;
+
+ columns = 0;
+ if (ioctl(0, TIOCGWINSZ, &ws) != -1)
+ columns = ws.ws_col;
+ if (columns == 0 && (cp = getenv("COLUMNS")))
+ columns = atoi(cp);
+ if (columns == 0)
+ columns = 80; /* last resort */
+ return (columns);
+}
+
+
+/*
+ * Recursively print a list of indirect blocks.
+ */
+static int
+printindir(ufs2_daddr_t blk, int level, char *bufp)
+{
+ struct bufarea buf, *bp;
+ char tempbuf[32]; /* enough to print an ufs2_daddr_t */
+ int i, j, cpl, charssofar;
+ ufs2_daddr_t blkno;
+
+ if (level == 0) {
+ /* for the final indirect level, don't use the cache */
+ bp = &buf;
+ bp->b_un.b_buf = bufp;
+ bp->b_prev = bp->b_next = bp;
+ initbarea(bp);
+
+ getblk(bp, blk, sblock.fs_bsize);
+ } else
+ bp = getdatablk(blk, sblock.fs_bsize);
+
+ cpl = charsperline();
+ for (i = charssofar = 0; i < NINDIR(&sblock); i++) {
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ blkno = bp->b_un.b_indir1[i];
+ else
+ blkno = bp->b_un.b_indir2[i];
+ if (blkno == 0) {
+ if (level == 0)
+ putchar('\n');
+ return 0;
+ }
+ j = sprintf(tempbuf, "%jd", (intmax_t)blkno);
+ if (level == 0) {
+ charssofar += j;
+ if (charssofar >= cpl - 2) {
+ putchar('\n');
+ charssofar = j;
+ }
+ }
+ fputs(tempbuf, stdout);
+ if (level == 0) {
+ printf(", ");
+ charssofar += 2;
+ } else {
+ printf(" =>\n");
+ if (printindir(blkno, level - 1, bufp) == 0)
+ return 0;
+ }
+ }
+ if (level == 0)
+ putchar('\n');
+ return 1;
+}
+
+
+/*
+ * Print the block pointers for one inode.
+ */
+static void
+printblocks(ino_t inum, union dinode *dp)
+{
+ char *bufp;
+ int i, nfrags;
+ long ndb, offset;
+ ufs2_daddr_t blkno;
+
+ printf("Blocks for inode %d:\n", inum);
+ printf("Direct blocks:\n");
+ ndb = howmany(DIP(dp, di_size), sblock.fs_bsize);
+ for (i = 0; i < NDADDR; i++) {
+ if (DIP(dp, di_db[i]) == 0) {
+ putchar('\n');
+ return;
+ }
+ if (i > 0)
+ printf(", ");
+ blkno = DIP(dp, di_db[i]);
+ printf("%jd", (intmax_t)blkno);
+ if (--ndb == 0 && (offset = blkoff(&sblock, DIP(dp, di_size))) != 0) {
+ nfrags = numfrags(&sblock, fragroundup(&sblock, offset));
+ printf(" (%d frag%s)", nfrags, nfrags > 1? "s": "");
+ }
+ }
+ putchar('\n');
+ if (DIP(dp, di_ib[0]) == 0)
+ return;
+
+ bufp = malloc((unsigned int)sblock.fs_bsize);
+ if (bufp == 0)
+ errx(EEXIT, "cannot allocate indirect block buffer");
+ printf("Indirect blocks:\n");
+ for (i = 0; i < NIADDR; i++)
+ if (printindir(DIP(dp, di_ib[i]), i, bufp) == 0)
+ break;
+ free(bufp);
+}
+
+
+int
+checkactive(void)
+{
+ if (!curinode) {
+ warnx("no current inode\n");
+ return 0;
+ }
+ return 1;
+}
+
+int
+checkactivedir(void)
+{
+ if (!curinode) {
+ warnx("no current inode\n");
+ return 0;
+ }
+ if ((DIP(curinode, di_mode) & IFMT) != IFDIR) {
+ warnx("inode %d not a directory", curinum);
+ return 0;
+ }
+ return 1;
+}
+
+int
+printactive(int doblocks)
+{
+ if (!checkactive())
+ return 1;
+ switch (DIP(curinode, di_mode) & IFMT) {
+ case IFDIR:
+ case IFREG:
+ case IFBLK:
+ case IFCHR:
+ case IFLNK:
+ case IFSOCK:
+ case IFIFO:
+ if (doblocks)
+ printblocks(curinum, curinode);
+ else
+ printstat("current inode", curinum, curinode);
+ break;
+ case 0:
+ printf("current inode %d: unallocated inode\n", curinum);
+ break;
+ default:
+ printf("current inode %d: screwy itype 0%o (mode 0%o)?\n",
+ curinum, DIP(curinode, di_mode) & IFMT, DIP(curinode, di_mode));
+ break;
+ }
+ return 0;
+}
diff --git a/sbin/fsirand/Makefile b/sbin/fsirand/Makefile
new file mode 100644
index 0000000..7e2da62
--- /dev/null
+++ b/sbin/fsirand/Makefile
@@ -0,0 +1,10 @@
+# $OpenBSD: Makefile,v 1.1 1997/01/26 02:23:20 millert Exp $
+# $FreeBSD$
+
+PROG= fsirand
+WARNS?= 0
+MAN= fsirand.8
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+.include <bsd.prog.mk>
diff --git a/sbin/fsirand/fsirand.8 b/sbin/fsirand/fsirand.8
new file mode 100644
index 0000000..00f0eee
--- /dev/null
+++ b/sbin/fsirand/fsirand.8
@@ -0,0 +1,116 @@
+.\" Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed by Todd C. Miller.
+.\" 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 ``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.
+.\"
+.\" $OpenBSD: fsirand.8,v 1.6 1997/02/23 03:58:26 millert Exp $
+.\" $FreeBSD$
+.\"
+.Dd January 25, 1997
+.Dt FSIRAND 8
+.Os
+.Sh NAME
+.Nm fsirand
+.Nd randomize inode generation numbers
+.Sh SYNOPSIS
+.Nm
+.Op Fl b
+.Op Fl f
+.Op Fl p
+.Ar special
+.Op Ar "special ..."
+.Sh DESCRIPTION
+The
+.Nm
+utility installs random generation numbers on all the inodes for
+each file system specified on the command line by
+.Ar special .
+This increases the security of NFS-exported file systems by making
+it difficult to ``guess'' filehandles.
+.Pp
+.Em Note :
+.Xr newfs 8
+now does the equivalent of
+.Nm
+itself so it is no longer necessary to
+run
+.Nm
+by hand on a new file system.
+It is only used to
+re-randomize or report on an existing file system.
+.Pp
+The
+.Nm
+utility should only be used on an unmounted file system that
+has been checked with
+.Xr fsck 8
+or a file system that is mounted read-only.
+The
+.Nm
+utility may be used on the root file system in single-user mode
+but the system should be rebooted via ``reboot -n'' afterwards.
+.Sh OPTIONS
+.Bl -tag -width indent
+The available options are as follows:
+.It Fl b
+Use the default block size (usually 512 bytes) instead
+of the value gleaned from the disklabel.
+.It Fl f
+Force
+.Nm
+to run even if the file system on
+.Ar special
+is not marked as clean.
+.It Fl p
+Print the current generation numbers for all inodes instead of
+generating new ones.
+.El
+.Sh CAVEATS
+Since
+.Nm
+allocates enough memory to hold all the inodes in
+a given cylinder group it may use a large amount
+of memory for large disks with few cylinder groups.
+.Sh SEE ALSO
+.Xr fs 5 ,
+.Xr fsck 8 ,
+.Xr newfs 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in SunOS 3.x.
+.Pp
+This version of
+.Nm
+first appeared in
+.Ox 2.1 .
+.Pp
+A
+.Fx
+version first appeared in
+.Fx 2.2.5 .
+.Sh AUTHORS
+.An Todd C. Miller Aq Todd.Miller@courtesan.com
diff --git a/sbin/fsirand/fsirand.c b/sbin/fsirand/fsirand.c
new file mode 100644
index 0000000..defdf52
--- /dev/null
+++ b/sbin/fsirand/fsirand.c
@@ -0,0 +1,310 @@
+/* $OpenBSD: fsirand.c,v 1.9 1997/02/28 00:46:33 millert Exp $ */
+
+/*
+ * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Todd C. Miller.
+ * 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 ``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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/disklabel.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void usage(void) __dead2;
+int fsirand(char *);
+
+/*
+ * Possible superblock locations ordered from most to least likely.
+ */
+static int sblock_try[] = SBLOCKSEARCH;
+
+int printonly = 0, force = 0, ignorelabel = 0;
+
+int
+main(int argc, char *argv[])
+{
+ int n, ex = 0;
+ struct rlimit rl;
+
+ while ((n = getopt(argc, argv, "bfp")) != -1) {
+ switch (n) {
+ case 'b':
+ ignorelabel++;
+ break;
+ case 'p':
+ printonly++;
+ break;
+ case 'f':
+ force++;
+ break;
+ default:
+ usage();
+ }
+ }
+ if (argc - optind < 1)
+ usage();
+
+ srandomdev();
+
+ /* Increase our data size to the max */
+ if (getrlimit(RLIMIT_DATA, &rl) == 0) {
+ rl.rlim_cur = rl.rlim_max;
+ if (setrlimit(RLIMIT_DATA, &rl) < 0)
+ warn("can't get resource limit to max data size");
+ } else
+ warn("can't get resource limit for data size");
+
+ for (n = optind; n < argc; n++) {
+ if (argc - optind != 1)
+ (void)puts(argv[n]);
+ ex += fsirand(argv[n]);
+ if (n < argc - 1)
+ putchar('\n');
+ }
+
+ exit(ex);
+}
+
+int
+fsirand(char *device)
+{
+ struct ufs1_dinode *dp1;
+ struct ufs2_dinode *dp2;
+ caddr_t inodebuf;
+ size_t ibufsize;
+ struct fs *sblock;
+ ino_t inumber, maxino;
+ ufs2_daddr_t sblockloc, dblk;
+ char sbuf[SBLOCKSIZE], sbuftmp[SBLOCKSIZE];
+ int i, devfd, n, cg;
+ u_int32_t bsize = DEV_BSIZE;
+ struct disklabel label;
+
+ if ((devfd = open(device, printonly ? O_RDONLY : O_RDWR)) < 0) {
+ warn("can't open %s", device);
+ return (1);
+ }
+
+ /* Get block size (usually 512) from disklabel if possible */
+ if (!ignorelabel) {
+ if (ioctl(devfd, DIOCGDINFO, &label) < 0)
+ warn("can't read disklabel, using sector size of %d",
+ bsize);
+ else
+ bsize = label.d_secsize;
+ }
+
+ /* Read in master superblock */
+ (void)memset(&sbuf, 0, sizeof(sbuf));
+ sblock = (struct fs *)&sbuf;
+ for (i = 0; sblock_try[i] != -1; i++) {
+ sblockloc = sblock_try[i];
+ if (lseek(devfd, sblockloc, SEEK_SET) == -1) {
+ warn("can't seek to superblock (%qd) on %s",
+ sblockloc, device);
+ return (1);
+ }
+ if ((n = read(devfd, (void *)sblock, SBLOCKSIZE))!=SBLOCKSIZE) {
+ warnx("can't read superblock on %s: %s", device,
+ (n < SBLOCKSIZE) ? "short read" : strerror(errno));
+ return (1);
+ }
+ if ((sblock->fs_magic == FS_UFS1_MAGIC ||
+ (sblock->fs_magic == FS_UFS2_MAGIC &&
+ sblock->fs_sblockloc == sblock_try[i])) &&
+ sblock->fs_bsize <= MAXBSIZE &&
+ sblock->fs_bsize >= sizeof(struct fs))
+ break;
+ }
+ if (sblock_try[i] == -1) {
+ fprintf(stderr, "Cannot find file system superblock\n");
+ return (1);
+ }
+ maxino = sblock->fs_ncg * sblock->fs_ipg;
+
+ if (sblock->fs_magic == FS_UFS1_MAGIC &&
+ sblock->fs_old_inodefmt < FS_44INODEFMT) {
+ warnx("file system format is too old, sorry");
+ return (1);
+ }
+ if (!force && !printonly && sblock->fs_clean != 1) {
+ warnx("file system is not clean, fsck %s first", device);
+ return (1);
+ }
+
+ /* Make sure backup superblocks are sane. */
+ sblock = (struct fs *)&sbuftmp;
+ for (cg = 0; cg < sblock->fs_ncg; cg++) {
+ dblk = fsbtodb(sblock, cgsblock(sblock, cg));
+ if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) < 0) {
+ warn("can't seek to %qd", (off_t)dblk * bsize);
+ return (1);
+ } else if ((n = write(devfd, (void *)sblock, SBLOCKSIZE)) != SBLOCKSIZE) {
+ warn("can't read backup superblock %d on %s: %s",
+ cg + 1, device, (n < SBLOCKSIZE) ? "short write"
+ : strerror(errno));
+ return (1);
+ }
+ if (sblock->fs_magic != FS_UFS1_MAGIC &&
+ sblock->fs_magic != FS_UFS2_MAGIC) {
+ warnx("bad magic number in backup superblock %d on %s",
+ cg + 1, device);
+ return (1);
+ }
+ if (sblock->fs_sbsize > SBLOCKSIZE) {
+ warnx("size of backup superblock %d on %s is preposterous",
+ cg + 1, device);
+ return (1);
+ }
+ }
+ sblock = (struct fs *)&sbuf;
+
+ /* XXX - should really cap buffer at 512kb or so */
+ if (sblock->fs_magic == FS_UFS1_MAGIC)
+ ibufsize = sizeof(struct ufs1_dinode) * sblock->fs_ipg;
+ else
+ ibufsize = sizeof(struct ufs2_dinode) * sblock->fs_ipg;
+ if ((inodebuf = malloc(ibufsize)) == NULL)
+ errx(1, "can't allocate memory for inode buffer");
+
+ if (printonly && (sblock->fs_id[0] || sblock->fs_id[1])) {
+ if (sblock->fs_id[0])
+ (void)printf("%s was randomized on %s", device,
+ ctime((const time_t *)&(sblock->fs_id[0])));
+ (void)printf("fsid: %x %x\n", sblock->fs_id[0],
+ sblock->fs_id[1]);
+ }
+
+ /* Randomize fs_id unless old 4.2BSD file system */
+ if (!printonly) {
+ /* Randomize fs_id and write out new sblock and backups */
+ sblock->fs_id[0] = (u_int32_t)time(NULL);
+ sblock->fs_id[1] = random();
+
+ if (lseek(devfd, sblockloc, SEEK_SET) == -1) {
+ warn("can't seek to superblock (%qd) on %s", sblockloc,
+ device);
+ return (1);
+ }
+ if ((n = write(devfd, (void *)sblock, SBLOCKSIZE)) !=
+ SBLOCKSIZE) {
+ warn("can't write superblock on %s: %s", device,
+ (n < SBLOCKSIZE) ? "short write" : strerror(errno));
+ return (1);
+ }
+ }
+
+ /* For each cylinder group, randomize inodes and update backup sblock */
+ for (cg = 0, inumber = 0; cg < sblock->fs_ncg; cg++) {
+ /* Update superblock if appropriate */
+ if (!printonly) {
+ dblk = fsbtodb(sblock, cgsblock(sblock, cg));
+ if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) < 0) {
+ warn("can't seek to %qd", (off_t)dblk * bsize);
+ return (1);
+ } else if ((n = write(devfd, (void *)sblock,
+ SBLOCKSIZE)) != SBLOCKSIZE) {
+ warn("can't write backup superblock %d on %s: %s",
+ cg + 1, device, (n < SBLOCKSIZE) ?
+ "short write" : strerror(errno));
+ return (1);
+ }
+ }
+
+ /* Read in inodes, then print or randomize generation nums */
+ dblk = fsbtodb(sblock, ino_to_fsba(sblock, inumber));
+ if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) < 0) {
+ warn("can't seek to %qd", (off_t)dblk * bsize);
+ return (1);
+ } else if ((n = read(devfd, inodebuf, ibufsize)) != ibufsize) {
+ warnx("can't read inodes: %s",
+ (n < ibufsize) ? "short read" : strerror(errno));
+ return (1);
+ }
+
+ for (n = 0; n < sblock->fs_ipg; n++, inumber++) {
+ if (sblock->fs_magic == FS_UFS1_MAGIC)
+ dp1 = &((struct ufs1_dinode *)inodebuf)[n];
+ else
+ dp2 = &((struct ufs2_dinode *)inodebuf)[n];
+ if (inumber >= ROOTINO) {
+ if (printonly)
+ (void)printf("ino %d gen %qx\n",
+ inumber,
+ sblock->fs_magic == FS_UFS1_MAGIC ?
+ (quad_t)dp1->di_gen : dp2->di_gen);
+ else if (sblock->fs_magic == FS_UFS1_MAGIC)
+ dp1->di_gen = random();
+ else
+ dp2->di_gen = random();
+ }
+ }
+
+ /* Write out modified inodes */
+ if (!printonly) {
+ if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) < 0) {
+ warn("can't seek to %qd",
+ (off_t)dblk * bsize);
+ return (1);
+ } else if ((n = write(devfd, inodebuf, ibufsize)) !=
+ ibufsize) {
+ warnx("can't write inodes: %s",
+ (n != ibufsize) ? "short write" :
+ strerror(errno));
+ return (1);
+ }
+ }
+ }
+ (void)close(devfd);
+
+ return(0);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr,
+ "usage: fsirand [-b] [-f] [-p] special [special ...]\n");
+ exit(1);
+}
diff --git a/sbin/gbde/Makefile b/sbin/gbde/Makefile
new file mode 100644
index 0000000..8705802
--- /dev/null
+++ b/sbin/gbde/Makefile
@@ -0,0 +1,36 @@
+# $FreeBSD$
+
+PROG= gbde
+SRCS= gbde.c template.c
+SRCS+= rijndael-alg-fst.c
+SRCS+= rijndael-api-fst.c
+SRCS+= sha2.c
+SRCS+= g_bde_lock.c
+
+# rijndael-fst.c does evil casting things which results in warnings on
+# 64 bit machines, the test-vectors check out however, so it works right.
+.if ${MACHINE_ARCH} == "i386"
+WARNS?= 5
+.else
+WARNS?= 3
+.endif
+
+CFLAGS+= -I${.CURDIR}/../../sys
+.PATH: ${.CURDIR}/../../sys/geom/bde \
+ ${.CURDIR}/../../sys/crypto/rijndael \
+ ${.CURDIR}/../../sys/crypto/sha2
+
+CLEANFILES+= template.c
+
+MAN= gbde.8
+DPADD= ${LIBMD} ${LIBUTIL} ${LIBGEOM}
+LDADD= -lmd -lutil -lgeom
+
+template.c: template.txt
+ file2c 'const char template[] = {' ',0};' \
+ < ${.CURDIR}/template.txt > template.c
+
+test: ${PROG}
+ sh ${.CURDIR}/test.sh ${.CURDIR}
+
+.include <bsd.prog.mk>
diff --git a/sbin/gbde/gbde.8 b/sbin/gbde/gbde.8
new file mode 100644
index 0000000..fe3d55d
--- /dev/null
+++ b/sbin/gbde/gbde.8
@@ -0,0 +1,232 @@
+.\"
+.\" Copyright (c) 2002 Poul-Henning Kamp
+.\" Copyright (c) 2002 Networks Associates Technology, Inc.
+.\" All rights reserved.
+.\"
+.\" This software was developed for the FreeBSD Project by Poul-Henning Kamp
+.\" and NAI Labs, the Security Research Division of Network Associates, Inc.
+.\" under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
+.\" DARPA CHATS research program.
+.\"
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 12, 2005
+.Dt GBDE 8
+.Os
+.Sh NAME
+.Nm gbde
+.Nd operation and management utility for Geom Based Disk Encryption
+.Sh SYNOPSIS
+.Nm
+.Cm attach
+.Ar destination
+.Op Fl l Ar lockfile
+.Op Fl p Ar pass-phrase
+.Nm
+.Cm detach
+.Ar destination
+.Nm
+.Cm init
+.Ar destination
+.Op Fl i
+.Op Fl f Ar filename
+.Op Fl L Ar new-lockfile
+.Op Fl P Ar new-pass-phrase
+.Nm
+.Cm setkey
+.Ar destination
+.Op Fl n Ar key
+.Op Fl l Ar lockfile
+.Op Fl p Ar pass-phrase
+.Op Fl L Ar new-lockfile
+.Op Fl P Ar new-pass-phrase
+.Nm
+.Cm nuke
+.Ar destination
+.Op Fl n Ar key
+.Op Fl l Ar lockfile
+.Op Fl p Ar pass-phrase
+.Nm
+.Cm destroy
+.Ar destination
+.Op Fl l Ar lockfile
+.Op Fl p Ar pass-phrase
+.Sh DESCRIPTION
+.Bf -symbolic
+NOTICE:
+Please be aware that this code has not yet received much review
+and analysis by qualified cryptographers and therefore should be considered
+a slightly suspect experimental facility.
+.Pp
+We cannot at this point guarantee that the on-disk format will not change
+in response to reviews or bug-fixes, so potential users are advised to
+be prepared that
+.Xr dump 8 Ns / Ns
+.Xr restore 8
+based migrations may be called for in the future.
+.Ef
+.Pp
+The
+.Nm
+utility is the only official operation and management interface for the
+.Xr gbde 4
+.Tn GEOM
+based disk encryption kernel facility.
+The interaction between the
+.Nm
+utility and the kernel part is not a published interface.
+.Pp
+The operational aspect consists of two subcommands:
+one to open and attach
+a device to the in-kernel cryptographic
+.Nm
+module
+.Pq Cm attach ,
+and one to close and detach a device
+.Pq Cm detach .
+.Pp
+The management part allows initialization of the master key and lock sectors
+on a device
+.Pq Cm init ,
+initialization and replacement of pass-phrases
+.Pq Cm setkey ,
+and key invalidation
+.Pq Cm nuke
+and blackening
+.Pq Cm destroy
+functions.
+.Pp
+The
+.Fl l Ar lockfile
+argument is used to supply the lock selector data.
+If no
+.Fl l
+option is specified, the first sector is used for this purpose.
+.Pp
+The
+.Fl L Ar new-lockfile
+argument
+specifies the lock selector file for the key
+initialized with the
+.Cm init
+subcommand
+or modified with the
+.Cm setkey
+subcommand.
+.Pp
+The
+.Fl n Ar key
+argument can be used to specify to which of the four keys
+the operation applies.
+A value of 1 to 4 selects the specified key, a value of 0 (the default)
+means
+.Dq "this key"
+(i.e., the key used to gain access to the device)
+and a value of \-1 means
+.Dq "all keys" .
+.Pp
+The
+.Fl f Ar filename
+specifies an optional parameter file for use under initialization.
+.Pp
+Alternatively, the
+.Fl i
+option toggles an interactive mode where a template file with descriptions
+of the parameters can be interactively edited.
+.Pp
+The
+.Fl p Ar pass-phrase
+argument
+specifies the pass-phrase used for opening the device.
+If not specified, the controlling terminal will be used to prompt the user
+for the pass-phrase.
+Be aware that using this option may expose the pass-phrase to other
+users who happen to run
+.Xr ps 1
+or similar while the command is running.
+.Pp
+The
+.Fl P Ar new-pass-phrase
+argument
+can be used to specify the new pass-phrase to the
+.Cm init
+and
+.Cm setkey
+subcommands.
+If not specified, the user is prompted for the new pass-phrase on the
+controlling terminal.
+Be aware that using this option may expose the pass-phrase to other
+users who happen to run
+.Xr ps 1
+or similar while the command is running.
+.Sh EXAMPLES
+To initialize a device, using default parameters:
+.Pp
+.Dl "gbde init /dev/ad0s1f -L /etc/ad0s1f.lock"
+.Pp
+To attach an encrypted device:
+.Pp
+.Dl "gbde attach ad0s1f -l /etc/ad0s1f.lock"
+.Pp
+The encrypted device has the suffix
+.Pa .bde
+so a typical
+command to create and mount a file system would be:
+.Pp
+.Dl "newfs /dev/ad0s1f.bde"
+.Dl "mount /dev/ad0s1f.bde /secret"
+.Pp
+To detach an encrypted device:
+.Pp
+.Dl "gbde detach ad0s1f"
+.Pp
+Please notice that detaching an encrypted device corresponds to
+physically removing it, do not forget to unmount the file system first.
+.Pp
+To initialize the second key using a detached lockfile and a trivial
+pass-phrase:
+.Pp
+.Dl "gbde setkey ad0s1f -n 2 -P foo -L key2.lockfile"
+.Pp
+To destroy all copies of the masterkey:
+.Pp
+.Dl "gbde destroy ad0s1f -n -1"
+.Sh SEE ALSO
+.Xr gbde 4 ,
+.Xr geom 4
+.Sh HISTORY
+This software was developed for the
+.Fx
+Project by
+.An "Poul-Henning Kamp"
+and NAI Labs, the Security Research Division of Network Associates, Inc.\&
+under DARPA/SPAWAR contract N66001-01-C-8035
+.Pq Dq CBOSS ,
+as part of the
+DARPA CHATS research program.
+.Sh AUTHORS
+.An "Poul-Henning Kamp" Aq phk@FreeBSD.org
+.Sh BUGS
+The cryptographic algorithms and the overall design have not been
+attacked mercilessly for over 10 years by a gang of cryptoanalysts.
diff --git a/sbin/gbde/gbde.c b/sbin/gbde/gbde.c
new file mode 100644
index 0000000..00c8e1c
--- /dev/null
+++ b/sbin/gbde/gbde.c
@@ -0,0 +1,865 @@
+/*-
+ * Copyright (c) 2002 Poul-Henning Kamp
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Poul-Henning Kamp
+ * and NAI Labs, the Security Research Division of Network Associates, Inc.
+ * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
+ * DARPA CHATS research program.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ *
+ * XXX: Future stuff
+ *
+ * Replace the template file options (-i & -f) with command-line variables
+ * "-v property=foo"
+ *
+ * Introduce -e, extra entropy source (XOR with /dev/random)
+ *
+ * Introduce -E, alternate entropy source (instead of /dev/random)
+ *
+ * Introduce -i take IV from keyboard or
+ *
+ * Introduce -I take IV from file/cmd
+ *
+ * Introduce -m/-M store encrypted+encoded masterkey in file
+ *
+ * Introduce -k/-K get pass-phrase part from file/cmd
+ *
+ * Introduce -d add more dest-devices to worklist.
+ *
+ * Add key-option: selfdestruct bit.
+ *
+ * New/changed verbs:
+ * "onetime" attach with onetime nonstored locksector
+ * "key"/"unkey" to blast memory copy of key without orphaning
+ * "nuke" blow away everything attached, crash/halt/power-off if possible.
+ * "blast" destroy all copies of the masterkey
+ * "destroy" destroy one copy of the masterkey
+ * "backup"/"restore" of masterkey sectors.
+ *
+ * Make all verbs work on both attached/detached devices.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/mutex.h>
+#include <md5.h>
+#include <readpassphrase.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <err.h>
+#include <stdio.h>
+#include <libutil.h>
+#include <libgeom.h>
+#include <sys/errno.h>
+#include <sys/disk.h>
+#include <sys/stat.h>
+#include <crypto/rijndael/rijndael-api-fst.h>
+#include <crypto/sha2/sha2.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+
+#define GBDEMOD "geom_bde"
+#define KASSERT(foo, bar) do { if(!(foo)) { warn bar ; exit (1); } } while (0)
+
+#include <geom/geom.h>
+#include <geom/bde/g_bde.h>
+
+extern const char template[];
+
+
+#if 0
+static void
+g_hexdump(void *ptr, int length)
+{
+ int i, j, k;
+ unsigned char *cp;
+
+ cp = ptr;
+ for (i = 0; i < length; i+= 16) {
+ printf("%04x ", i);
+ for (j = 0; j < 16; j++) {
+ k = i + j;
+ if (k < length)
+ printf(" %02x", cp[k]);
+ else
+ printf(" ");
+ }
+ printf(" |");
+ for (j = 0; j < 16; j++) {
+ k = i + j;
+ if (k >= length)
+ printf(" ");
+ else if (cp[k] >= ' ' && cp[k] <= '~')
+ printf("%c", cp[k]);
+ else
+ printf(".");
+ }
+ printf("|\n");
+ }
+}
+#endif
+
+static void __dead2
+usage(void)
+{
+
+ (void)fprintf(stderr,
+"usage: gbde attach destination [-l lockfile] [-p pass-phrase]\n"
+" gbde detach destination\n"
+" gbde init destination [-i] [-f filename] [-L new-lockfile]\n"
+" [-P new-pass-phrase]\n"
+" gbde setkey destination [-n key] [-l lockfile] [-p pass-phrase]\n"
+" [-L new-lockfile] [-P new-pass-phrase]\n"
+" gbde nuke destination [-n key] [-l lockfile] [-p pass-phrase]\n"
+" gbde destroy destination [-l lockfile] [-p pass-phrase]\n");
+ exit(1);
+}
+
+void *
+g_read_data(struct g_consumer *cp, off_t offset, off_t length, int *error)
+{
+ void *p;
+ int fd, i;
+ off_t o2;
+
+ p = malloc(length);
+ if (p == NULL)
+ err(1, "malloc");
+ fd = *(int *)cp;
+ o2 = lseek(fd, offset, SEEK_SET);
+ if (o2 != offset)
+ err(1, "lseek");
+ i = read(fd, p, length);
+ if (i != length)
+ err(1, "read");
+ if (error != NULL)
+ error = 0;
+ return (p);
+}
+
+static void
+random_bits(void *p, u_int len)
+{
+ static int fdr = -1;
+ int i;
+
+ if (fdr < 0) {
+ fdr = open("/dev/urandom", O_RDONLY);
+ if (fdr < 0)
+ err(1, "/dev/urandom");
+ }
+
+ i = read(fdr, p, len);
+ if (i != (int)len)
+ err(1, "read from /dev/urandom");
+}
+
+/* XXX: not nice */
+static u_char sha2[SHA512_DIGEST_LENGTH];
+
+static void
+reset_passphrase(struct g_bde_softc *sc)
+{
+
+ memcpy(sc->sha2, sha2, SHA512_DIGEST_LENGTH);
+}
+
+static void
+setup_passphrase(struct g_bde_softc *sc, int sure, const char *input)
+{
+ char buf1[BUFSIZ], buf2[BUFSIZ], *p;
+
+ if (input != NULL) {
+ g_bde_hash_pass(sc, input, strlen(input));
+ memcpy(sha2, sc->sha2, SHA512_DIGEST_LENGTH);
+ return;
+ }
+ for (;;) {
+ p = readpassphrase(
+ sure ? "Enter new passphrase:" : "Enter passphrase: ",
+ buf1, sizeof buf1,
+ RPP_ECHO_OFF | RPP_REQUIRE_TTY);
+ if (p == NULL)
+ err(1, "readpassphrase");
+
+ if (sure) {
+ p = readpassphrase("Reenter new passphrase: ",
+ buf2, sizeof buf2,
+ RPP_ECHO_OFF | RPP_REQUIRE_TTY);
+ if (p == NULL)
+ err(1, "readpassphrase");
+
+ if (strcmp(buf1, buf2)) {
+ printf("They didn't match.\n");
+ continue;
+ }
+ }
+ if (strlen(buf1) < 3) {
+ printf("Too short passphrase.\n");
+ continue;
+ }
+ break;
+ }
+ g_bde_hash_pass(sc, buf1, strlen(buf1));
+ memcpy(sha2, sc->sha2, SHA512_DIGEST_LENGTH);
+}
+
+static void
+encrypt_sector(void *d, int len, int klen, void *key)
+{
+ keyInstance ki;
+ cipherInstance ci;
+ int error;
+
+ error = rijndael_cipherInit(&ci, MODE_CBC, NULL);
+ if (error <= 0)
+ errx(1, "rijndael_cipherInit=%d", error);
+ error = rijndael_makeKey(&ki, DIR_ENCRYPT, klen, key);
+ if (error <= 0)
+ errx(1, "rijndael_makeKeY=%d", error);
+ error = rijndael_blockEncrypt(&ci, &ki, d, len * 8, d);
+ if (error <= 0)
+ errx(1, "rijndael_blockEncrypt=%d", error);
+}
+
+static void
+cmd_attach(const struct g_bde_softc *sc, const char *dest, const char *lfile)
+{
+ int ffd;
+ u_char buf[16];
+ struct gctl_req *r;
+ const char *errstr;
+
+ r = gctl_get_handle();
+ gctl_ro_param(r, "verb", -1, "create geom");
+ gctl_ro_param(r, "class", -1, "BDE");
+ gctl_ro_param(r, "provider", -1, dest);
+ gctl_ro_param(r, "pass", SHA512_DIGEST_LENGTH, sc->sha2);
+ if (lfile != NULL) {
+ ffd = open(lfile, O_RDONLY, 0);
+ if (ffd < 0)
+ err(1, "%s", lfile);
+ read(ffd, buf, 16);
+ gctl_ro_param(r, "key", 16, buf);
+ close(ffd);
+ }
+ /* gctl_dump(r, stdout); */
+ errstr = gctl_issue(r);
+ if (errstr != NULL)
+ errx(1, "Attach to %s failed: %s", dest, errstr);
+
+ exit (0);
+}
+
+static void
+cmd_detach(const char *dest)
+{
+ struct gctl_req *r;
+ const char *errstr;
+ char buf[BUFSIZ];
+
+ r = gctl_get_handle();
+ gctl_ro_param(r, "verb", -1, "destroy geom");
+ gctl_ro_param(r, "class", -1, "BDE");
+ sprintf(buf, "%s.bde", dest);
+ gctl_ro_param(r, "geom", -1, buf);
+ /* gctl_dump(r, stdout); */
+ errstr = gctl_issue(r);
+ if (errstr != NULL)
+ errx(1, "Detach of %s failed: %s", dest, errstr);
+ exit (0);
+}
+
+static void
+cmd_open(struct g_bde_softc *sc, int dfd , const char *l_opt, u_int *nkey)
+{
+ int error;
+ int ffd;
+ u_char keyloc[16];
+ u_int sectorsize;
+ off_t mediasize;
+ struct stat st;
+
+ error = ioctl(dfd, DIOCGSECTORSIZE, &sectorsize);
+ if (error)
+ sectorsize = 512;
+ error = ioctl(dfd, DIOCGMEDIASIZE, &mediasize);
+ if (error) {
+ error = fstat(dfd, &st);
+ if (error == 0 && S_ISREG(st.st_mode))
+ mediasize = st.st_size;
+ else
+ error = ENOENT;
+ }
+ if (error)
+ mediasize = (off_t)-1;
+ if (l_opt != NULL) {
+ ffd = open(l_opt, O_RDONLY, 0);
+ if (ffd < 0)
+ err(1, "%s", l_opt);
+ read(ffd, keyloc, sizeof keyloc);
+ close(ffd);
+ } else {
+ memset(keyloc, 0, sizeof keyloc);
+ }
+
+ error = g_bde_decrypt_lock(sc, sc->sha2, keyloc, mediasize,
+ sectorsize, nkey);
+ if (error == ENOENT)
+ errx(1, "Lock was destroyed.");
+ if (error == ESRCH)
+ errx(1, "Lock was nuked.");
+ if (error == ENOTDIR)
+ errx(1, "Lock not found");
+ if (error != 0)
+ errx(1, "Error %d decrypting lock", error);
+ if (nkey)
+ printf("Opened with key %u\n", *nkey);
+ return;
+}
+
+static void
+cmd_nuke(struct g_bde_key *gl, int dfd , int key)
+{
+ int i;
+ u_char *sbuf;
+ off_t offset, offset2;
+
+ sbuf = malloc(gl->sectorsize);
+ memset(sbuf, 0, gl->sectorsize);
+ offset = (gl->lsector[key] & ~(gl->sectorsize - 1));
+ offset2 = lseek(dfd, offset, SEEK_SET);
+ if (offset2 != offset)
+ err(1, "lseek");
+ i = write(dfd, sbuf, gl->sectorsize);
+ free(sbuf);
+ if (i != (int)gl->sectorsize)
+ err(1, "write");
+ printf("Nuked key %d\n", key);
+}
+
+static void
+cmd_write(struct g_bde_key *gl, struct g_bde_softc *sc, int dfd , int key, const char *l_opt)
+{
+ int i, ffd;
+ uint64_t off[2];
+ u_char keyloc[16];
+ u_char *sbuf, *q;
+ off_t offset, offset2;
+
+ sbuf = malloc(gl->sectorsize);
+ /*
+ * Find the byte-offset in the lock sector where we will put the lock
+ * data structure. We can put it any random place as long as the
+ * structure fits.
+ */
+ for(;;) {
+ random_bits(off, sizeof off);
+ off[0] &= (gl->sectorsize - 1);
+ if (off[0] + G_BDE_LOCKSIZE > gl->sectorsize)
+ continue;
+ break;
+ }
+
+ /* Add the sector offset in bytes */
+ off[0] += (gl->lsector[key] & ~(gl->sectorsize - 1));
+ gl->lsector[key] = off[0];
+
+ i = g_bde_keyloc_encrypt(sc->sha2, off[0], off[1], keyloc);
+ if (i)
+ errx(1, "g_bde_keyloc_encrypt()");
+ if (l_opt != NULL) {
+ ffd = open(l_opt, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ if (ffd < 0)
+ err(1, "%s", l_opt);
+ write(ffd, keyloc, sizeof keyloc);
+ close(ffd);
+ } else if (gl->flags & GBDE_F_SECT0) {
+ offset2 = lseek(dfd, 0, SEEK_SET);
+ if (offset2 != 0)
+ err(1, "lseek");
+ i = read(dfd, sbuf, gl->sectorsize);
+ if (i != (int)gl->sectorsize)
+ err(1, "read");
+ memcpy(sbuf + key * 16, keyloc, sizeof keyloc);
+ offset2 = lseek(dfd, 0, SEEK_SET);
+ if (offset2 != 0)
+ err(1, "lseek");
+ i = write(dfd, sbuf, gl->sectorsize);
+ if (i != (int)gl->sectorsize)
+ err(1, "write");
+ } else {
+ errx(1, "No -L option and no space in sector 0 for lockfile");
+ }
+
+ /* Allocate a sectorbuffer and fill it with random junk */
+ if (sbuf == NULL)
+ err(1, "malloc");
+ random_bits(sbuf, gl->sectorsize);
+
+ /* Fill random bits in the spare field */
+ random_bits(gl->spare, sizeof(gl->spare));
+
+ /* Encode the structure where we want it */
+ q = sbuf + (off[0] % gl->sectorsize);
+ i = g_bde_encode_lock(sc->sha2, gl, q);
+ if (i < 0)
+ errx(1, "programming error encoding lock");
+
+ encrypt_sector(q, G_BDE_LOCKSIZE, 256, sc->sha2 + 16);
+ offset = gl->lsector[key] & ~(gl->sectorsize - 1);
+ offset2 = lseek(dfd, offset, SEEK_SET);
+ if (offset2 != offset)
+ err(1, "lseek");
+ i = write(dfd, sbuf, gl->sectorsize);
+ if (i != (int)gl->sectorsize)
+ err(1, "write");
+ free(sbuf);
+#if 0
+ printf("Wrote key %d at %jd\n", key, (intmax_t)offset);
+ printf("s0 = %jd\n", (intmax_t)gl->sector0);
+ printf("sN = %jd\n", (intmax_t)gl->sectorN);
+ printf("l[0] = %jd\n", (intmax_t)gl->lsector[0]);
+ printf("l[1] = %jd\n", (intmax_t)gl->lsector[1]);
+ printf("l[2] = %jd\n", (intmax_t)gl->lsector[2]);
+ printf("l[3] = %jd\n", (intmax_t)gl->lsector[3]);
+ printf("k = %jd\n", (intmax_t)gl->keyoffset);
+ printf("ss = %jd\n", (intmax_t)gl->sectorsize);
+#endif
+}
+
+static void
+cmd_destroy(struct g_bde_key *gl, int nkey)
+{
+ int i;
+
+ bzero(&gl->sector0, sizeof gl->sector0);
+ bzero(&gl->sectorN, sizeof gl->sectorN);
+ bzero(&gl->keyoffset, sizeof gl->keyoffset);
+ bzero(&gl->flags, sizeof gl->flags);
+ bzero(gl->mkey, sizeof gl->mkey);
+ for (i = 0; i < G_BDE_MAXKEYS; i++)
+ if (i != nkey)
+ gl->lsector[i] = ~0;
+}
+
+static int
+sorthelp(const void *a, const void *b)
+{
+ const uint64_t *oa, *ob;
+
+ oa = a;
+ ob = b;
+ if (*oa > *ob)
+ return 1;
+ if (*oa < *ob)
+ return -1;
+ return 0;
+}
+
+static void
+cmd_init(struct g_bde_key *gl, int dfd, const char *f_opt, int i_opt, const char *l_opt)
+{
+ int i;
+ u_char *buf;
+ unsigned sector_size;
+ uint64_t first_sector;
+ uint64_t last_sector;
+ uint64_t total_sectors;
+ off_t off, off2;
+ unsigned nkeys;
+ const char *p;
+ char *q, cbuf[BUFSIZ];
+ unsigned u, u2;
+ uint64_t o;
+ properties params;
+
+ bzero(gl, sizeof *gl);
+ if (f_opt != NULL) {
+ i = open(f_opt, O_RDONLY);
+ if (i < 0)
+ err(1, "%s", f_opt);
+ params = properties_read(i);
+ close (i);
+ } else if (i_opt) {
+ /* XXX: Polish */
+ asprintf(&q, "%stemp.XXXXXXXXXX", _PATH_TMP);
+ if (q == NULL)
+ err(1, "asprintf");
+ i = mkstemp(q);
+ if (i < 0)
+ err(1, "%s", q);
+ write(i, template, strlen(template));
+ close (i);
+ p = getenv("EDITOR");
+ if (p == NULL)
+ p = "vi";
+ if (snprintf(cbuf, sizeof(cbuf), "%s %s\n", p, q) >=
+ (ssize_t)sizeof(cbuf)) {
+ unlink(q);
+ errx(1, "EDITOR is too long");
+ }
+ system(cbuf);
+ i = open(q, O_RDONLY);
+ if (i < 0)
+ err(1, "%s", f_opt);
+ params = properties_read(i);
+ close (i);
+ unlink(q);
+ free(q);
+ } else {
+ /* XXX: Hack */
+ i = open(_PATH_DEVNULL, O_RDONLY);
+ if (i < 0)
+ err(1, "%s", _PATH_DEVNULL);
+ params = properties_read(i);
+ close (i);
+ }
+
+ /* <sector_size> */
+ p = property_find(params, "sector_size");
+ i = ioctl(dfd, DIOCGSECTORSIZE, &u);
+ if (p != NULL) {
+ sector_size = strtoul(p, &q, 0);
+ if (!*p || *q)
+ errx(1, "sector_size not a proper number");
+ } else if (i == 0) {
+ sector_size = u;
+ } else {
+ errx(1, "Missing sector_size property");
+ }
+ if (sector_size & (sector_size - 1))
+ errx(1, "sector_size not a power of 2");
+ if (sector_size < 512)
+ errx(1, "sector_size is smaller than 512");
+ buf = malloc(sector_size);
+ if (buf == NULL)
+ err(1, "Failed to malloc sector buffer");
+ gl->sectorsize = sector_size;
+
+ i = ioctl(dfd, DIOCGMEDIASIZE, &off);
+ if (i == 0) {
+ first_sector = 0;
+ total_sectors = off / sector_size;
+ last_sector = total_sectors - 1;
+ } else {
+ first_sector = 0;
+ last_sector = 0;
+ total_sectors = 0;
+ }
+
+ /* <first_sector> */
+ p = property_find(params, "first_sector");
+ if (p != NULL) {
+ first_sector = strtoul(p, &q, 0);
+ if (!*p || *q)
+ errx(1, "first_sector not a proper number");
+ }
+
+ /* <last_sector> */
+ p = property_find(params, "last_sector");
+ if (p != NULL) {
+ last_sector = strtoul(p, &q, 0);
+ if (!*p || *q)
+ errx(1, "last_sector not a proper number");
+ if (last_sector <= first_sector)
+ errx(1, "last_sector not larger than first_sector");
+ total_sectors = last_sector + 1;
+ }
+
+ /* <total_sectors> */
+ p = property_find(params, "total_sectors");
+ if (p != NULL) {
+ total_sectors = strtoul(p, &q, 0);
+ if (!*p || *q)
+ errx(1, "total_sectors not a proper number");
+ if (last_sector == 0)
+ last_sector = first_sector + total_sectors - 1;
+ }
+
+ if (l_opt == NULL && first_sector != 0)
+ errx(1, "No -L new-lockfile argument and first_sector != 0");
+ else if (l_opt == NULL) {
+ first_sector++;
+ total_sectors--;
+ gl->flags |= GBDE_F_SECT0;
+ }
+ gl->sector0 = first_sector * gl->sectorsize;
+
+ if (total_sectors != (last_sector - first_sector) + 1)
+ errx(1, "total_sectors disagree with first_sector and last_sector");
+ if (total_sectors == 0)
+ errx(1, "missing last_sector or total_sectors");
+
+ gl->sectorN = (last_sector + 1) * gl->sectorsize;
+
+ /* Find a random keyoffset */
+ random_bits(&o, sizeof o);
+ o %= (gl->sectorN - gl->sector0);
+ o &= ~(gl->sectorsize - 1);
+ gl->keyoffset = o;
+
+ /* <number_of_keys> */
+ p = property_find(params, "number_of_keys");
+ if (p != NULL) {
+ nkeys = strtoul(p, &q, 0);
+ if (!*p || *q)
+ errx(1, "number_of_keys not a proper number");
+ if (nkeys < 1 || nkeys > G_BDE_MAXKEYS)
+ errx(1, "number_of_keys out of range");
+ } else {
+ nkeys = 4;
+ }
+ for (u = 0; u < nkeys; u++) {
+ for(;;) {
+ do {
+ random_bits(&o, sizeof o);
+ o %= gl->sectorN;
+ o &= ~(gl->sectorsize - 1);
+ } while(o < gl->sector0);
+ for (u2 = 0; u2 < u; u2++)
+ if (o == gl->lsector[u2])
+ break;
+ if (u2 < u)
+ continue;
+ break;
+ }
+ gl->lsector[u] = o;
+ }
+ for (; u < G_BDE_MAXKEYS; u++) {
+ do
+ random_bits(&o, sizeof o);
+ while (o < gl->sectorN);
+ gl->lsector[u] = o;
+ }
+ qsort(gl->lsector, G_BDE_MAXKEYS, sizeof gl->lsector[0], sorthelp);
+
+ /* Flush sector zero if we use it for lockfile data */
+ if (gl->flags & GBDE_F_SECT0) {
+ off2 = lseek(dfd, 0, SEEK_SET);
+ if (off2 != 0)
+ err(1, "lseek(2) to sector 0");
+ random_bits(buf, sector_size);
+ i = write(dfd, buf, sector_size);
+ if (i != (int)sector_size)
+ err(1, "write sector 0");
+ }
+
+ /* <random_flush> */
+ p = property_find(params, "random_flush");
+ if (p != NULL) {
+ off = first_sector * sector_size;
+ off2 = lseek(dfd, off, SEEK_SET);
+ if (off2 != off)
+ err(1, "lseek(2) to first_sector");
+ off2 = last_sector * sector_size;
+ while (off <= off2) {
+ random_bits(buf, sector_size);
+ i = write(dfd, buf, sector_size);
+ if (i != (int)sector_size)
+ err(1, "write to $device_name");
+ off += sector_size;
+ }
+ }
+
+ random_bits(gl->mkey, sizeof gl->mkey);
+ random_bits(gl->salt, sizeof gl->salt);
+
+ return;
+}
+
+static enum action {
+ ACT_HUH,
+ ACT_ATTACH, ACT_DETACH,
+ ACT_INIT, ACT_SETKEY, ACT_DESTROY, ACT_NUKE
+} action;
+
+int
+main(int argc, char **argv)
+{
+ const char *opts;
+ const char *l_opt, *L_opt;
+ const char *p_opt, *P_opt;
+ const char *f_opt;
+ char *dest;
+ int i_opt, n_opt, ch, dfd, doopen;
+ u_int nkey;
+ int i;
+ char *q, buf[BUFSIZ];
+ struct g_bde_key *gl;
+ struct g_bde_softc sc;
+
+ if (argc < 3)
+ usage();
+
+ if ((i = modfind("g_bde")) < 0) {
+ /* need to load the gbde module */
+ if (kldload(GBDEMOD) < 0 || modfind("g_bde") < 0)
+ err(1, GBDEMOD ": Kernel module not available");
+ }
+ doopen = 0;
+ if (!strcmp(argv[1], "attach")) {
+ action = ACT_ATTACH;
+ opts = "l:p:";
+ } else if (!strcmp(argv[1], "detach")) {
+ action = ACT_DETACH;
+ opts = "";
+ } else if (!strcmp(argv[1], "init")) {
+ action = ACT_INIT;
+ doopen = 1;
+ opts = "f:iL:P:";
+ } else if (!strcmp(argv[1], "setkey")) {
+ action = ACT_SETKEY;
+ doopen = 1;
+ opts = "l:L:n:p:P:";
+ } else if (!strcmp(argv[1], "destroy")) {
+ action = ACT_DESTROY;
+ doopen = 1;
+ opts = "l:p:";
+ } else if (!strcmp(argv[1], "nuke")) {
+ action = ACT_NUKE;
+ doopen = 1;
+ opts = "l:n:p:";
+ } else {
+ usage();
+ }
+ argc--;
+ argv++;
+
+ dest = strdup(argv[1]);
+ argc--;
+ argv++;
+
+ p_opt = NULL;
+ P_opt = NULL;
+ l_opt = NULL;
+ L_opt = NULL;
+ f_opt = NULL;
+ n_opt = 0;
+ i_opt = 0;
+
+ while((ch = getopt(argc, argv, opts)) != -1)
+ switch (ch) {
+ case 'f':
+ f_opt = optarg;
+ break;
+ case 'i':
+ i_opt = !i_opt;
+ case 'l':
+ l_opt = optarg;
+ break;
+ case 'L':
+ L_opt = optarg;
+ break;
+ case 'n':
+ n_opt = strtoul(optarg, &q, 0);
+ if (!*optarg || *q)
+ errx(1, "-n argument not numeric");
+ if (n_opt < -1 || n_opt > G_BDE_MAXKEYS)
+ errx(1, "-n argument out of range");
+ break;
+ case 'p':
+ p_opt = optarg;
+ break;
+ case 'P':
+ P_opt = optarg;
+ break;
+ default:
+ usage();
+ }
+
+ if (doopen) {
+ dfd = open(dest, O_RDWR);
+ if (dfd < 0 && dest[0] != '/') {
+ if (snprintf(buf, sizeof(buf), "%s%s",
+ _PATH_DEV, dest) >= (ssize_t)sizeof(buf))
+ errno = ENAMETOOLONG;
+ else
+ dfd = open(buf, O_RDWR);
+ }
+ if (dfd < 0)
+ err(1, "%s", dest);
+ } else {
+ if (!memcmp(dest, _PATH_DEV, strlen(_PATH_DEV)))
+ strcpy(dest, dest + strlen(_PATH_DEV));
+ }
+
+ memset(&sc, 0, sizeof sc);
+ sc.consumer = (void *)&dfd;
+ gl = &sc.key;
+ switch(action) {
+ case ACT_ATTACH:
+ setup_passphrase(&sc, 0, p_opt);
+ cmd_attach(&sc, dest, l_opt);
+ break;
+ case ACT_DETACH:
+ cmd_detach(dest);
+ break;
+ case ACT_INIT:
+ cmd_init(gl, dfd, f_opt, i_opt, L_opt);
+ setup_passphrase(&sc, 1, P_opt);
+ cmd_write(gl, &sc, dfd, 0, L_opt);
+ break;
+ case ACT_SETKEY:
+ setup_passphrase(&sc, 0, p_opt);
+ cmd_open(&sc, dfd, l_opt, &nkey);
+ if (n_opt == 0)
+ n_opt = nkey + 1;
+ setup_passphrase(&sc, 1, P_opt);
+ cmd_write(gl, &sc, dfd, n_opt - 1, L_opt);
+ break;
+ case ACT_DESTROY:
+ setup_passphrase(&sc, 0, p_opt);
+ cmd_open(&sc, dfd, l_opt, &nkey);
+ cmd_destroy(gl, nkey);
+ reset_passphrase(&sc);
+ cmd_write(gl, &sc, dfd, nkey, l_opt);
+ break;
+ case ACT_NUKE:
+ setup_passphrase(&sc, 0, p_opt);
+ cmd_open(&sc, dfd, l_opt, &nkey);
+ if (n_opt == 0)
+ n_opt = nkey + 1;
+ if (n_opt == -1) {
+ for(i = 0; i < G_BDE_MAXKEYS; i++)
+ cmd_nuke(gl, dfd, i);
+ } else {
+ cmd_nuke(gl, dfd, n_opt - 1);
+ }
+ break;
+ default:
+ errx(1, "internal error");
+ }
+
+ return(0);
+}
diff --git a/sbin/gbde/image.uu b/sbin/gbde/image.uu
new file mode 100644
index 0000000..82e6f2e
--- /dev/null
+++ b/sbin/gbde/image.uu
@@ -0,0 +1,3305 @@
+$FreeBSD$
+
+begin 644 gbde.image.bz2
+M0EIH.3%!629364"&9H\`Y1=_____________________________________
+M________X7"_"W7I7N^[5>OM;NXTY!ZK'VZ]]MS?=??>>OM=[?/I-WMSSFM;
+MO>[&RU[OMNKV=>\:^>K>]N;5WN];V*7F[O6[[[>G;WWRKZ]ZK7?7UZ7=[WN=
+M[?<5WWR[97V[M=U[>UAYYW'WNW<^^WUM??7U]UZ=>MZ?7KOOK[;WM[O>]'O;
+MKWU?;[Z^=*YZYNLNKYQ7KWO=Z]W.>OOO/HR>KV^N>L]>>Z\O;ZWWN[O=IOK*
+MT]>WUUS=V]:Y+[/OON]>[?9[GWS>][OK27KI]UK.N]]]WOJW=URR]L[[LU>\
+M77VSN]YM\]Z*]NW>S[GWONYONZ^[SI>^^YWN[6Z^YUF:Y]Z^[[=]=\NNY]Y;
+MO>^NEW>S=GGKW>^^[;?.[[7?;[?'7N.^YZ^UW/K[VY./:N\M]S:]WWV>^I>W
+M./3MVW>]GKR^]5K-=GG>^\MW'NWO>^^]?/7O7WO/?;O7UU[O?=]OO=<9@7V]
+M<\V=W5OOMS7>;[[Z^O/OO=ES[>7VON]Z^WE>]OI\^]ZE??+,[WG5FW33V;4/
+M:]O-[M:SUWWOENY[S*]>[/0]]O!=];G>MWEGOO:MW>K?7NW6NVHO6NMWOJ\^
+MOO?;K;UUM;>VV:Z>YV]T]ON9MCWGMN[WGNCWWW=[Z[5/O?5=??;WINOMG>TO
+MO+??;U?;XS=]VK;<W>UYYYSNOGWO/I[WTT=KV[JI[:]]WU[?:NFNO=W>GNZ]
+MFV]W>WGJ+S;OO>T=+Q[7UP[WKNOHR;C>M5?=SMNU==O>Y3[[?55WW;U\\U>=
+M[>^;NUU?5VNWGON^^>W3ZUKNNN[-WW>NVC4\QUO;N&77;>W>>=\Y].^^[[=]
+MZWW?;YTJ]BFW??=O3M[V[[Z^=Y?>^\L];;O?/OM\]MON[N.]Y[KSV[PWN]W=
+M+ZK=]/M]SO>]GN^[>]O??=U-Z=[OGWMQ][ZP3[[;OK[V=]W?:ZZ^]>]SO`.)
+M[?7>[=U7O>OOOEM]]W=?>VUE:-;,ON]Y][O5V^^][OK[[[V]Y]WVV]M>@Z.V
+M?7OFU\???=[[E=WM-/MRV/.9MJ)6>[[UMNWSOGQ]?/ON>]]OGE/'K;?=J[ZU
+M]?9=U\[O<T]H9OM]WJVN?7>ENZ[A[M[O.2]U[O<S-[7V^[YT^OI]W??/HO?<
+MZSXWWJ7N?>YW(^?;[?;S[)OO=][SV8O?<GS.]7OO?>WW9:WUWWWOGN>^^]/?
+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
+-^K_XNY(IPH2"!#,T>```
+`
+end
diff --git a/sbin/gbde/template.txt b/sbin/gbde/template.txt
new file mode 100644
index 0000000..3d22007
--- /dev/null
+++ b/sbin/gbde/template.txt
@@ -0,0 +1,32 @@
+# $FreeBSD$
+#
+# Sector size is the smallest unit of data which can be read or written.
+# Making it too small decreases performance and decreases available space.
+# Making it too large may prevent filesystems from working. 512 is the
+# minimum and always safe. For UFS, use the fragment size
+#
+sector_size = 512
+
+#
+# Start and end of the encrypted section of the partition. Specify in
+# sector numbers. If none specified, "all" will be assumed, to the
+# extent the value of this can be established.
+#
+#first_sector = 0
+#last_sector = 2879
+#total_sectors = 2880
+
+#
+# An encrypted partition can have more than one key. It may be a good idea
+# to make at least two keys, and save one of them for "just in case" use.
+# The minimum is obviously one and the maximum is 4.
+#
+number_of_keys = 4
+
+#
+# Flushing the partition with random bytes prevents a brute-force attack
+# from skipping sectors which obviously contains un-encrypted data.
+# NB: This variable is boolean, if it is present it means "yes" even if
+# you set it to the value "no"
+#
+#random_flush =
diff --git a/sbin/gbde/test.sh b/sbin/gbde/test.sh
new file mode 100644
index 0000000..67d8b38
--- /dev/null
+++ b/sbin/gbde/test.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+# $FreeBSD$
+
+set -e
+
+MD=99
+mdconfig -d -u $MD > /dev/null 2>&1 || true
+
+mdconfig -a -t malloc -s 1m -u $MD
+
+D=/dev/md$MD
+
+./gbde init $D -P foo -L /tmp/_l1
+./gbde setkey $D -p foo -l /tmp/_l1 -P bar -L /tmp/_l1
+./gbde setkey $D -p bar -l /tmp/_l1 -P foo -L /tmp/_l1
+
+./gbde setkey $D -p foo -l /tmp/_l1 -n 2 -P foo2 -L /tmp/_l2
+./gbde setkey $D -p foo2 -l /tmp/_l2 -n 3 -P foo3 -L /tmp/_l3
+./gbde setkey $D -p foo3 -l /tmp/_l3 -n 4 -P foo4 -L /tmp/_l4
+./gbde setkey $D -p foo4 -l /tmp/_l4 -n 1 -P foo1 -L /tmp/_l1
+
+./gbde nuke $D -p foo1 -l /tmp/_l1 -n 4
+if ./gbde nuke $D -p foo4 -l /tmp/_l4 -n 3 ; then false ; fi
+./gbde destroy $D -p foo2 -l /tmp/_l2
+if ./gbde destroy $D -p foo2 -l /tmp/_l2 ; then false ; fi
+
+./gbde nuke $D -p foo1 -l /tmp/_l1 -n -1
+if ./gbde nuke $D -p foo1 -l /tmp/_l1 -n -1 ; then false ; fi
+if ./gbde nuke $D -p foo2 -l /tmp/_l2 -n -1 ; then false ; fi
+if ./gbde nuke $D -p foo3 -l /tmp/_l3 -n -1 ; then false ; fi
+if ./gbde nuke $D -p foo4 -l /tmp/_l4 -n -1 ; then false ; fi
+
+./gbde init $D -P foo
+./gbde setkey $D -p foo -P bar
+./gbde setkey $D -p bar -P foo
+
+./gbde setkey $D -p foo -n 2 -P foo2
+./gbde setkey $D -p foo2 -n 3 -P foo3
+./gbde setkey $D -p foo3 -n 4 -P foo4
+./gbde setkey $D -p foo4 -n 1 -P foo1
+
+mdconfig -d -u $MD
+
+mdconfig -a -t malloc -s 1m -u $MD
+if [ -f image.uu ] ; then
+ uudecode -p image.uu | bzcat > $D
+else
+ uudecode -p ${1}/image.uu | bzcat > $D
+fi
+
+if [ `md5 < $D` != "a4066a739338d451b919e63f9ee4a12c" ] ; then
+ echo "Failed to set up md(4) device correctly"
+ exit 2
+fi
+
+./gbde attach $D -p foo
+fsck_ffs ${D}.bde
+./gbde detach $D
+mdconfig -d -u $MD
+
+
+echo "***********"
+echo "Test passed"
+echo "***********"
+exit 0
diff --git a/sbin/geom/Makefile b/sbin/geom/Makefile
new file mode 100644
index 0000000..95f00d9
--- /dev/null
+++ b/sbin/geom/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= core class
+
+.include <bsd.subdir.mk>
diff --git a/sbin/geom/Makefile.inc b/sbin/geom/Makefile.inc
new file mode 100644
index 0000000..94ca09f
--- /dev/null
+++ b/sbin/geom/Makefile.inc
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+WARNS?= 6
+CLASS_DIR?=/lib/geom
+
+.include "../Makefile.inc"
diff --git a/sbin/geom/class/Makefile b/sbin/geom/class/Makefile
new file mode 100644
index 0000000..e172bbf
--- /dev/null
+++ b/sbin/geom/class/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+SUBDIR= concat
+.if !defined(NO_CRYPT) && !defined(NO_OPENSSL)
+SUBDIR+=eli
+.endif
+SUBDIR+=label
+SUBDIR+=mirror
+SUBDIR+=nop
+SUBDIR+=raid3
+SUBDIR+=shsec
+SUBDIR+=stripe
+
+.include <bsd.subdir.mk>
diff --git a/sbin/geom/class/Makefile.inc b/sbin/geom/class/Makefile.inc
new file mode 100644
index 0000000..63109c2
--- /dev/null
+++ b/sbin/geom/class/Makefile.inc
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+SHLIBDIR?=${CLASS_DIR}
+SHLIB_NAME?=geom_${CLASS}.so
+LINKS= ${BINDIR}/geom ${BINDIR}/g${CLASS}
+MAN= g${CLASS}.8
+SRCS+= geom_${CLASS}.c subr.c
+
+CFLAGS+= -I${.CURDIR}/../..
+
+.include "../Makefile.inc"
diff --git a/sbin/geom/class/concat/Makefile b/sbin/geom/class/concat/Makefile
new file mode 100644
index 0000000..b3a3e23
--- /dev/null
+++ b/sbin/geom/class/concat/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../misc
+
+CLASS= concat
+
+.include <bsd.lib.mk>
diff --git a/sbin/geom/class/concat/gconcat.8 b/sbin/geom/class/concat/gconcat.8
new file mode 100644
index 0000000..2e1b79d
--- /dev/null
+++ b/sbin/geom/class/concat/gconcat.8
@@ -0,0 +1,197 @@
+.\" Copyright (c) 2004-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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 21, 2004
+.Dt GCONCAT 8
+.Os
+.Sh NAME
+.Nm gconcat
+.Nd "disk concatenation control utility"
+.Sh SYNOPSIS
+.Nm
+.Cm create
+.Op Fl v
+.Ar name
+.Ar prov ...
+.Nm
+.Cm destroy
+.Op Fl fv
+.Ar name ...
+.Nm
+.Cm label
+.Op Fl hv
+.Ar name
+.Ar prov ...
+.Nm
+.Cm stop
+.Op Fl fv
+.Ar name ...
+.Nm
+.Cm clear
+.Op Fl v
+.Ar prov ...
+.Nm
+.Cm dump
+.Ar prov ...
+.Nm
+.Cm list
+.Nm
+.Cm status
+.Nm
+.Cm load
+.Nm
+.Cm unload
+.Sh DESCRIPTION
+The
+.Nm
+utility is used for device concatenation configuration.
+The concatenation can be configured using two different methods:
+.Dq manual
+or
+.Dq automatic .
+When using the
+.Dq manual
+method, no metadata are stored on the devices, so the concatenated
+device has to be configured by hand every time it is needed.
+The
+.Dq automatic
+method uses on-disk metadata to detect devices.
+Once devices are labeled, they will be automatically detected and
+configured.
+.Pp
+The first argument to
+.Nm
+indicates an action to be performed:
+.Bl -tag -width ".Cm destroy"
+.It Cm create
+Concatenate the given devices with specified
+.Ar name .
+This is the
+.Dq manual
+method.
+The kernel module
+.Pa geom_concat.ko
+will be loaded if it is not loaded already.
+.It Cm label
+Concatenate the given devices with the specified
+.Ar name .
+This is the
+.Dq automatic
+method, where metadata are stored in every device's last sector.
+The kernel module
+.Pa geom_concat.ko
+will be loaded if it is not loaded already.
+.It Cm stop
+Turn off existing concatenate device by its
+.Ar name .
+This command does not touch on-disk metadata!
+.It Cm destroy
+Same as
+.Cm stop .
+.It Cm clear
+Clear metadata on the given devices.
+.It Cm dump
+Dump metadata stored on the given devices.
+.It Cm list
+See
+.Xr geom 8 .
+.It Cm status
+See
+.Xr geom 8 .
+.It Cm load
+See
+.Xr geom 8 .
+.It Cm unload
+See
+.Xr geom 8 .
+.El
+.Pp
+Additional options:
+.Bl -tag -width indent
+.It Fl f
+Force the removal of the specified concatenated device.
+.It Fl h
+Hardcode providers' names in metadata.
+.It Fl v
+Be more verbose.
+.El
+.Sh SYSCTL VARIABLES
+The following
+.Xr sysctl 8
+variables can be used to control the behavior of the
+.Nm CONCAT
+GEOM class.
+The default value is shown next to each variable.
+.Bl -tag -width indent
+.It Va kern.geom.concat.debug : No 0
+Debug level of the
+.Nm CONCAT
+GEOM class.
+This can be set to a number between 0 and 3 inclusive.
+If set to 0 minimal debug information is printed, and if set to 3 the
+maximum amount of debug information is printed.
+.El
+.Sh EXIT STATUS
+Exit status is 0 on success, and 1 if the command fails.
+.Sh EXAMPLES
+The following example shows how to configure four disks for automatic
+concatenation, create a file system on it, and mount it:
+.Bd -literal -offset indent
+gconcat label -v data /dev/da0 /dev/da1 /dev/da2 /dev/da3
+newfs /dev/concat/data
+mount /dev/concat/data /mnt
+[...]
+umount /mnt
+gconcat stop data
+gconcat unload
+.Ed
+.Pp
+Configure concatenated provider on one disk only.
+Create file system.
+Add two more disks and extend existing file system.
+.Bd -literal -offset indent
+gconcat label data /dev/da0
+newfs /dev/concat/data
+gconcat label data /dev/da0 /dev/da1 /dev/da2
+growfs /dev/concat/data
+.Ed
+.Sh SEE ALSO
+.Xr geom 4 ,
+.Xr loader.conf 5 ,
+.Xr geom 8 ,
+.Xr growfs 8 ,
+.Xr mount 8 ,
+.Xr newfs 8 ,
+.Xr sysctl 8 ,
+.Xr umount 8 ,
+.Xr vinum 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 5.3 .
+.Sh AUTHORS
+.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org
diff --git a/sbin/geom/class/concat/geom_concat.c b/sbin/geom/class/concat/geom_concat.c
new file mode 100644
index 0000000..4157cea
--- /dev/null
+++ b/sbin/geom/class/concat/geom_concat.c
@@ -0,0 +1,247 @@
+/*-
+ * Copyright (c) 2004-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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <errno.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <assert.h>
+#include <libgeom.h>
+#include <geom/concat/g_concat.h>
+
+#include "core/geom.h"
+#include "misc/subr.h"
+
+
+uint32_t lib_version = G_LIB_VERSION;
+uint32_t version = G_CONCAT_VERSION;
+
+static void concat_main(struct gctl_req *req, unsigned flags);
+static void concat_clear(struct gctl_req *req);
+static void concat_dump(struct gctl_req *req);
+static void concat_label(struct gctl_req *req);
+
+struct g_command class_commands[] = {
+ { "clear", G_FLAG_VERBOSE, concat_main, G_NULL_OPTS,
+ "[-v] prov ..."
+ },
+ { "create", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, G_NULL_OPTS,
+ "[-v] name prov ..."
+ },
+ { "destroy", G_FLAG_VERBOSE, NULL,
+ {
+ { 'f', "force", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-fv] name ..."
+ },
+ { "dump", 0, concat_main, G_NULL_OPTS,
+ "prov ..."
+ },
+ { "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, concat_main,
+ {
+ { 'h', "hardcode", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-hv] name prov ..."
+ },
+ { "stop", G_FLAG_VERBOSE, NULL,
+ {
+ { 'f', "force", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-fv] name ..."
+ },
+ G_CMD_SENTINEL
+};
+
+static int verbose = 0;
+
+static void
+concat_main(struct gctl_req *req, unsigned flags)
+{
+ const char *name;
+
+ if ((flags & G_FLAG_VERBOSE) != 0)
+ verbose = 1;
+
+ name = gctl_get_ascii(req, "verb");
+ if (name == NULL) {
+ gctl_error(req, "No '%s' argument.", "verb");
+ return;
+ }
+ if (strcmp(name, "label") == 0)
+ concat_label(req);
+ else if (strcmp(name, "clear") == 0)
+ concat_clear(req);
+ else if (strcmp(name, "dump") == 0)
+ concat_dump(req);
+ else
+ gctl_error(req, "Unknown command: %s.", name);
+}
+
+static void
+concat_label(struct gctl_req *req)
+{
+ struct g_concat_metadata md;
+ u_char sector[512];
+ const char *name;
+ int error, i, hardcode, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 2) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+ hardcode = gctl_get_int(req, "hardcode");
+
+ /*
+ * Clear last sector first to spoil all components if device exists.
+ */
+ for (i = 1; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_clear(name, NULL);
+ if (error != 0) {
+ gctl_error(req, "Can't store metadata on %s: %s.", name,
+ strerror(error));
+ return;
+ }
+ }
+
+ strlcpy(md.md_magic, G_CONCAT_MAGIC, sizeof(md.md_magic));
+ md.md_version = G_CONCAT_VERSION;
+ name = gctl_get_ascii(req, "arg0");
+ strlcpy(md.md_name, name, sizeof(md.md_name));
+ md.md_id = arc4random();
+ md.md_all = nargs - 1;
+
+ /*
+ * Ok, store metadata.
+ */
+ for (i = 1; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ md.md_no = i - 1;
+ if (!hardcode)
+ bzero(md.md_provider, sizeof(md.md_provider));
+ else {
+ if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) == 0)
+ name += strlen(_PATH_DEV);
+ strlcpy(md.md_provider, name, sizeof(md.md_provider));
+ }
+ md.md_provsize = g_get_mediasize(name);
+ if (md.md_provsize == 0) {
+ fprintf(stderr, "Can't get mediasize of %s: %s.\n",
+ name, strerror(errno));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ concat_metadata_encode(&md, sector);
+ error = g_metadata_store(name, sector, sizeof(sector));
+ if (error != 0) {
+ fprintf(stderr, "Can't store metadata on %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (verbose)
+ printf("Metadata value stored on %s.\n", name);
+ }
+}
+
+static void
+concat_clear(struct gctl_req *req)
+{
+ const char *name;
+ int error, i, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+
+ for (i = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_clear(name, G_CONCAT_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Can't clear metadata on %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (verbose)
+ printf("Metadata cleared on %s.\n", name);
+ }
+}
+
+static void
+concat_metadata_dump(const struct g_concat_metadata *md)
+{
+
+ printf(" Magic string: %s\n", md->md_magic);
+ printf(" Metadata version: %u\n", (u_int)md->md_version);
+ printf(" Device name: %s\n", md->md_name);
+ printf(" Device ID: %u\n", (u_int)md->md_id);
+ printf(" Disk number: %u\n", (u_int)md->md_no);
+ printf("Total number of disks: %u\n", (u_int)md->md_all);
+ printf(" Hardcoded provider: %s\n", md->md_provider);
+}
+
+static void
+concat_dump(struct gctl_req *req)
+{
+ struct g_concat_metadata md, tmpmd;
+ const char *name;
+ int error, i, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+
+ for (i = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_read(name, (u_char *)&tmpmd, sizeof(tmpmd),
+ G_CONCAT_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Can't read metadata from %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ concat_metadata_decode((u_char *)&tmpmd, &md);
+ printf("Metadata on %s:\n", name);
+ concat_metadata_dump(&md);
+ printf("\n");
+ }
+}
diff --git a/sbin/geom/class/eli/Makefile b/sbin/geom/class/eli/Makefile
new file mode 100644
index 0000000..041e738
--- /dev/null
+++ b/sbin/geom/class/eli/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../misc ${.CURDIR}/../../../../sys/geom/eli ${.CURDIR}/../../../../sys/crypto/sha2
+
+CLASS= eli
+SRCS= g_eli_crypto.c
+SRCS+= g_eli_key.c
+SRCS+= pkcs5v2.c
+SRCS+= sha2.c
+
+DPADD= ${LIBMD} ${LIBCRYPTO}
+LDADD= -lmd -lcrypto
+
+WARNS?= 3
+
+CFLAGS+=-I${.CURDIR}/../../../../sys
+
+.include <bsd.lib.mk>
diff --git a/sbin/geom/class/eli/geli.8 b/sbin/geom/class/eli/geli.8
new file mode 100644
index 0000000..e04d066
--- /dev/null
+++ b/sbin/geom/class/eli/geli.8
@@ -0,0 +1,527 @@
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 11, 2005
+.Dt GELI 8
+.Os
+.Sh NAME
+.Nm geli
+.Nd "control utility for cryptographic GEOM class"
+.Sh SYNOPSIS
+To compile GEOM_ELI into your kernel, place the following lines in your kernel
+configuration file:
+.Bd -ragged -offset indent
+.Cd "device crypto"
+.Cd "options GEOM_ELI"
+.Ed
+.Pp
+Alternately, to load the GEOM_ELI module at boot time, place the following line
+in your
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+geom_eli_load="YES"
+.Ed
+.Pp
+Usage of the
+.Xr geli 8
+utility:
+.Pp
+.Nm
+.Cm init
+.Op Fl bPv
+.Op Fl a Ar algo
+.Op Fl i Ar iterations
+.Op Fl K Ar newkeyfile
+.Op Fl l Ar keylen
+.Op Fl s Ar sectorsize
+.Ar prov
+.Nm
+.Cm label - an alias for
+.Cm init
+.Nm
+.Cm attach
+.Op Fl dpv
+.Op Fl k Ar keyfile
+.Ar prov
+.Nm
+.Cm detach
+.Op Fl fl
+.Ar prov ...
+.Nm
+.Cm stop - an alias for
+.Cm detach
+.Nm
+.Cm onetime
+.Op Fl d
+.Op Fl a Ar algo
+.Op Fl l Ar keylen
+.Op Fl s Ar sectorsize
+.Ar prov ...
+.Nm
+.Cm setkey
+.Op Fl pPv
+.Op Fl i Ar iterations
+.Op Fl k Ar keyfile
+.Op Fl K Ar newkeyfile
+.Op Fl n Ar keyno
+.Ar prov
+.Nm
+.Cm delkey
+.Op Fl afv
+.Op Fl n Ar keyno
+.Ar prov
+.Nm
+.Cm kill
+.Op Fl av
+.Op Ar prov ...
+.Nm
+.Cm backup
+.Op Fl v
+.Ar prov
+.Ar file
+.Nm
+.Cm restore
+.Op Fl v
+.Ar file
+.Ar prov
+.Nm
+.Cm clear
+.Op Fl v
+.Ar prov ...
+.Nm
+.Cm dump
+.Op Fl v
+.Ar prov ...
+.Nm
+.Cm list
+.Nm
+.Cm status
+.Nm
+.Cm load
+.Nm
+.Cm unload
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to configure encryption on GEOM providers.
+.Pp
+The following is a list of the most important features:
+.Pp
+.Bl -bullet -offset indent -compact
+.It
+Utilizes the
+.Xr crypto 9
+framework, so when there is crypto hardware available,
+.Nm
+will make use of it automatically.
+.It
+Supports many cryptographic algorithms (currently
+.Nm AES ,
+.Nm Blowfish
+and
+.Nm 3DES ) .
+.It
+Can create a key from a couple of components (user entered passphrase, random
+bits from a file, etc.).
+.It
+Allows to encrypt the root partition - the user will be asked for the
+passphrase before the root file system is mounted.
+.It
+The passphrase of the user is strengthened with:
+.Rs
+.%A B. Kaliski
+.%T "PKCS #5: Password-Based Cryptography Specification, Version 2.0."
+.%R RFC
+.%N 2898
+.Re
+.It
+Allows to use two independent keys (e.g.
+.Qq "user key"
+and
+.Qq "company key" ) .
+.It
+It is fast -
+.Nm
+performs simple sector-to-sector encryption.
+.It
+Allows to backup/restore Master Keys, so when a user has to quickly
+destroy his keys,
+it is possible to get the data back by restoring keys from the backup.
+.It
+Providers can be configured to automatically detach on last close
+(so users don't have to remember to detach providers after unmounting
+the file systems).
+.It
+Allows to attach a provider with a random, one-time key - useful for swap
+partitions and temporary file systems.
+.El
+.Pp
+The first argument to
+.Nm
+indicates an action to be performed:
+.Bl -tag -width ".Cm onetime"
+.It Cm init
+Initialize provider which needs to be encrypted.
+Here you can set up the cryptographic algorithm to use, key length, etc.
+The last provider's sector is used to store metadata.
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl a Ar algo"
+.It Fl a Ar algo
+Encryption algorithm to use.
+Currently supported algorithms are:
+.Nm AES ,
+.Nm Blowfish
+and
+.Nm 3DES .
+The default is
+.Nm AES .
+.It Fl b
+Ask for the passphrase on boot, before the root partition is mounted.
+This makes it possible to use an encrypted root partition.
+One will still need bootable unencrypted storage with a
+.Pa /boot/
+directory, which can be a CD-ROM disc or USB pen-drive, that can be removed
+after boot.
+.It Fl i Ar iterations
+Number of iterations to use with PKCS#5v2.
+If this option is not specified,
+.Nm
+will find the number of iterations which is equal to 2 seconds of crypto work.
+If 0 is given, PKCS#5v2 will not be used.
+.It Fl K Ar newkeyfile
+Specifies a file which contains part of the key.
+If
+.Ar newkeyfile
+is given as -, standard input will be used.
+Here is how more than one file with a key component can be used:
+.Bd -literal -offset indent
+# cat key1 key2 key3 | geli init -K - /dev/da0
+.Ed
+.It Fl l Ar keylen
+Key length to use with the given cryptographic algorithm.
+If not given, the default key length for the given algorithm is used, which is:
+128 for
+.Nm AES ,
+128 for
+.Nm Blowfish
+and 192 for
+.Nm 3DES .
+.It Fl s Ar sectorsize
+Change decrypted provider's sector size.
+Increasing sector size allows to increase performance, because we need to
+generate an IV and do encrypt/decrypt for every single sector - less number
+of sectors means less work to do.
+.It Fl P
+Do not use passphrase as the key component.
+.El
+.It Cm attach
+Attach the given provider.
+The master key will be decrypted using the given
+passphrase/keyfile and a new GEOM provider will be created using the given
+provider's name with an
+.Qq .eli
+suffix.
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl a Ar algo"
+.It Fl d
+If specified, a decrypted provider will be detached automatically on last close.
+This can help with short memory - user doesn't have to remember to detach the
+provider after unmounting the file system.
+It only works when the provider was opened for writing, so it will not work if
+the file system on the provider is mounted read-only.
+Probably a better choice is the
+.Fl l
+option for the
+.Cm detach
+subcommand.
+.It Fl k Ar keyfile
+Specifies a file which contains part of the key.
+For more information see the description of the
+.Fl K
+option for the
+.Cm init
+subcommand.
+.It Fl p
+Do not use passphrase as the key component.
+.El
+.It Cm detach
+Detach the given providers, which means remove the devfs entry
+and clear the keys from memory.
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl a Ar algo"
+.It Fl f
+Force detach - detach even if the provider is open.
+.It Fl l
+Mark provider to detach on last close.
+If this option is specified, the provider will not be detached
+until it is open, but when it will be closed last time, it will
+be automatically detached (even
+if it was only opened for reading).
+.El
+.It Cm onetime
+Attach the given providers with random, one-time keys.
+The command can be used to encrypt swap partitions or temporary file systems.
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl a Ar algo"
+.It Fl a Ar algo
+Encryption algorithm to use.
+For more information, see the description of the
+.Cm init
+subcommand.
+.It Fl d
+Detach on last close.
+Note, the option is not usable for temporary file systems as the provider will
+be detached after creating the file system on it.
+It still can (and should be) used for swap partitions.
+For more information, see the description of the
+.Cm attach
+subcommand.
+.It Fl l Ar keylen
+Key length to use with the given cryptographic algorithm.
+For more information, see the description of the
+.Cm init
+subcommand.
+.It Fl s Ar sectorsize
+Change decrypted provider's sector size.
+For more information, see the description of the
+.Cm init
+subcommand.
+.El
+.It Cm setkey
+Change or setup (if not yet initialized) selected key.
+There is one master key, which can be encrypted with two independent user keys.
+With the
+.Cm init
+subcommand, only key number 0 is initialized.
+The key can always be changed: for an attached provider,
+for a detached provider or on the backup file.
+When a provider is attached, the user does not have to provide
+an old passphrase/keyfile.
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl a Ar algo"
+.It Fl i Ar iterations
+Number of iterations to use with PKCS#5v2.
+If 0 is given, PKCS#5v2 will not be used.
+To be able to use this option with
+.Cm setkey
+subcommand, only one key have to be defined and this key has to be changed.
+.It Fl k Ar keyfile
+Specifies a file which contains part of the old key.
+.It Fl K Ar newkeyfile
+Specifies a file which contains part of the new key.
+.It Fl n Ar keyno
+Specifies the number of the key to change (could be 0 or 1).
+If the provider is attached and no key number is given, the key
+used for attaching the provider will be changed.
+If the provider is detached (or we are operating on a backup file)
+and no key number is given, the key decrypted with the passphrase/keyfile
+will be changed.
+.It Fl p
+Do not use passphrase as the old key component.
+.It Fl P
+Do not use passphrase as the new key component.
+.El
+.It Cm delkey
+Destroy (overwrite with random data) the selected key.
+If one is destroying keys for an attached provider, the provider
+will not be detached even if all keys will be destroyed.
+It can be even rescued with the
+.Cm setkey
+subcommand.
+.Bl -tag -width ".Fl a Ar algo"
+.It Fl a
+Destroy all keys (does not need
+.Fl f
+option).
+.It Fl f
+Force key destruction.
+This option is needed to destroy the last key.
+.It Fl n Ar keyno
+Specifies the key number.
+If the provider is attached and no key number is given, the key
+used for attaching the provider will be destroyed.
+If provider is detached (or we are operating on a backup file) the key number
+has to be given.
+.El
+.It Cm kill
+This command should be used in emergency situations.
+It will destroy all keys on the given provider and will detach it forcibly
+(if it is attached).
+This is absolutely a one-way command - if you do not have a metadata
+backup, your data is gone for good.
+.Bl -tag -width ".Fl a Ar algo"
+.It Fl a
+If specified, all currently attached providers will be killed.
+.El
+.It Cm backup
+Backup metadata from the given provider to the given file.
+.It Cm restore
+Restore metadata from the given file to the given provider.
+.It Cm clear
+Clear metadata from the given providers.
+.It Cm dump
+Dump metadata stored on the given providers.
+.It Cm list
+See
+.Xr geom 8 .
+.It Cm status
+See
+.Xr geom 8 .
+.It Cm load
+See
+.Xr geom 8 .
+.It Cm unload
+See
+.Xr geom 8 .
+.El
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl v"
+.It Fl v
+Be more verbose.
+.El
+.Sh SYSCTL VARIABLES
+The following
+.Xr sysctl 8
+variables can be used to control the behavior of the
+.Nm ELI
+GEOM class.
+The default value is shown next to each variable.
+.Bl -tag -width indent
+.It Va kern.geom.eli.debug : No 0
+Debug level of the
+.Nm ELI
+GEOM class.
+This can be set to a number between 0 and 3 inclusive.
+If set to 0, minimal debug information is printed.
+If set to 3, the
+maximum amount of debug information is printed.
+This variable could be set in
+.Pa /boot/loader.conf .
+.It Va kern.geom.eli.tries : No 3
+Number of times a user is asked for the passphrase.
+This is only used for providers which should be attached on boot
+(before the root file system is mounted).
+If set to 0, attaching providers on boot will be disabled.
+This variable should be set in
+.Pa /boot/loader.conf .
+.It Va kern.geom.eli.overwrites : No 5
+Specifies how many times the Master-Key will be overwritten
+with random values when it is destroyed.
+After this operation it is filled with zeros.
+.It Va kern.geom.eli.visible_passphrase : No 0
+If set to 1, the passphrase entered on boot (before the root
+file system is mounted) will be visible.
+This possibility should be used with caution as the entered
+passphrase can be logged and exposed via
+.Xr dmesg 8 .
+This variable should be set in
+.Pa /boot/loader.conf .
+.It Va kern.geom.eli.threads : No 0
+Specifies how many kernel threads should be used for doing software
+cryptography.
+Its purpose is to increase performance on SMP systems.
+If hardware acceleration is available, only one thread will be started.
+If set to 0, CPU-bound thread will be started for every active CPU.
+This variable could be set in
+.Pa /boot/loader.conf .
+.El
+.Sh EXIT STATUS
+Exit status is 0 on success, and 1 if the command fails.
+.Sh EXAMPLES
+Initialize a provider which is going to be encrypted with a
+passphrase and random data from a file on the user's pen drive.
+Use 4kB sector size.
+Attach the provider, create a file system and mount it.
+Do the work.
+Unmount the provider and detach it:
+.Bd -literal -offset indent
+# dd if=/dev/random of=/mnt/pendrive/da2.key bs=64 count=1
+# geli init -s 4096 -K /mnt/pendrive/da2.key /dev/da2
+Enter new passphrase:
+Reenter new passphrase:
+# geli attach -k /mnt/pendrive/da2.key /dev/da2
+Enter passphrase:
+# dd if=/dev/random of=/dev/da2.eli bs=1m
+# newfs /dev/da2.eli
+# mount /dev/da2.eli /mnt/secret
+\&...
+# umount /mnt/secret
+# geli detach da2.eli
+.Ed
+.Pp
+Create an encrypted provider, but use two keys:
+one for your girlfriend and one for
+you (so there will be no tragedy if she forgets her passphrase):
+.Bd -literal -offset indent
+# geli init /dev/da2
+Enter new passphrase: (enter your passphrase)
+Reenter new passphrase:
+# geli setkey -n 1 /dev/da2
+Enter passphrase: (enter your passphrase)
+Enter new passphrase: (let your girlfriend enter her passphrase ...)
+Reenter new passphrase: (... twice)
+.Ed
+.Pp
+You are the security-person in your company.
+Create an encrypted provider for use by the user, but remember that users
+forget their passphrases, so back Master Key up with your own random key:
+.Bd -literal -offset indent
+# dd if=/dev/random of=/mnt/pendrive/keys/`hostname` bs=64 count=1
+# geli init -P -K /mnt/pendrive/keys/`hostname` /dev/ad0s1e
+# geli backup /dev/ad0s1e /mnt/pendrive/backups/`hostname`
+(use key number 0, so the encrypted Master Key by you will be overwritten)
+# geli setkey -n 0 -k /mnt/pendrive/keys/`hostname` /dev/ad0s1e
+(allow the user to enter his passphrase)
+Enter new passphrase:
+Reenter new passphrase:
+.Ed
+.Pp
+Encrypted swap partition setup:
+.Bd -literal -offset indent
+# dd if=/dev/random of=/dev/ad0s1b bs=1m
+# geli onetime -d -a 3des ad0s1b
+# swapon /dev/ad0s1b.eli
+.Ed
+.Sh SEE ALSO
+.Xr crypto 4 ,
+.Xr gbde 4 ,
+.Xr geom 4 ,
+.Xr gbde 8 ,
+.Xr geom 8 ,
+.Xr crypto 9
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 6.0 .
+.Sh AUTHORS
+.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org
diff --git a/sbin/geom/class/eli/geom_eli.c b/sbin/geom/class/eli/geom_eli.c
new file mode 100644
index 0000000..0f07619
--- /dev/null
+++ b/sbin/geom/class/eli/geom_eli.c
@@ -0,0 +1,1133 @@
+/*-
+ * Copyright (c) 2004 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <readpassphrase.h>
+#include <string.h>
+#include <strings.h>
+#include <libgeom.h>
+#include <paths.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <opencrypto/cryptodev.h>
+#include <geom/eli/g_eli.h>
+#include <geom/eli/pkcs5v2.h>
+
+#include "core/geom.h"
+#include "misc/subr.h"
+
+
+uint32_t lib_version = G_LIB_VERSION;
+uint32_t version = G_ELI_VERSION;
+
+static char algo[] = "aes";
+static intmax_t keylen = 0;
+static intmax_t keyno = -1;
+static intmax_t iterations = -1;
+static intmax_t sectorsize = 0;
+static char keyfile[] = "", newkeyfile[] = "";
+
+static void eli_main(struct gctl_req *req, unsigned flags);
+static void eli_init(struct gctl_req *req);
+static void eli_attach(struct gctl_req *req);
+static void eli_setkey(struct gctl_req *req);
+static void eli_delkey(struct gctl_req *req);
+static void eli_kill(struct gctl_req *req);
+static void eli_backup(struct gctl_req *req);
+static void eli_restore(struct gctl_req *req);
+static void eli_clear(struct gctl_req *req);
+static void eli_dump(struct gctl_req *req);
+
+/*
+ * Available commands:
+ *
+ * init [-bhPv] [-a algo] [-i iterations] [-l keylen] [-K newkeyfile] prov
+ * label - alias for 'init'
+ * attach [-dpv] [-k keyfile] prov
+ * detach [-fl] prov ...
+ * stop - alias for 'detach'
+ * onetime [-d] [-a algo] [-l keylen] prov ...
+ * setkey [-pPv] [-n keyno] [-k keyfile] [-K newkeyfile] prov
+ * delkey [-afv] [-n keyno] prov
+ * kill [-av] [prov ...]
+ * backup [-v] prov file
+ * restore [-v] file prov
+ * clear [-v] prov ...
+ * dump [-v] prov ...
+ */
+struct g_command class_commands[] = {
+ { "init", G_FLAG_VERBOSE, eli_main,
+ {
+ { 'a', "algo", algo, G_TYPE_STRING },
+ { 'b', "boot", NULL, G_TYPE_NONE },
+ { 'i', "iterations", &iterations, G_TYPE_NUMBER },
+ { 'K', "newkeyfile", newkeyfile, G_TYPE_STRING },
+ { 'l', "keylen", &keylen, G_TYPE_NUMBER },
+ { 'P', "nonewpassphrase", NULL, G_TYPE_NONE },
+ { 's', "sectorsize", &sectorsize, G_TYPE_NUMBER },
+ G_OPT_SENTINEL
+ },
+ "[-bPv] [-a algo] [-i iterations] [-l keylen] [-K newkeyfile] [-s sectorsize] prov"
+ },
+ { "label", G_FLAG_VERBOSE, eli_main,
+ {
+ { 'a', "algo", algo, G_TYPE_STRING },
+ { 'b', "boot", NULL, G_TYPE_NONE },
+ { 'i', "iterations", &iterations, G_TYPE_NUMBER },
+ { 'K', "newkeyfile", newkeyfile, G_TYPE_STRING },
+ { 'l', "keylen", &keylen, G_TYPE_NUMBER },
+ { 'P', "nonewpassphrase", NULL, G_TYPE_NONE },
+ { 's', "sectorsize", &sectorsize, G_TYPE_NUMBER },
+ G_OPT_SENTINEL
+ },
+ "- an alias for 'init'"
+ },
+ { "attach", G_FLAG_VERBOSE | G_FLAG_LOADKLD, eli_main,
+ {
+ { 'd', "detach", NULL, G_TYPE_NONE },
+ { 'k', "keyfile", keyfile, G_TYPE_STRING },
+ { 'p', "nopassphrase", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-dpv] [-k keyfile] prov"
+ },
+ { "detach", 0, NULL,
+ {
+ { 'f', "force", NULL, G_TYPE_NONE },
+ { 'l', "last", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-fl] prov ..."
+ },
+ { "stop", 0, NULL,
+ {
+ { 'f', "force", NULL, G_TYPE_NONE },
+ { 'l', "last", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "- an alias for 'detach'"
+ },
+ { "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL,
+ {
+ { 'a', "algo", algo, G_TYPE_STRING },
+ { 'd', "detach", NULL, G_TYPE_NONE },
+ { 'l', "keylen", &keylen, G_TYPE_NUMBER },
+ { 's', "sectorsize", &sectorsize, G_TYPE_NUMBER },
+ G_OPT_SENTINEL
+ },
+ "[-d] [-a algo] [-l keylen] [-s sectorsize] prov ..."
+ },
+ { "setkey", G_FLAG_VERBOSE, eli_main,
+ {
+ { 'i', "iterations", &iterations, G_TYPE_NUMBER },
+ { 'k', "keyfile", keyfile, G_TYPE_STRING },
+ { 'K', "newkeyfile", newkeyfile, G_TYPE_STRING },
+ { 'n', "keyno", &keyno, G_TYPE_NUMBER },
+ { 'p', "nopassphrase", NULL, G_TYPE_NONE },
+ { 'P', "nonewpassphrase", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-pPv] [-n keyno] [-i iterations] [-k keyfile] [-K newkeyfile] prov"
+ },
+ { "delkey", G_FLAG_VERBOSE, eli_main,
+ {
+ { 'a', "all", NULL, G_TYPE_NONE },
+ { 'f', "force", NULL, G_TYPE_NONE },
+ { 'n', "keyno", &keyno, G_TYPE_NUMBER },
+ G_OPT_SENTINEL
+ },
+ "[-afv] [-n keyno] prov"
+ },
+ { "kill", G_FLAG_VERBOSE, eli_main,
+ {
+ { 'a', "all", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-av] [prov ...]"
+ },
+ { "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
+ "[-v] prov file"
+ },
+ { "restore", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
+ "[-v] file prov"
+ },
+ { "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
+ "[-v] prov ..."
+ },
+ { "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
+ "[-v] prov ..."
+ },
+ G_CMD_SENTINEL
+};
+
+static int verbose = 0;
+
+static int
+eli_protect(struct gctl_req *req)
+{
+ struct rlimit rl;
+
+ /* Disable core dumps. */
+ rl.rlim_cur = 0;
+ rl.rlim_max = 0;
+ if (setrlimit(RLIMIT_CORE, &rl) == -1) {
+ gctl_error(req, "Cannot disable core dumps: %s.",
+ strerror(errno));
+ return (-1);
+ }
+ /* Disable swapping. */
+ if (mlockall(MCL_FUTURE) == -1) {
+ gctl_error(req, "Cannot lock memory: %s.", strerror(errno));
+ return (-1);
+ }
+ return (0);
+}
+
+static void
+eli_main(struct gctl_req *req, unsigned flags)
+{
+ const char *name;
+
+ if (eli_protect(req) == -1)
+ return;
+
+ if ((flags & G_FLAG_VERBOSE) != 0)
+ verbose = 1;
+
+ name = gctl_get_ascii(req, "verb");
+ if (name == NULL) {
+ gctl_error(req, "No '%s' argument.", "verb");
+ return;
+ }
+ if (strcmp(name, "init") == 0 || strcmp(name, "label") == 0)
+ eli_init(req);
+ else if (strcmp(name, "attach") == 0)
+ eli_attach(req);
+ else if (strcmp(name, "setkey") == 0)
+ eli_setkey(req);
+ else if (strcmp(name, "delkey") == 0)
+ eli_delkey(req);
+ else if (strcmp(name, "kill") == 0)
+ eli_kill(req);
+ else if (strcmp(name, "backup") == 0)
+ eli_backup(req);
+ else if (strcmp(name, "restore") == 0)
+ eli_restore(req);
+ else if (strcmp(name, "dump") == 0)
+ eli_dump(req);
+ else if (strcmp(name, "clear") == 0)
+ eli_clear(req);
+ else
+ gctl_error(req, "Unknown command: %s.", name);
+}
+
+static void
+arc4rand(unsigned char *buf, size_t size)
+{
+ uint32_t *buf4;
+ size_t size4;
+ unsigned i;
+
+ buf4 = (uint32_t *)buf;
+ size4 = size / 4;
+
+ for (i = 0; i < size4; i++)
+ buf4[i] = arc4random();
+ for (i *= 4; i < size; i++)
+ buf[i] = arc4random() % 0xff;
+}
+
+static int
+eli_is_attached(const char *prov)
+{
+ char name[MAXPATHLEN];
+ unsigned secsize;
+
+ /*
+ * Not the best way to do it, but the easiest.
+ * We try to open provider and check if it is a GEOM provider
+ * by asking about its sectorsize.
+ */
+ snprintf(name, sizeof(name), "%s%s", prov, G_ELI_SUFFIX);
+ secsize = g_get_sectorsize(name);
+ if (secsize > 0)
+ return (1);
+ return (0);
+}
+
+static unsigned char *
+eli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key,
+ int new)
+{
+ struct hmac_ctx ctx;
+ const char *str;
+ int error, nopassphrase;
+
+ nopassphrase =
+ gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase");
+
+ g_eli_crypto_hmac_init(&ctx, NULL, 0);
+
+ str = gctl_get_ascii(req, new ? "newkeyfile" : "keyfile");
+ if (str[0] != '\0') {
+ char buf[MAXPHYS];
+ ssize_t done;
+ int fd;
+
+ if (strcmp(str, "-") == 0)
+ fd = STDIN_FILENO;
+ else {
+ fd = open(str, O_RDONLY);
+ if (fd == -1) {
+ gctl_error(req, "Cannot open keyfile %s: %s.",
+ str, strerror(errno));
+ return (NULL);
+ }
+ }
+ while ((done = read(fd, buf, sizeof(buf))) > 0)
+ g_eli_crypto_hmac_update(&ctx, buf, done);
+ error = errno;
+ if (strcmp(str, "-") != 0)
+ close(fd);
+ bzero(buf, sizeof(buf));
+ if (done == -1) {
+ gctl_error(req, "Cannot read keyfile %s: %s.", str,
+ strerror(error));
+ return (NULL);
+ }
+ }
+
+ if (!nopassphrase) {
+ char buf1[BUFSIZ], buf2[BUFSIZ], *p;
+
+ if (!new && md->md_iterations == -1) {
+ gctl_error(req, "Missing -p flag.");
+ return (NULL);
+ }
+ for (;;) {
+ p = readpassphrase(
+ new ? "Enter new passphrase:" : "Enter passphrase:",
+ buf1, sizeof(buf1), RPP_ECHO_OFF | RPP_REQUIRE_TTY);
+ if (p == NULL) {
+ bzero(buf1, sizeof(buf1));
+ gctl_error(req, "Cannot read passphrase: %s.",
+ strerror(errno));
+ return (NULL);
+ }
+
+ if (new) {
+ p = readpassphrase("Reenter new passphrase: ",
+ buf2, sizeof(buf2),
+ RPP_ECHO_OFF | RPP_REQUIRE_TTY);
+ if (p == NULL) {
+ bzero(buf1, sizeof(buf1));
+ gctl_error(req,
+ "Cannot read passphrase: %s.",
+ strerror(errno));
+ return (NULL);
+ }
+
+ if (strcmp(buf1, buf2) != 0) {
+ bzero(buf2, sizeof(buf2));
+ fprintf(stderr, "They didn't match.\n");
+ continue;
+ }
+ bzero(buf2, sizeof(buf2));
+ }
+ break;
+ }
+ /*
+ * Field md_iterations equal to -1 means "choose some sane
+ * value for me".
+ */
+ if (md->md_iterations == -1) {
+ assert(new);
+ if (verbose)
+ printf("Calculating number of iterations...\n");
+ md->md_iterations = pkcs5v2_calculate(2000000);
+ assert(md->md_iterations > 0);
+ if (verbose) {
+ printf("Done, using %d iterations.\n",
+ md->md_iterations);
+ }
+ }
+ /*
+ * If md_iterations is equal to 0, user don't want PKCS5v2.
+ */
+ if (md->md_iterations == 0) {
+ g_eli_crypto_hmac_update(&ctx, md->md_salt,
+ sizeof(md->md_salt));
+ g_eli_crypto_hmac_update(&ctx, buf1, strlen(buf1));
+ } else /* if (md->md_iterations > 0) */ {
+ unsigned char dkey[G_ELI_USERKEYLEN];
+
+ pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt,
+ sizeof(md->md_salt), buf1, md->md_iterations);
+ g_eli_crypto_hmac_update(&ctx, dkey, sizeof(dkey));
+ bzero(dkey, sizeof(dkey));
+ }
+ bzero(buf1, sizeof(buf1));
+ }
+ g_eli_crypto_hmac_final(&ctx, key, 0);
+ return (key);
+}
+
+static int
+eli_metadata_read(struct gctl_req *req, const char *prov,
+ struct g_eli_metadata *md)
+{
+ unsigned char sector[sizeof(struct g_eli_metadata)];
+ int error;
+
+ if (g_get_sectorsize(prov) == 0) {
+ int fd;
+
+ /* This is a file probably. */
+ fd = open(prov, O_RDONLY);
+ if (fd == -1) {
+ gctl_error(req, "Cannot open %s: %s.", prov,
+ strerror(errno));
+ return (-1);
+ }
+ if (read(fd, sector, sizeof(sector)) != sizeof(sector)) {
+ gctl_error(req, "Cannot read metadata from %s: %s.",
+ prov, strerror(errno));
+ close(fd);
+ return (-1);
+ }
+ close(fd);
+ } else {
+ /* This is a GEOM provider. */
+ error = g_metadata_read(prov, sector, sizeof(sector),
+ G_ELI_MAGIC);
+ if (error != 0) {
+ gctl_error(req, "Cannot read metadata from %s: %s.",
+ prov, strerror(error));
+ return (-1);
+ }
+ }
+ if (eli_metadata_decode(sector, md) != 0) {
+ gctl_error(req, "MD5 hash mismatch for %s.", prov);
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+eli_metadata_store(struct gctl_req *req, const char *prov,
+ struct g_eli_metadata *md)
+{
+ unsigned char sector[sizeof(struct g_eli_metadata)];
+ int error;
+
+ eli_metadata_encode(md, sector);
+ if (g_get_sectorsize(prov) == 0) {
+ int fd;
+
+ /* This is a file probably. */
+ fd = open(prov, O_WRONLY | O_TRUNC);
+ if (fd == -1) {
+ gctl_error(req, "Cannot open %s: %s.", prov,
+ strerror(errno));
+ bzero(sector, sizeof(sector));
+ return (-1);
+ }
+ if (write(fd, sector, sizeof(sector)) != sizeof(sector)) {
+ gctl_error(req, "Cannot write metadata to %s: %s.",
+ prov, strerror(errno));
+ bzero(sector, sizeof(sector));
+ close(fd);
+ return (-1);
+ }
+ close(fd);
+ } else {
+ /* This is a GEOM provider. */
+ error = g_metadata_store(prov, sector, sizeof(sector));
+ if (error != 0) {
+ gctl_error(req, "Cannot write metadata to %s: %s.",
+ prov, strerror(errno));
+ bzero(sector, sizeof(sector));
+ return (-1);
+ }
+ }
+ bzero(sector, sizeof(sector));
+ return (0);
+}
+
+static void
+eli_init(struct gctl_req *req)
+{
+ struct g_eli_metadata md;
+ unsigned char sector[sizeof(struct g_eli_metadata)];
+ unsigned char key[G_ELI_USERKEYLEN];
+ const char *str, *prov;
+ unsigned secsize;
+ off_t mediasize;
+ intmax_t val;
+ int error, nargs, boot;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs != 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+ prov = gctl_get_ascii(req, "arg0");
+ mediasize = g_get_mediasize(prov);
+ secsize = g_get_sectorsize(prov);
+ if (mediasize == 0 || secsize == 0) {
+ gctl_error(req, "Cannot get informations about %s: %s.", prov,
+ strerror(errno));
+ return;
+ }
+
+ bzero(&md, sizeof(md));
+ strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic));
+ md.md_version = G_ELI_VERSION;
+ md.md_flags = 0;
+ boot = gctl_get_int(req, "boot");
+ if (boot) {
+ int nonewpassphrase;
+
+ /* Part of key cannot be read on boot from a file. */
+ str = gctl_get_ascii(req, "newkeyfile");
+ if (str[0] != '\0') {
+ gctl_error(req,
+ "Options -b and -K are mutually exclusive.");
+ return;
+ }
+ /* Key has to be given as a passphrase on boot. */
+ nonewpassphrase = gctl_get_int(req, "nonewpassphrase");
+ if (nonewpassphrase) {
+ gctl_error(req,
+ "Options -b and -P are mutually exclusive.");
+ return;
+ }
+ md.md_flags |= G_ELI_FLAG_BOOT;
+ }
+ str = gctl_get_ascii(req, "algo");
+ md.md_algo = g_eli_str2algo(str);
+ if (md.md_algo < CRYPTO_ALGORITHM_MIN ||
+ md.md_algo > CRYPTO_ALGORITHM_MAX) {
+ gctl_error(req, "Invalid encryption algorithm.");
+ return;
+ }
+ val = gctl_get_intmax(req, "keylen");
+ md.md_keylen = val;
+ md.md_keylen = g_eli_keylen(md.md_algo, md.md_keylen);
+ if (md.md_keylen == 0) {
+ gctl_error(req, "Invalid key length.");
+ return;
+ }
+ md.md_provsize = mediasize;
+
+ val = gctl_get_intmax(req, "iterations");
+ md.md_iterations = val;
+
+ val = gctl_get_intmax(req, "sectorsize");
+ if (val == 0)
+ md.md_sectorsize = secsize;
+ else {
+ if (val < 0 || (val % secsize) != 0) {
+ gctl_error(req, "Invalid sector size.");
+ return;
+ }
+ md.md_sectorsize = val;
+ }
+
+ md.md_keys = 0x01;
+ arc4rand(md.md_salt, sizeof(md.md_salt));
+ arc4rand(md.md_mkeys, sizeof(md.md_mkeys));
+
+ /* Generate user key. */
+ if (eli_genkey(req, &md, key, 1) == NULL) {
+ bzero(key, sizeof(key));
+ bzero(&md, sizeof(md));
+ return;
+ }
+
+ /* Encrypt the first and the only Master Key. */
+ error = g_eli_mkey_encrypt(md.md_algo, key, md.md_keylen, md.md_mkeys);
+ bzero(key, sizeof(key));
+ if (error != 0) {
+ bzero(&md, sizeof(md));
+ gctl_error(req, "Cannot encrypt Master Key: %s.",
+ strerror(error));
+ return;
+ }
+
+ eli_metadata_encode(&md, sector);
+ bzero(&md, sizeof(md));
+ error = g_metadata_store(prov, sector, sizeof(sector));
+ bzero(sector, sizeof(sector));
+ if (error != 0) {
+ gctl_error(req, "Cannot store metadata on %s: %s.", prov,
+ strerror(error));
+ return;
+ }
+ if (verbose)
+ printf("Metadata value stored on %s.\n", prov);
+}
+
+static void
+eli_attach(struct gctl_req *req)
+{
+ struct g_eli_metadata md;
+ unsigned char key[G_ELI_USERKEYLEN];
+ const char *prov;
+ int nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs != 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+ prov = gctl_get_ascii(req, "arg0");
+
+ if (eli_metadata_read(req, prov, &md) == -1)
+ return;
+
+ if (eli_genkey(req, &md, key, 0) == NULL) {
+ bzero(key, sizeof(key));
+ return;
+ }
+
+ gctl_ro_param(req, "key", sizeof(key), key);
+ if (gctl_issue(req) == NULL) {
+ if (verbose)
+ printf("Attched to %s.\n", prov);
+ }
+ bzero(key, sizeof(key));
+}
+
+static void
+eli_setkey_attached(struct gctl_req *req, const char *prov,
+ struct g_eli_metadata *md)
+{
+ unsigned char key[G_ELI_USERKEYLEN];
+ intmax_t val;
+
+ val = gctl_get_intmax(req, "iterations");
+ /* Check if iterations number should be changed. */
+ if (val != -1)
+ md->md_iterations = val;
+
+ /* Generate key for Master Key encryption. */
+ if (eli_genkey(req, md, key, 1) == NULL) {
+ bzero(key, sizeof(key));
+ return;
+ }
+
+ gctl_ro_param(req, "key", sizeof(key), key);
+ gctl_issue(req);
+ bzero(key, sizeof(key));
+}
+
+static void
+eli_setkey_detached(struct gctl_req *req, const char *prov,
+ struct g_eli_metadata *md)
+{
+ unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN];
+ unsigned char *mkeydst;
+ intmax_t val;
+ unsigned nkey;
+ int error;
+
+ if (md->md_keys == 0) {
+ gctl_error(req, "No valid keys on %s.", prov);
+ return;
+ }
+
+ /* Generate key for Master Key decryption. */
+ if (eli_genkey(req, md, key, 0) == NULL) {
+ bzero(key, sizeof(key));
+ return;
+ }
+
+ /* Decrypt Master Key. */
+ error = g_eli_mkey_decrypt(md, key, mkey, &nkey);
+ bzero(key, sizeof(key));
+ if (error != 0) {
+ bzero(md, sizeof(*md));
+ if (error == -1)
+ gctl_error(req, "Wrong key for %s.", prov);
+ else /* if (error > 0) */ {
+ gctl_error(req, "Cannot decrypt Master Key: %s.",
+ strerror(error));
+ }
+ return;
+ }
+ if (verbose)
+ printf("Decrypted Master Key %u.\n", nkey);
+
+ val = gctl_get_intmax(req, "keyno");
+ if (val != -1)
+ nkey = val;
+#if 0
+ else
+ ; /* Use the key number which was found during decryption. */
+#endif
+ if (nkey >= G_ELI_MAXMKEYS) {
+ gctl_error(req, "Invalid '%s' argument.", "keyno");
+ return;
+ }
+
+ val = gctl_get_intmax(req, "iterations");
+ /* Check if iterations number should and can be changed. */
+ if (val != -1) {
+ if (bitcount32(md->md_keys) != 1) {
+ gctl_error(req, "To be able to use '-i' option, only "
+ "one key can be defined.");
+ return;
+ }
+ if (md->md_keys != (1 << nkey)) {
+ gctl_error(req, "Only already defined key can be "
+ "changed when '-i' option is used.");
+ return;
+ }
+ md->md_iterations = val;
+ }
+
+ mkeydst = md->md_mkeys + nkey * G_ELI_MKEYLEN;
+ md->md_keys |= (1 << nkey);
+
+ bcopy(mkey, mkeydst, sizeof(mkey));
+ bzero(mkey, sizeof(mkey));
+
+ /* Generate key for Master Key encryption. */
+ if (eli_genkey(req, md, key, 1) == NULL) {
+ bzero(key, sizeof(key));
+ bzero(md, sizeof(*md));
+ return;
+ }
+
+ /* Encrypt the Master-Key with the new key. */
+ error = g_eli_mkey_encrypt(md->md_algo, key, md->md_keylen, mkeydst);
+ bzero(key, sizeof(key));
+ if (error != 0) {
+ bzero(md, sizeof(*md));
+ gctl_error(req, "Cannot encrypt Master Key: %s.",
+ strerror(error));
+ return;
+ }
+
+ /* Store metadata with fresh key. */
+ eli_metadata_store(req, prov, md);
+ bzero(md, sizeof(*md));
+}
+
+static void
+eli_setkey(struct gctl_req *req)
+{
+ struct g_eli_metadata md;
+ const char *prov;
+ int nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs != 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+ prov = gctl_get_ascii(req, "arg0");
+
+ if (eli_metadata_read(req, prov, &md) == -1)
+ return;
+
+ if (eli_is_attached(prov))
+ eli_setkey_attached(req, prov, &md);
+ else
+ eli_setkey_detached(req, prov, &md);
+}
+
+static void
+eli_delkey_attached(struct gctl_req *req, const char *prov __unused)
+{
+
+ gctl_issue(req);
+}
+
+static void
+eli_delkey_detached(struct gctl_req *req, const char *prov)
+{
+ struct g_eli_metadata md;
+ unsigned char *mkeydst;
+ intmax_t val;
+ unsigned nkey;
+ int all, force;
+
+ if (eli_metadata_read(req, prov, &md) == -1)
+ return;
+
+ all = gctl_get_int(req, "all");
+ if (all)
+ arc4rand(md.md_mkeys, sizeof(md.md_mkeys));
+ else {
+ force = gctl_get_int(req, "force");
+ val = gctl_get_intmax(req, "keyno");
+ if (val == -1) {
+ gctl_error(req, "Key number has to be specified.");
+ return;
+ }
+ nkey = val;
+ if (nkey >= G_ELI_MAXMKEYS) {
+ gctl_error(req, "Invalid '%s' argument.", "keyno");
+ return;
+ }
+ if (!(md.md_keys & (1 << nkey)) && !force) {
+ gctl_error(req, "Master Key %u is not set.", nkey);
+ return;
+ }
+ md.md_keys &= ~(1 << nkey);
+ if (md.md_keys == 0 && !force) {
+ gctl_error(req, "This is the last Master Key. Use '-f' "
+ "option if you really want to remove it.");
+ return;
+ }
+ mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN;
+ arc4rand(mkeydst, G_ELI_MKEYLEN);
+ }
+
+ eli_metadata_store(req, prov, &md);
+ bzero(&md, sizeof(md));
+}
+
+static void
+eli_delkey(struct gctl_req *req)
+{
+ const char *prov;
+ int nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs != 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+ prov = gctl_get_ascii(req, "arg0");
+
+ if (eli_is_attached(prov))
+ eli_delkey_attached(req, prov);
+ else
+ eli_delkey_detached(req, prov);
+}
+
+static void
+eli_kill_detached(struct gctl_req *req, const char *prov)
+{
+ struct g_eli_metadata md;
+ int error;
+
+ /*
+ * NOTE: Maybe we should verify if this is geli provider first,
+ * but 'kill' command is quite critical so better don't waste
+ * the time.
+ */
+#if 0
+ error = g_metadata_read(prov, (unsigned char *)&md, sizeof(md),
+ G_ELI_MAGIC);
+ if (error != 0) {
+ gctl_error(req, "Cannot read metadata from %s: %s.", prov,
+ strerror(error));
+ return;
+ }
+#endif
+
+ arc4rand((unsigned char *)&md, sizeof(md));
+ error = g_metadata_store(prov, (unsigned char *)&md, sizeof(md));
+ if (error != 0) {
+ gctl_error(req, "Cannot write metadata to %s: %s.", prov,
+ strerror(error));
+ }
+}
+
+static void
+eli_kill(struct gctl_req *req)
+{
+ const char *prov;
+ int i, nargs, all;
+
+ nargs = gctl_get_int(req, "nargs");
+ all = gctl_get_int(req, "all");
+ if (!all && nargs == 0) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+ /*
+ * How '-a' option combine with a list of providers:
+ * Delete Master Keys from all attached providers:
+ * geli kill -a
+ * Delete Master Keys from all attached provider and from
+ * detached da0 and da1:
+ * geli kill -a da0 da1
+ * Delete Master Keys from (attached or detached) da0 and da1:
+ * geli kill da0 da1
+ */
+
+ /*
+ * First attached providers.
+ */
+ gctl_issue(req);
+ /*
+ * Now the rest.
+ */
+ for (i = 0; i < nargs; i++) {
+ prov = gctl_get_ascii(req, "arg%d", i);
+ if (!eli_is_attached(prov))
+ eli_kill_detached(req, prov);
+ }
+}
+
+static void
+eli_backup(struct gctl_req *req)
+{
+ struct g_eli_metadata md;
+ const char *file, *prov;
+ unsigned secsize;
+ unsigned char *sector;
+ off_t mediasize;
+ int nargs, filefd, provfd;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs != 2) {
+ gctl_error(req, "Invalid number of arguments.");
+ return;
+ }
+ prov = gctl_get_ascii(req, "arg0");
+ file = gctl_get_ascii(req, "arg1");
+
+ provfd = filefd = -1;
+ sector = NULL;
+ secsize = 0;
+
+ provfd = open(prov, O_RDONLY);
+ if (provfd == -1 && errno == ENOENT && prov[0] != '/') {
+ char devprov[MAXPATHLEN];
+
+ snprintf(devprov, sizeof(devprov), "%s%s", _PATH_DEV, prov);
+ provfd = open(devprov, O_RDONLY);
+ }
+ if (provfd == -1) {
+ gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
+ return;
+ }
+ filefd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0600);
+ if (filefd == -1) {
+ gctl_error(req, "Cannot open %s: %s.", file, strerror(errno));
+ goto out;
+ }
+
+ mediasize = g_get_mediasize(prov);
+ secsize = g_get_sectorsize(prov);
+ if (mediasize == 0 || secsize == 0) {
+ gctl_error(req, "Cannot get informations about %s: %s.", prov,
+ strerror(errno));
+ return;
+ }
+
+ sector = malloc(secsize);
+ if (sector == NULL) {
+ gctl_error(req, "Cannot allocate memory.");
+ return;
+ }
+
+ /* Read metadata from the provider. */
+ if (pread(provfd, sector, secsize, mediasize - secsize) !=
+ (ssize_t)secsize) {
+ gctl_error(req, "Cannot read metadata: %s.", strerror(errno));
+ goto out;
+ }
+ /* Check if this is geli provider. */
+ if (eli_metadata_decode(sector, &md) != 0) {
+ gctl_error(req, "MD5 hash mismatch: not a geli provider?");
+ goto out;
+ }
+ /* Write metadata to the destination file. */
+ if (write(filefd, sector, secsize) != (ssize_t)secsize) {
+ gctl_error(req, "Cannot write to %s: %s.", file,
+ strerror(errno));
+ goto out;
+ }
+out:
+ if (provfd > 0)
+ close(provfd);
+ if (filefd > 0)
+ close(filefd);
+ if (sector != NULL) {
+ bzero(sector, secsize);
+ free(sector);
+ }
+}
+
+static void
+eli_restore(struct gctl_req *req)
+{
+ struct g_eli_metadata md;
+ const char *file, *prov;
+ unsigned char *sector;
+ unsigned secsize;
+ off_t mediasize;
+ int nargs, filefd, provfd;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs != 2) {
+ gctl_error(req, "Invalid number of arguments.");
+ return;
+ }
+ file = gctl_get_ascii(req, "arg0");
+ prov = gctl_get_ascii(req, "arg1");
+
+ provfd = filefd = -1;
+ sector = NULL;
+ secsize = 0;
+
+ filefd = open(file, O_RDONLY);
+ if (filefd == -1) {
+ gctl_error(req, "Cannot open %s: %s.", file, strerror(errno));
+ goto out;
+ }
+ provfd = open(prov, O_WRONLY);
+ if (provfd == -1 && errno == ENOENT && prov[0] != '/') {
+ char devprov[MAXPATHLEN];
+
+ snprintf(devprov, sizeof(devprov), "%s%s", _PATH_DEV, prov);
+ provfd = open(devprov, O_WRONLY);
+ }
+ if (provfd == -1) {
+ gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
+ return;
+ }
+
+ mediasize = g_get_mediasize(prov);
+ secsize = g_get_sectorsize(prov);
+ if (mediasize == 0 || secsize == 0) {
+ gctl_error(req, "Cannot get informations about %s: %s.", prov,
+ strerror(errno));
+ return;
+ }
+
+ sector = malloc(secsize);
+ if (sector == NULL) {
+ gctl_error(req, "Cannot allocate memory.");
+ return;
+ }
+
+ /* Read metadata from the backup file. */
+ if (read(filefd, sector, secsize) != (ssize_t)secsize) {
+ gctl_error(req, "Cannot read from %s: %s.", file,
+ strerror(errno));
+ goto out;
+ }
+ /* Check if this file contains geli metadata. */
+ if (eli_metadata_decode(sector, &md) != 0) {
+ gctl_error(req, "MD5 hash mismatch: not a geli backup file?");
+ goto out;
+ }
+ /* Read metadata from the provider. */
+ if (pwrite(provfd, sector, secsize, mediasize - secsize) !=
+ (ssize_t)secsize) {
+ gctl_error(req, "Cannot write metadata: %s.", strerror(errno));
+ goto out;
+ }
+out:
+ if (provfd > 0)
+ close(provfd);
+ if (filefd > 0)
+ close(filefd);
+ if (sector != NULL) {
+ bzero(sector, secsize);
+ free(sector);
+ }
+}
+
+static void
+eli_clear(struct gctl_req *req)
+{
+ const char *name;
+ int error, i, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+
+ for (i = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_clear(name, G_ELI_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Cannot clear metadata on %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (verbose)
+ printf("Metadata cleared on %s.\n", name);
+ }
+}
+
+static void
+eli_dump(struct gctl_req *req)
+{
+ struct g_eli_metadata md, tmpmd;
+ const char *name;
+ int error, i, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+
+ for (i = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_read(name, (unsigned char *)&tmpmd,
+ sizeof(tmpmd), G_ELI_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Cannot read metadata from %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (eli_metadata_decode((unsigned char *)&tmpmd, &md) != 0) {
+ fprintf(stderr, "MD5 hash mismatch for %s, skipping.\n",
+ name);
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ printf("Metadata on %s:\n", name);
+ eli_metadata_dump(&md);
+ printf("\n");
+ }
+}
diff --git a/sbin/geom/class/label/Makefile b/sbin/geom/class/label/Makefile
new file mode 100644
index 0000000..cba7ac6
--- /dev/null
+++ b/sbin/geom/class/label/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../misc
+
+CLASS= label
+
+.include <bsd.lib.mk>
diff --git a/sbin/geom/class/label/geom_label.c b/sbin/geom/class/label/geom_label.c
new file mode 100644
index 0000000..24b452e
--- /dev/null
+++ b/sbin/geom/class/label/geom_label.c
@@ -0,0 +1,220 @@
+/*-
+ * Copyright (c) 2004-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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <assert.h>
+#include <libgeom.h>
+#include <geom/label/g_label.h>
+
+#include "core/geom.h"
+#include "misc/subr.h"
+
+
+uint32_t lib_version = G_LIB_VERSION;
+uint32_t version = G_LABEL_VERSION;
+
+static void label_main(struct gctl_req *req, unsigned flags);
+static void label_clear(struct gctl_req *req);
+static void label_dump(struct gctl_req *req);
+static void label_label(struct gctl_req *req);
+
+struct g_command class_commands[] = {
+ { "clear", G_FLAG_VERBOSE, label_main, G_NULL_OPTS,
+ "[-v] dev ..."
+ },
+ { "create", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, G_NULL_OPTS,
+ "[-v] name dev"
+ },
+ { "destroy", G_FLAG_VERBOSE, NULL,
+ {
+ { 'f', "force", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-fv] name ..."
+ },
+ { "dump", 0, label_main, G_NULL_OPTS,
+ "dev ..."
+ },
+ { "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, label_main, G_NULL_OPTS,
+ "[-v] name dev"
+ },
+ { "stop", G_FLAG_VERBOSE, NULL,
+ {
+ { 'f', "force", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-fv] name ..."
+ },
+ G_CMD_SENTINEL
+};
+
+static int verbose = 0;
+
+static void
+label_main(struct gctl_req *req, unsigned flags)
+{
+ const char *name;
+
+ if ((flags & G_FLAG_VERBOSE) != 0)
+ verbose = 1;
+
+ name = gctl_get_ascii(req, "verb");
+ if (name == NULL) {
+ gctl_error(req, "No '%s' argument.", "verb");
+ return;
+ }
+ if (strcmp(name, "label") == 0)
+ label_label(req);
+ else if (strcmp(name, "clear") == 0)
+ label_clear(req);
+ else if (strcmp(name, "dump") == 0)
+ label_dump(req);
+ else
+ gctl_error(req, "Unknown command: %s.", name);
+}
+
+static void
+label_label(struct gctl_req *req)
+{
+ struct g_label_metadata md;
+ const char *name, *label;
+ u_char sector[512];
+ int error, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs != 2) {
+ gctl_error(req, "Invalid number of arguments.");
+ return;
+ }
+
+ /*
+ * Clear last sector first to spoil all components if device exists.
+ */
+ name = gctl_get_ascii(req, "arg1");
+ error = g_metadata_clear(name, NULL);
+ if (error != 0) {
+ gctl_error(req, "Can't store metadata on %s: %s.", name,
+ strerror(error));
+ return;
+ }
+
+ strlcpy(md.md_magic, G_LABEL_MAGIC, sizeof(md.md_magic));
+ md.md_version = G_LABEL_VERSION;
+ label = gctl_get_ascii(req, "arg0");
+ strlcpy(md.md_label, label, sizeof(md.md_label));
+ md.md_provsize = g_get_mediasize(name);
+ if (md.md_provsize == 0) {
+ gctl_error(req, "Can't get mediasize of %s: %s.", name,
+ strerror(errno));
+ return;
+ }
+
+ /*
+ * Ok, store metadata.
+ */
+ label_metadata_encode(&md, sector);
+ error = g_metadata_store(name, sector, sizeof(sector));
+ if (error != 0) {
+ fprintf(stderr, "Can't store metadata on %s: %s.\n", name,
+ strerror(error));
+ gctl_error(req, "Not done.");
+ }
+ if (verbose)
+ printf("Metadata value stored on %s.\n", name);
+}
+
+static void
+label_clear(struct gctl_req *req)
+{
+ const char *name;
+ int error, i, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+
+ for (i = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_clear(name, G_LABEL_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Can't clear metadata on %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (verbose)
+ printf("Metadata cleared on %s.\n", name);
+ }
+}
+
+static void
+label_metadata_dump(const struct g_label_metadata *md)
+{
+
+ printf(" Magic string: %s\n", md->md_magic);
+ printf("Metadata version: %u\n", (u_int)md->md_version);
+ printf(" Label: %s\n", md->md_label);
+}
+
+static void
+label_dump(struct gctl_req *req)
+{
+ struct g_label_metadata md, tmpmd;
+ const char *name;
+ int error, i, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+
+ for (i = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_read(name, (u_char *)&tmpmd, sizeof(tmpmd),
+ G_LABEL_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Can't read metadata from %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ label_metadata_decode((u_char *)&tmpmd, &md);
+ printf("Metadata on %s:\n", name);
+ label_metadata_dump(&md);
+ printf("\n");
+ }
+}
diff --git a/sbin/geom/class/label/glabel.8 b/sbin/geom/class/label/glabel.8
new file mode 100644
index 0000000..cbff2b4
--- /dev/null
+++ b/sbin/geom/class/label/glabel.8
@@ -0,0 +1,227 @@
+.\" Copyright (c) 2004-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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 2, 2004
+.Dt GLABEL 8
+.Os
+.Sh NAME
+.Nm glabel
+.Nd "disk labelization control utility"
+.Sh SYNOPSIS
+.Nm
+.Cm create
+.Op Fl v
+.Ar name
+.Ar dev
+.Nm
+.Cm destroy
+.Op Fl fv
+.Ar name ...
+.Nm
+.Cm label
+.Op Fl v
+.Ar name
+.Ar dev
+.Nm
+.Cm stop
+.Op Fl fv
+.Ar name ...
+.Nm
+.Cm clear
+.Op Fl v
+.Ar dev ...
+.Nm
+.Cm dump
+.Ar dev ...
+.Nm
+.Cm list
+.Nm
+.Cm status
+.Nm
+.Cm load
+.Nm
+.Cm unload
+.Sh DESCRIPTION
+The
+.Nm
+utility is used for GEOM provider labelization.
+A label can be set up on a GEOM provider in two ways:
+.Dq manual
+or
+.Dq automatic .
+When using the
+.Dq manual
+method, no metadata are stored on the devices, so a label has to be configured
+by hand every time it is needed.
+The
+.Dq automatic
+method uses on-disk metadata to store the label and detect it automatically in
+the future.
+.Pp
+This class also provides volume label detection for file systems.
+Those labels cannot be set with
+.Nm ,
+but must be set with the appropriate file system utility, e.g.\& for UFS
+the file system label is set with
+.Xr tunefs 8 .
+Currently supported file systems are:
+.Pp
+.Bl -bullet -offset indent -compact
+.It
+UFS1 (directory
+.Pa /dev/ufs/ ) .
+.It
+UFS2 (directory
+.Pa /dev/ufs/ ) .
+.It
+MSDOSFS (FAT12, FAT16, FAT32) (directory
+.Pa /dev/msdosfs/ ) .
+.It
+CD ISO9660 (directory
+.Pa /dev/iso9660/ ) .
+.It
+EXT2FS (directory
+.Pa /dev/ext2fs/ ) .
+.It
+REISERFS (directory
+.Pa /dev/reiserfs/ ) .
+.It
+NTFS (directory
+.Pa /dev/ntfs/ ) .
+.El
+.Pp
+Non file-system labels are created in the directory
+.Pa /dev/label/ .
+.Pp
+The first argument to
+.Nm
+indicates an action to be performed:
+.Bl -tag -width ".Cm destroy"
+.It Cm create
+Create temporary label
+.Ar name
+for the given provider.
+This is the
+.Dq manual
+method.
+The kernel module
+.Pa geom_label.ko
+will be loaded if it is not loaded already.
+.It Cm label
+Set up a label
+.Ar name
+for the given provider.
+This is the
+.Dq automatic
+method, where metadata is stored in a provider's last sector.
+The kernel module
+.Pa geom_label.ko
+will be loaded if it is not loaded already.
+.It Cm stop
+Turn off the given label by its
+.Ar name .
+This command does not touch on-disk metadata!
+.It Cm destroy
+Same as
+.Cm stop .
+.It Cm clear
+Clear metadata on the given devices.
+.It Cm dump
+Dump metadata stored on the given devices.
+.It Cm list
+See
+.Xr geom 8 .
+.It Cm status
+See
+.Xr geom 8 .
+.It Cm load
+See
+.Xr geom 8 .
+.It Cm unload
+See
+.Xr geom 8 .
+.El
+.Pp
+Additional options:
+.Bl -tag -width indent
+.It Fl f
+Force the removal of the specified labels.
+.It Fl v
+Be more verbose.
+.El
+.Sh SYSCTL VARIABLES
+The following
+.Xr sysctl 8
+variables can be used to control the behavior of the
+.Nm LABEL
+GEOM class.
+The default value is shown next to each variable.
+.Bl -tag -width indent
+.It Va kern.geom.label.debug : No 0
+Debug level of the
+.Nm LABEL
+GEOM class.
+This can be set to a number between 0 and 2 inclusive.
+If set to 0 minimal debug information is printed, and if set to 2 the
+maximum amount of debug information is printed.
+.El
+.Sh EXIT STATUS
+Exit status is 0 on success, and 1 if the command fails.
+.Sh EXAMPLES
+The following example shows how to set up a label for disk
+.Dq Li da2 ,
+create a file system on it, and mount it:
+.Bd -literal -offset indent
+glabel label -v usr /dev/da2
+newfs /dev/label/usr
+mount /dev/label/usr /usr
+[...]
+umount /usr
+glabel stop usr
+glabel unload
+.Ed
+.Pp
+The next example shows how to set up a label for a UFS file system:
+.Bd -literal -offset indent
+tunefs -L data /dev/da4s1a
+mount /dev/ufs/data /mnt/data
+.Ed
+.Sh SEE ALSO
+.Xr geom 4 ,
+.Xr loader.conf 5 ,
+.Xr geom 8 ,
+.Xr mount 8 ,
+.Xr newfs 8 ,
+.Xr sysctl 8 ,
+.Xr tunefs 8 ,
+.Xr umount 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 5.3 .
+.Sh AUTHORS
+.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org
diff --git a/sbin/geom/class/mirror/Makefile b/sbin/geom/class/mirror/Makefile
new file mode 100644
index 0000000..f07a790
--- /dev/null
+++ b/sbin/geom/class/mirror/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../misc
+
+CLASS= mirror
+
+DPADD= ${LIBMD}
+LDADD= -lmd
+
+.include <bsd.lib.mk>
diff --git a/sbin/geom/class/mirror/geom_mirror.c b/sbin/geom/class/mirror/geom_mirror.c
new file mode 100644
index 0000000..989f5e3
--- /dev/null
+++ b/sbin/geom/class/mirror/geom_mirror.c
@@ -0,0 +1,372 @@
+/*-
+ * Copyright (c) 2004-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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <errno.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <assert.h>
+#include <libgeom.h>
+#include <geom/mirror/g_mirror.h>
+#include <core/geom.h>
+#include <misc/subr.h>
+
+
+uint32_t lib_version = G_LIB_VERSION;
+uint32_t version = G_MIRROR_VERSION;
+
+static char label_balance[] = "split", configure_balance[] = "none";
+static intmax_t label_slice = 4096, configure_slice = -1;
+static intmax_t insert_priority = 0;
+
+static void mirror_main(struct gctl_req *req, unsigned flags);
+static void mirror_activate(struct gctl_req *req);
+static void mirror_clear(struct gctl_req *req);
+static void mirror_dump(struct gctl_req *req);
+static void mirror_label(struct gctl_req *req);
+
+struct g_command class_commands[] = {
+ { "activate", G_FLAG_VERBOSE, mirror_main, G_NULL_OPTS,
+ "[-v] name prov ..."
+ },
+ { "clear", G_FLAG_VERBOSE, mirror_main, G_NULL_OPTS,
+ "[-v] prov ..."
+ },
+ { "configure", G_FLAG_VERBOSE, NULL,
+ {
+ { 'a', "autosync", NULL, G_TYPE_NONE },
+ { 'b', "balance", configure_balance, G_TYPE_STRING },
+ { 'd', "dynamic", NULL, G_TYPE_NONE },
+ { 'h', "hardcode", NULL, G_TYPE_NONE },
+ { 'n', "noautosync", NULL, G_TYPE_NONE },
+ { 's', "slice", &configure_slice, G_TYPE_NUMBER },
+ G_OPT_SENTINEL
+ },
+ "[-adhnv] [-b balance] [-s slice] name"
+ },
+ { "deactivate", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
+ "[-v] name prov ..."
+ },
+ { "dump", 0, mirror_main, G_NULL_OPTS,
+ "prov ..."
+ },
+ { "forget", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
+ "name ..."
+ },
+ { "label", G_FLAG_VERBOSE, mirror_main,
+ {
+ { 'b', "balance", label_balance, G_TYPE_STRING },
+ { 'h', "hardcode", NULL, G_TYPE_NONE },
+ { 'n', "noautosync", NULL, G_TYPE_NONE },
+ { 's', "slice", &label_slice, G_TYPE_NUMBER },
+ G_OPT_SENTINEL
+ },
+ "[-hnv] [-b balance] [-s slice] name prov ..."
+ },
+ { "insert", G_FLAG_VERBOSE, NULL,
+ {
+ { 'h', "hardcode", NULL, G_TYPE_NONE },
+ { 'i', "inactive", NULL, G_TYPE_NONE },
+ { 'p', "priority", &insert_priority, G_TYPE_NUMBER },
+ G_OPT_SENTINEL
+ },
+ "[-hiv] [-p priority] name prov ..."
+ },
+ { "rebuild", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
+ "[-v] name prov ..."
+ },
+ { "remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
+ "[-v] name prov ..."
+ },
+ { "stop", G_FLAG_VERBOSE, NULL,
+ {
+ { 'f', "force", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-fv] name ..."
+ },
+ G_CMD_SENTINEL
+};
+
+static int verbose = 0;
+
+static void
+mirror_main(struct gctl_req *req, unsigned flags)
+{
+ const char *name;
+
+ if ((flags & G_FLAG_VERBOSE) != 0)
+ verbose = 1;
+
+ name = gctl_get_ascii(req, "verb");
+ if (name == NULL) {
+ gctl_error(req, "No '%s' argument.", "verb");
+ return;
+ }
+ if (strcmp(name, "label") == 0)
+ mirror_label(req);
+ else if (strcmp(name, "clear") == 0)
+ mirror_clear(req);
+ else if (strcmp(name, "dump") == 0)
+ mirror_dump(req);
+ else if (strcmp(name, "activate") == 0)
+ mirror_activate(req);
+ else
+ gctl_error(req, "Unknown command: %s.", name);
+}
+
+static void
+mirror_label(struct gctl_req *req)
+{
+ struct g_mirror_metadata md;
+ u_char sector[512];
+ const char *str;
+ unsigned sectorsize;
+ off_t mediasize;
+ intmax_t val;
+ int error, i, nargs, bal, hardcode, noautosync;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 2) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+
+ strlcpy(md.md_magic, G_MIRROR_MAGIC, sizeof(md.md_magic));
+ md.md_version = G_MIRROR_VERSION;
+ str = gctl_get_ascii(req, "arg0");
+ strlcpy(md.md_name, str, sizeof(md.md_name));
+ md.md_mid = arc4random();
+ md.md_all = nargs - 1;
+ md.md_mflags = 0;
+ md.md_dflags = 0;
+ md.md_genid = 0;
+ md.md_syncid = 1;
+ md.md_sync_offset = 0;
+ val = gctl_get_intmax(req, "slice");
+ md.md_slice = val;
+ str = gctl_get_ascii(req, "balance");
+ bal = balance_id(str);
+ if (bal == -1) {
+ gctl_error(req, "Invalid balance algorithm.");
+ return;
+ }
+ md.md_balance = bal;
+ noautosync = gctl_get_int(req, "noautosync");
+ if (noautosync)
+ md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
+ hardcode = gctl_get_int(req, "hardcode");
+
+ /*
+ * Calculate sectorsize by finding least common multiple from
+ * sectorsizes of every disk and find the smallest mediasize.
+ */
+ mediasize = 0;
+ sectorsize = 0;
+ for (i = 1; i < nargs; i++) {
+ unsigned ssize;
+ off_t msize;
+
+ str = gctl_get_ascii(req, "arg%d", i);
+ msize = g_get_mediasize(str);
+ ssize = g_get_sectorsize(str);
+ if (msize == 0 || ssize == 0) {
+ gctl_error(req, "Can't get informations about %s: %s.",
+ str, strerror(errno));
+ return;
+ }
+ msize -= ssize;
+ if (mediasize == 0 || (mediasize > 0 && msize < mediasize))
+ mediasize = msize;
+ if (sectorsize == 0)
+ sectorsize = ssize;
+ else
+ sectorsize = g_lcm(sectorsize, ssize);
+ }
+ md.md_mediasize = mediasize;
+ md.md_sectorsize = sectorsize;
+
+ /*
+ * Clear last sector first, to spoil all components if device exists.
+ */
+ for (i = 1; i < nargs; i++) {
+ str = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_clear(str, NULL);
+ if (error != 0) {
+ gctl_error(req, "Can't store metadata on %s: %s.", str,
+ strerror(error));
+ return;
+ }
+ }
+
+ /*
+ * Ok, store metadata (use disk number as priority).
+ */
+ for (i = 1; i < nargs; i++) {
+ str = gctl_get_ascii(req, "arg%d", i);
+ md.md_did = arc4random();
+ md.md_priority = i - 1;
+ md.md_provsize = g_get_mediasize(str);
+ assert(md.md_provsize != 0);
+ if (!hardcode)
+ bzero(md.md_provider, sizeof(md.md_provider));
+ else {
+ if (strncmp(str, _PATH_DEV, strlen(_PATH_DEV)) == 0)
+ str += strlen(_PATH_DEV);
+ strlcpy(md.md_provider, str, sizeof(md.md_provider));
+ }
+ mirror_metadata_encode(&md, sector);
+ error = g_metadata_store(str, sector, sizeof(sector));
+ if (error != 0) {
+ fprintf(stderr, "Can't store metadata on %s: %s.\n",
+ str, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (verbose)
+ printf("Metadata value stored on %s.\n", str);
+ }
+}
+
+static void
+mirror_clear(struct gctl_req *req)
+{
+ const char *name;
+ int error, i, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+
+ for (i = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_clear(name, G_MIRROR_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Can't clear metadata on %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (verbose)
+ printf("Metadata cleared on %s.\n", name);
+ }
+}
+
+static void
+mirror_dump(struct gctl_req *req)
+{
+ struct g_mirror_metadata md, tmpmd;
+ const char *name;
+ int error, i, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+
+ for (i = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_read(name, (u_char *)&tmpmd, sizeof(tmpmd),
+ G_MIRROR_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Can't read metadata from %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (mirror_metadata_decode((u_char *)&tmpmd, &md) != 0) {
+ fprintf(stderr, "MD5 hash mismatch for %s, skipping.\n",
+ name);
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ printf("Metadata on %s:\n", name);
+ mirror_metadata_dump(&md);
+ printf("\n");
+ }
+}
+
+static void
+mirror_activate(struct gctl_req *req)
+{
+ struct g_mirror_metadata md, tmpmd;
+ const char *name, *path;
+ int error, i, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 2) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+ name = gctl_get_ascii(req, "arg0");
+
+ for (i = 1; i < nargs; i++) {
+ path = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_read(path, (u_char *)&tmpmd, sizeof(tmpmd),
+ G_MIRROR_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Cannot read metadata from %s: %s.\n",
+ path, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (mirror_metadata_decode((u_char *)&tmpmd, &md) != 0) {
+ fprintf(stderr,
+ "MD5 hash mismatch for provider %s, skipping.\n",
+ path);
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (strcmp(md.md_name, name) != 0) {
+ fprintf(stderr,
+ "Provider %s is not the mirror %s component.\n",
+ path, name);
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ md.md_dflags &= ~G_MIRROR_DISK_FLAG_INACTIVE;
+ mirror_metadata_encode(&md, (u_char *)&tmpmd);
+ error = g_metadata_store(path, (u_char *)&tmpmd, sizeof(tmpmd));
+ if (error != 0) {
+ fprintf(stderr, "Cannot write metadata from %s: %s.\n",
+ path, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (verbose)
+ printf("Provider %s activated.\n", path);
+ }
+}
diff --git a/sbin/geom/class/mirror/gmirror.8 b/sbin/geom/class/mirror/gmirror.8
new file mode 100644
index 0000000..1c021ef
--- /dev/null
+++ b/sbin/geom/class/mirror/gmirror.8
@@ -0,0 +1,306 @@
+.\" Copyright (c) 2004-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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 9, 2004
+.Dt GMIRROR 8
+.Os
+.Sh NAME
+.Nm gmirror
+.Nd "control utility for mirrored devices"
+.Sh SYNOPSIS
+.Nm
+.Cm label
+.Op Fl hnv
+.Op Fl b Ar balance
+.Op Fl s Ar slice
+.Ar name
+.Ar prov ...
+.Nm
+.Cm clear
+.Op Fl v
+.Ar prov ...
+.Nm
+.Cm configure
+.Op Fl adhnv
+.Op Fl b Ar balance
+.Op Fl s Ar slice
+.Ar name
+.Nm
+.Cm rebuild
+.Op Fl v
+.Ar name
+.Ar prov ...
+.Nm
+.Cm insert
+.Op Fl hiv
+.Op Fl p Ar priority
+.Ar name
+.Ar prov ...
+.Nm
+.Cm remove
+.Op Fl v
+.Ar name
+.Ar prov ...
+.Nm
+.Cm activate
+.Op Fl v
+.Ar name
+.Ar prov ...
+.Nm
+.Cm deactivate
+.Op Fl v
+.Ar name
+.Ar prov ...
+.Nm
+.Cm forget
+.Op Fl v
+.Ar name ...
+.Nm
+.Cm stop
+.Op Fl fv
+.Ar name ...
+.Nm
+.Cm clear
+.Op Fl v
+.Ar prov ...
+.Nm
+.Cm dump
+.Ar prov ...
+.Nm
+.Cm list
+.Nm
+.Cm status
+.Nm
+.Cm load
+.Nm
+.Cm unload
+.Sh DESCRIPTION
+The
+.Nm
+utility is used for mirror (RAID1) configurations.
+After a mirror's creation, all components are detected and configured
+automatically.
+All operations like failure detection, stale component detection, rebuild
+of stale components, etc.\& are also done automatically.
+The
+.Nm
+utility uses on-disk metadata (stored in the provider's last sector) to store all needed
+information.
+Since the last sector is used for this purpose, it is possible to place a root
+file system on a mirror.
+.Pp
+The first argument to
+.Nm
+indicates an action to be performed:
+.Bl -tag -width ".Cm deactivate"
+.It Cm label
+Create a mirror.
+The order of components is important, because a component's priority is based on its position
+(starting from 0).
+The component with the biggest priority is used by the prefer balance algorithm
+and is also used as a master component when resynchronization is needed,
+e.g.\& after a power failure when the device was open for writing.
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl b Ar balance"
+.It Fl b Ar balance
+Specifies balance algorithm to use, one of:
+.Bl -tag -width ".Cm round-robin"
+.It Cm load
+Read from the component with the lowest load.
+.It Cm prefer
+Read from the component with the biggest priority.
+.It Cm round-robin
+Use round-robin algorithm when choosing component to read.
+.It Cm split
+Split read requests, which are bigger than or equal to slice size on N pieces,
+where N is the number of active components.
+This is the default balance algorithm.
+.El
+.It Fl h
+Hardcode providers' names in metadata.
+.It Fl n
+Turn off autosynchronization of stale components.
+.It Fl s Ar slice
+When using the
+.Cm split
+balance algorithm and an I/O READ request is bigger than or equal to this value,
+the I/O request will be split into N pieces, where N is the number of active
+components.
+Defaults to 4096 bytes.
+.El
+.It Cm clear
+Clear metadata on the given providers.
+.It Cm configure
+Configure the given device.
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl b Ar balance"
+.It Fl a
+Turn on autosynchronization of stale components.
+.It Fl b Ar balance
+Specifies balance algorithm to use.
+.It Fl d
+Do not hardcode providers' names in metadata.
+.It Fl h
+Hardcode providers' names in metadata.
+.It Fl n
+Turn off autosynchronization of stale components.
+.It Fl s Ar slice
+Specifies slice size for
+.Cm split
+balance algorithm.
+.El
+.It Cm rebuild
+Rebuild the given mirror components forcibly.
+If autosynchronization was not turned off for the given device, this command
+should be unnecessary.
+.It Cm insert
+Add the given component(s) to the existing mirror.
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl p Ar priority"
+.It Fl h
+Hardcode providers' names in metadata.
+.It Fl i
+Mark component(s) as inactive immediately after insertion.
+.It Fl p Ar priority
+Specifies priority of the given component(s).
+.El
+.It Cm remove
+Remove the given component(s) from the mirror and clear metadata on it.
+.It Cm activate
+Activate the given component(s), which were marked as inactive before.
+.It Cm deactivate
+Mark the given component(s) as inactive, so it will not be automatically
+connected to the mirror.
+.It Cm forget
+Forget about components which are not connected.
+This command is useful when a disk has failed and cannot be reconnected, preventing the
+.Cm remove
+command from being used to remove it.
+.It Cm stop
+Stop the given mirror.
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl f"
+.It Fl f
+Stop the given mirror even if it is opened.
+.El
+.It Cm dump
+Dump metadata stored on the given providers.
+.It Cm list
+See
+.Xr geom 8 .
+.It Cm status
+See
+.Xr geom 8 .
+.It Cm load
+See
+.Xr geom 8 .
+.It Cm unload
+See
+.Xr geom 8 .
+.El
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl v"
+.It Fl v
+Be more verbose.
+.El
+.Sh EXIT STATUS
+Exit status is 0 on success, and 1 if the command fails.
+.Sh EXAMPLES
+Use 3 disks to setup a mirror.
+Choose split balance algorithm, split only
+requests which are bigger than or equal to 2kB.
+Create file system,
+mount it, then unmount it and stop device:
+.Bd -literal -offset indent
+gmirror label -v -b split -s 2048 data da0 da1 da2
+newfs /dev/mirror/data
+mount /dev/mirror/data /mnt
+\&...
+umount /mnt
+gmirror stop data
+gmirror unload
+.Ed
+.Pp
+Create a mirror on disk with valid data (note that the last sector of the disk
+will be overwritten).
+Add another disk to this mirror,
+so it will be synchronized with existing disk:
+.Bd -literal -offset indent
+gmirror label -v -b round-robin data da0
+gmirror insert data da1
+.Ed
+.Pp
+Create a mirror, but do not use automatic synchronization feature.
+Add another disk and rebuild it:
+.Bd -literal -offset indent
+gmirror label -v -n -b load data da0 da1
+gmirror insert data da2
+gmirror rebuild data da2
+.Ed
+.Pp
+One disk failed.
+Replace it with a brand new one:
+.Bd -literal -offset indent
+gmirror forget data
+gmirror insert data da1
+.Ed
+.Pp
+Create a mirror, deactivate one component, do the backup and connect it again.
+It will not be resynchronized, if there is no need to do so (there were no writes in
+the meantime):
+.Bd -literal -offset indent
+gmirror label data da0 da1
+gmirror deactivate data da1
+dd if=/dev/da1 of=/backup/data.img bs=1m
+gmirror activate data da1
+.Ed
+.Sh SEE ALSO
+.Xr geom 4 ,
+.Xr geom 8 ,
+.Xr mount 8 ,
+.Xr newfs 8 ,
+.Xr umount 8 ,
+.Xr vinum 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 5.3 .
+.Sh AUTHORS
+.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org
+.Sh BUGS
+There should be a way to change a component's priority inside a running mirror.
+.Pp
+There should be a section with an implementation description.
+.Pp
+Documentation for sysctls
+.Va kern.geom.mirror.*
+is missing.
diff --git a/sbin/geom/class/nop/Makefile b/sbin/geom/class/nop/Makefile
new file mode 100644
index 0000000..75a2037
--- /dev/null
+++ b/sbin/geom/class/nop/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../misc
+
+CLASS= nop
+
+.include <bsd.lib.mk>
diff --git a/sbin/geom/class/nop/geom_nop.c b/sbin/geom/class/nop/geom_nop.c
new file mode 100644
index 0000000..054d52d
--- /dev/null
+++ b/sbin/geom/class/nop/geom_nop.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 2004 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <stdint.h>
+#include <libgeom.h>
+#include <geom/nop/g_nop.h>
+
+#include "core/geom.h"
+
+
+uint32_t lib_version = G_LIB_VERSION;
+uint32_t version = G_NOP_VERSION;
+
+static intmax_t failprob = 0;
+static intmax_t offset = 0;
+static intmax_t secsize = 0;
+static intmax_t size = 0;
+
+struct g_command class_commands[] = {
+ { "create", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL,
+ {
+ { 'f', "failprob", &failprob, G_TYPE_NUMBER },
+ { 'o', "offset", &offset, G_TYPE_NUMBER },
+ { 's', "size", &size, G_TYPE_NUMBER },
+ { 'S', "secsize", &secsize, G_TYPE_NUMBER },
+ G_OPT_SENTINEL
+ },
+ "[-v] [-f failprob] [-o offset] [-s size] [-S secsize] dev ..."
+ },
+ { "configure", G_FLAG_VERBOSE, NULL,
+ {
+ { 'f', "failprob", &failprob, G_TYPE_NUMBER },
+ G_OPT_SENTINEL
+ },
+ "[-v] [-f failprob] prov ..."
+ },
+ { "destroy", G_FLAG_VERBOSE, NULL,
+ {
+ { 'f', "force", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-fv] prov ..."
+ },
+ { "reset", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
+ "[-v] prov ..."
+ },
+ G_CMD_SENTINEL
+};
diff --git a/sbin/geom/class/nop/gnop.8 b/sbin/geom/class/nop/gnop.8
new file mode 100644
index 0000000..64c04ca
--- /dev/null
+++ b/sbin/geom/class/nop/gnop.8
@@ -0,0 +1,167 @@
+.\" Copyright (c) 2004-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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 21, 2004
+.Dt GNOP 8
+.Os
+.Sh NAME
+.Nm gnop
+.Nd "control utility for NOP GEOM class"
+.Sh SYNOPSIS
+.Nm
+.Cm create
+.Op Fl v
+.Op Fl f Ar failprob
+.Op Fl o Ar offset
+.Op Fl s Ar size
+.Op Fl S Ar secsize
+.Ar dev ...
+.Nm
+.Cm configure
+.Op Fl v
+.Op Fl f Ar failprob
+.Ar prov ...
+.Nm
+.Cm destroy
+.Op Fl fv
+.Ar prov ...
+.Nm
+.Cm reset
+.Op Fl v
+.Ar prov ...
+.Nm
+.Cm list
+.Nm
+.Cm status
+.Nm
+.Cm load
+.Nm
+.Cm unload
+.Sh DESCRIPTION
+The
+.Nm
+utility is used for setting up transparent providers on existing ones.
+Its main purpose is testing other GEOM classes, as it allows forced provider
+removal and I/O error simulation with a given probability.
+It also gathers the following statistics: number of read requests, number of
+write requests, number of read bytes and number of wrote bytes.
+In addition, it can be used as a good starting point for implementing new GEOM
+class.
+.Pp
+The first argument to
+.Nm
+indicates an action to be performed:
+.Bl -tag -width ".Cm configure"
+.It Cm create
+Set up a transparent provider on the given devices.
+If the operation succeeds, the new provider should appear with name
+.Pa /dev/ Ns Ao Ar dev Ac Ns Pa .nop .
+The kernel module
+.Pa geom_nop.ko
+will be loaded if it is not loaded already.
+.It Cm configure
+Configure existing transparent provider.
+At the moment it is only used for changing failure probability.
+.It Cm destroy
+Turn off the given transparent providers.
+.It Cm reset
+Reset statistics for the given transparent providers.
+.It Cm list
+See
+.Xr geom 8 .
+.It Cm status
+See
+.Xr geom 8 .
+.It Cm load
+See
+.Xr geom 8 .
+.It Cm unload
+See
+.Xr geom 8 .
+.El
+.Pp
+Additional options:
+.Bl -tag -width ".Fl f Ar failprob"
+.It Fl f
+Force the removal of the specified provider.
+.It Fl f Ar failprob
+Specifies failure probability in percentage.
+.It Fl o Ar offset
+Where to begin on the original provider.
+.It Fl s Ar size
+Size of the transparent provider.
+.It Fl S Ar secsize
+Sector size of the transparent provider.
+.It Fl v
+Be more verbose.
+.El
+.Sh SYSCTL VARIABLES
+The following
+.Xr sysctl 8
+variables can be used to control the behavior of the
+.Nm NOP
+GEOM class.
+The default value is shown next to each variable.
+.Bl -tag -width indent
+.It Va kern.geom.nop.debug : No 0
+Debug level of the
+.Nm NOP
+GEOM class.
+This can be set to a number between 0 and 2 inclusive.
+If set to 0 minimal debug information is printed, and if set to 2 the
+maximum amount of debug information is printed.
+.El
+.Sh EXIT STATUS
+Exit status is 0 on success, and 1 if the command fails.
+.Sh EXAMPLES
+The following example shows how to create a transparent provider for disk
+.Pa /dev/da0
+with 50% failure probability, and how to destroy it.
+.Bd -literal -offset indent
+gnop create -v -f 50 da0
+gnop destroy -v da0.nop
+.Ed
+.Pp
+The traffic statistics for the given transparent providers we can get with
+.Cm list
+command.
+Example below shows how can we obtain number of bytes written with
+.Xr newfs 8 :
+.Bd -literal -offset indent
+gnop create da0
+newfs /dev/da0.nop
+gnop list
+.Ed
+.Sh SEE ALSO
+.Xr geom 4 ,
+.Xr geom 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 5.3 .
+.Sh AUTHORS
+.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org
diff --git a/sbin/geom/class/raid3/Makefile b/sbin/geom/class/raid3/Makefile
new file mode 100644
index 0000000..1791669
--- /dev/null
+++ b/sbin/geom/class/raid3/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../misc
+
+CLASS= raid3
+
+DPADD= ${LIBMD}
+LDADD= -lmd
+
+.include <bsd.lib.mk>
diff --git a/sbin/geom/class/raid3/geom_raid3.c b/sbin/geom/class/raid3/geom_raid3.c
new file mode 100644
index 0000000..fb8900b
--- /dev/null
+++ b/sbin/geom/class/raid3/geom_raid3.c
@@ -0,0 +1,323 @@
+/*-
+ * Copyright (c) 2004-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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <errno.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <assert.h>
+#include <libgeom.h>
+#include <geom/raid3/g_raid3.h>
+#include <core/geom.h>
+#include <misc/subr.h>
+
+
+uint32_t lib_version = G_LIB_VERSION;
+uint32_t version = G_RAID3_VERSION;
+
+static void raid3_main(struct gctl_req *req, unsigned f);
+static void raid3_clear(struct gctl_req *req);
+static void raid3_dump(struct gctl_req *req);
+static void raid3_label(struct gctl_req *req);
+
+struct g_command class_commands[] = {
+ { "clear", G_FLAG_VERBOSE, raid3_main, G_NULL_OPTS,
+ "[-v] prov ..."
+ },
+ { "configure", G_FLAG_VERBOSE, NULL,
+ {
+ { 'a', "autosync", NULL, G_TYPE_NONE },
+ { 'd', "dynamic", NULL, G_TYPE_NONE },
+ { 'h', "hardcode", NULL, G_TYPE_NONE },
+ { 'n', "noautosync", NULL, G_TYPE_NONE },
+ { 'r', "round_robin", NULL, G_TYPE_NONE },
+ { 'R', "noround_robin", NULL, G_TYPE_NONE },
+ { 'w', "verify", NULL, G_TYPE_NONE },
+ { 'W', "noverify", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-adhnrRvwW] name"
+ },
+ { "dump", 0, raid3_main, G_NULL_OPTS,
+ "prov ..."
+ },
+ { "insert", G_FLAG_VERBOSE, NULL,
+ {
+ { 'h', "hardcode", NULL, G_TYPE_NONE },
+ { 'n', "number", NULL, G_TYPE_NUMBER },
+ G_OPT_SENTINEL
+ },
+ "[-hv] <-n number> name prov"
+ },
+ { "label", G_FLAG_VERBOSE, raid3_main,
+ {
+ { 'h', "hardcode", NULL, G_TYPE_NONE },
+ { 'n', "noautosync", NULL, G_TYPE_NONE },
+ { 'r', "round_robin", NULL, G_TYPE_NONE },
+ { 'w', "verify", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-hnrvw] name prov prov prov ..."
+ },
+ { "rebuild", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
+ "[-v] name prov"
+ },
+ { "remove", G_FLAG_VERBOSE, NULL,
+ {
+ { 'n', "number", NULL, G_TYPE_NUMBER },
+ G_OPT_SENTINEL
+ },
+ "[-v] <-n number> name"
+ },
+ { "stop", G_FLAG_VERBOSE, NULL,
+ {
+ { 'f', "force", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-fv] name ..."
+ },
+ G_CMD_SENTINEL
+};
+
+static int verbose = 0;
+
+static void
+raid3_main(struct gctl_req *req, unsigned flags)
+{
+ const char *name;
+
+ if ((flags & G_FLAG_VERBOSE) != 0)
+ verbose = 1;
+
+ name = gctl_get_ascii(req, "verb");
+ if (name == NULL) {
+ gctl_error(req, "No '%s' argument.", "verb");
+ return;
+ }
+ if (strcmp(name, "label") == 0)
+ raid3_label(req);
+ else if (strcmp(name, "clear") == 0)
+ raid3_clear(req);
+ else if (strcmp(name, "dump") == 0)
+ raid3_dump(req);
+ else
+ gctl_error(req, "Unknown command: %s.", name);
+}
+
+static void
+raid3_label(struct gctl_req *req)
+{
+ struct g_raid3_metadata md;
+ u_char sector[512];
+ const char *str;
+ unsigned sectorsize, ssize;
+ off_t mediasize, msize;
+ int error, i, nargs, hardcode, noautosync, round_robin, verify;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 4) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+ if (bitcount32(nargs - 2) != 1) {
+ gctl_error(req, "Invalid number of components.");
+ return;
+ }
+
+ strlcpy(md.md_magic, G_RAID3_MAGIC, sizeof(md.md_magic));
+ md.md_version = G_RAID3_VERSION;
+ str = gctl_get_ascii(req, "arg0");
+ strlcpy(md.md_name, str, sizeof(md.md_name));
+ md.md_id = arc4random();
+ md.md_all = nargs - 1;
+ md.md_mflags = 0;
+ md.md_dflags = 0;
+ md.md_genid = 0;
+ md.md_syncid = 1;
+ md.md_sync_offset = 0;
+ noautosync = gctl_get_int(req, "noautosync");
+ if (noautosync)
+ md.md_mflags |= G_RAID3_DEVICE_FLAG_NOAUTOSYNC;
+ round_robin = gctl_get_int(req, "round_robin");
+ if (round_robin)
+ md.md_mflags |= G_RAID3_DEVICE_FLAG_ROUND_ROBIN;
+ verify = gctl_get_int(req, "verify");
+ if (verify)
+ md.md_mflags |= G_RAID3_DEVICE_FLAG_VERIFY;
+ if (round_robin && verify) {
+ gctl_error(req, "Both '%c' and '%c' options given.", 'r', 'w');
+ return;
+ }
+ hardcode = gctl_get_int(req, "hardcode");
+
+ /*
+ * Calculate sectorsize by finding least common multiple from
+ * sectorsizes of every disk and find the smallest mediasize.
+ */
+ mediasize = 0;
+ sectorsize = 0;
+ for (i = 1; i < nargs; i++) {
+ str = gctl_get_ascii(req, "arg%d", i);
+ msize = g_get_mediasize(str);
+ ssize = g_get_sectorsize(str);
+ if (msize == 0 || ssize == 0) {
+ gctl_error(req, "Can't get informations about %s: %s.",
+ str, strerror(errno));
+ return;
+ }
+ msize -= ssize;
+ if (mediasize == 0 || (mediasize > 0 && msize < mediasize))
+ mediasize = msize;
+ if (sectorsize == 0)
+ sectorsize = ssize;
+ else
+ sectorsize = g_lcm(sectorsize, ssize);
+ }
+ md.md_mediasize = mediasize * (nargs - 2);
+ md.md_sectorsize = sectorsize * (nargs - 2);
+
+ /*
+ * Clear last sector first, to spoil all components if device exists.
+ */
+ for (i = 1; i < nargs; i++) {
+ str = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_clear(str, NULL);
+ if (error != 0) {
+ gctl_error(req, "Can't store metadata on %s: %s.", str,
+ strerror(error));
+ return;
+ }
+ }
+
+ /*
+ * Ok, store metadata (use disk number as priority).
+ */
+ for (i = 1; i < nargs; i++) {
+ str = gctl_get_ascii(req, "arg%d", i);
+ msize = g_get_mediasize(str);
+ ssize = g_get_sectorsize(str);
+ if (mediasize < msize - ssize) {
+ fprintf(stderr,
+ "warning: %s: only %jd bytes from %jd bytes used.\n",
+ str, (intmax_t)mediasize, (intmax_t)(msize - ssize));
+ }
+
+ md.md_no = i - 1;
+ md.md_provsize = msize;
+ if (!hardcode)
+ bzero(md.md_provider, sizeof(md.md_provider));
+ else {
+ if (strncmp(str, _PATH_DEV, strlen(_PATH_DEV)) == 0)
+ str += strlen(_PATH_DEV);
+ strlcpy(md.md_provider, str, sizeof(md.md_provider));
+ }
+ if (verify && md.md_no == md.md_all - 1) {
+ /*
+ * In "verify" mode, force synchronization of parity
+ * component on start.
+ */
+ md.md_syncid = 0;
+ }
+ raid3_metadata_encode(&md, sector);
+ error = g_metadata_store(str, sector, sizeof(sector));
+ if (error != 0) {
+ fprintf(stderr, "Can't store metadata on %s: %s.\n",
+ str, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (verbose)
+ printf("Metadata value stored on %s.\n", str);
+ }
+}
+
+static void
+raid3_clear(struct gctl_req *req)
+{
+ const char *name;
+ int error, i, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+
+ for (i = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_clear(name, G_RAID3_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Can't clear metadata on %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (verbose)
+ printf("Metadata cleared on %s.\n", name);
+ }
+}
+
+static void
+raid3_dump(struct gctl_req *req)
+{
+ struct g_raid3_metadata md, tmpmd;
+ const char *name;
+ int error, i, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+
+ for (i = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_read(name, (u_char *)&tmpmd, sizeof(tmpmd),
+ G_RAID3_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Can't read metadata from %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (raid3_metadata_decode((u_char *)&tmpmd, &md) != 0) {
+ fprintf(stderr, "MD5 hash mismatch for %s, skipping.\n",
+ name);
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ printf("Metadata on %s:\n", name);
+ raid3_metadata_dump(&md);
+ printf("\n");
+ }
+}
diff --git a/sbin/geom/class/raid3/graid3.8 b/sbin/geom/class/raid3/graid3.8
new file mode 100644
index 0000000..b2da0bf
--- /dev/null
+++ b/sbin/geom/class/raid3/graid3.8
@@ -0,0 +1,241 @@
+.\" Copyright (c) 2004-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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 22, 2004
+.Dt GRAID3 8
+.Os
+.Sh NAME
+.Nm graid3
+.Nd "control utility for RAID3 devices"
+.Sh SYNOPSIS
+.Nm
+.Cm label
+.Op Fl hnrvw
+.Ar name
+.Ar prov prov prov ...
+.Nm
+.Cm clear
+.Op Fl v
+.Ar prov ...
+.Nm
+.Cm configure
+.Op Fl adhnrRvwW
+.Ar name
+.Nm
+.Cm rebuild
+.Op Fl v
+.Ar name
+.Ar prov
+.Nm
+.Cm insert
+.Op Fl hv
+.Fl n Ar number
+.Ar name
+.Ar prov
+.Nm
+.Cm remove
+.Op Fl v
+.Fl n Ar number
+.Ar name
+.Nm
+.Cm stop
+.Op Fl fv
+.Ar name ...
+.Nm
+.Cm list
+.Nm
+.Cm status
+.Nm
+.Cm load
+.Nm
+.Cm unload
+.Sh DESCRIPTION
+The
+.Nm
+utility is used for RAID3 array configuration.
+After a device is created, all components are detected and configured
+automatically.
+All operations such as failure detection, stale component detection, rebuild
+of stale components, etc.\& are also done automatically.
+The
+.Nm
+utility uses on-disk metadata (the provider's last sector) to store all needed
+information.
+.Pp
+The first argument to
+.Nm
+indicates an action to be performed:
+.Bl -tag -width ".Cm configure"
+.It Cm label
+Create a RAID3 device.
+The last given component will contain parity data, whilst the others
+will all contain regular data.
+The number of components must be equal to 3, 5, 9, 17, etc.\& (2^n + 1).
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl h"
+.It Fl h
+Hardcode providers' names in metadata.
+.It Fl n
+Turn off autosynchronization of stale components.
+.It Fl r
+Use parity component for reading in round-robin fashion.
+Without this option the parity component is not used at all for reading operations
+when the device is in a complete state.
+With this option specified random I/O read operations are even 40% faster,
+but sequential reads are slower.
+One cannot use this option if the
+.Fl w
+option is also specified.
+.It Fl w
+Use verify reading feature.
+When reading from a device in a complete state, also read data from the parity component
+and verify the data by comparing XORed regular data with parity data.
+If verification fails, an
+.Er EIO
+error is returned and the value of the
+.Va kern.geom.raid3.stat.parity_mismatch
+sysctl is increased.
+One cannot use this option if the
+.Fl r
+option is also specified.
+.El
+.It Cm clear
+Clear metadata on the given providers.
+.It Cm configure
+Configure the given device.
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl a"
+.It Fl a
+Turn on autosynchronization of stale components.
+.It Fl d
+Do not hardcode providers' names in metadata.
+.It Fl h
+Hardcode providers' names in metadata.
+.It Fl n
+Turn off autosynchronization of stale components.
+.It Fl r
+Turn on round-robin reading.
+.It Fl R
+Turn off round-robin reading.
+.It Fl w
+Turn on verify reading.
+.It Fl W
+Turn off verify reading.
+.El
+.It Cm rebuild
+Rebuild the given component forcibly.
+If autosynchronization was not turned off for the given device, this command
+should be unnecessary.
+.It Cm insert
+Add the given component to the existing array, if one of the components was
+removed previously with the
+.Cm remove
+command or if one component is missing and will not be connected again.
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl h"
+.It Fl h
+Hardcode providers' names in metadata.
+.El
+.It Cm remove
+Remove the given component from the given array and clear metadata on it.
+.It Cm stop
+Stop the given arrays.
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl f"
+.It Fl f
+Stop the given array even if it is opened.
+.El
+.It Cm list
+See
+.Xr geom 8 .
+.It Cm status
+See
+.Xr geom 8 .
+.It Cm load
+See
+.Xr geom 8 .
+.It Cm unload
+See
+.Xr geom 8 .
+.El
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl v"
+.It Fl v
+Be more verbose.
+.El
+.Sh EXIT STATUS
+Exit status is 0 on success, and 1 if the command fails.
+.Sh EXAMPLES
+Use 3 disks to setup a RAID3 array (with the round-robin reading feature).
+Create a file system, mount it, then unmount it and stop device:
+.Bd -literal -offset indent
+graid3 label -v -r data da0 da1 da2
+newfs /dev/raid3/data
+mount /dev/raid3/data /mnt
+\&...
+umount /mnt
+graid3 stop data
+graid3 unload
+.Ed
+.Pp
+.Pp
+Create a RAID3 array, but do not use the automatic synchronization feature.
+Rebuild parity component:
+.Bd -literal -offset indent
+graid3 label -n data da0 da1 da2
+graid3 rebuild data da2
+.Ed
+.Pp
+Replace one data disk with a brand new one:
+.Bd -literal -offset indent
+graid3 remove -n 0 data
+graid3 insert -n 0 data da5
+.Ed
+.Sh SEE ALSO
+.Xr geom 4 ,
+.Xr geom 8 ,
+.Xr mount 8 ,
+.Xr newfs 8 ,
+.Xr umount 8 ,
+.Xr vinum 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 5.3 .
+.Sh AUTHORS
+.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org
+.Sh BUGS
+There should be a section with an implementation description.
+.Pp
+Documentation for sysctls
+.Va kern.geom.raid3.*
+is missing.
diff --git a/sbin/geom/class/shsec/Makefile b/sbin/geom/class/shsec/Makefile
new file mode 100644
index 0000000..ea38f15
--- /dev/null
+++ b/sbin/geom/class/shsec/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../misc
+
+CLASS= shsec
+
+.include <bsd.lib.mk>
diff --git a/sbin/geom/class/shsec/geom_shsec.c b/sbin/geom/class/shsec/geom_shsec.c
new file mode 100644
index 0000000..7789fc2
--- /dev/null
+++ b/sbin/geom/class/shsec/geom_shsec.c
@@ -0,0 +1,259 @@
+/*-
+ * Copyright (c) 2004-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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <errno.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <assert.h>
+#include <libgeom.h>
+#include <geom/shsec/g_shsec.h>
+
+#include "core/geom.h"
+#include "misc/subr.h"
+
+
+uint32_t lib_version = G_LIB_VERSION;
+uint32_t version = G_SHSEC_VERSION;
+
+static void shsec_main(struct gctl_req *req, unsigned flags);
+static void shsec_clear(struct gctl_req *req);
+static void shsec_dump(struct gctl_req *req);
+static void shsec_label(struct gctl_req *req);
+
+struct g_command class_commands[] = {
+ { "clear", G_FLAG_VERBOSE, shsec_main, G_NULL_OPTS,
+ "clear [-v] prov ..."
+ },
+ { "dump", 0, shsec_main, G_NULL_OPTS,
+ "dump prov ..."
+ },
+ { "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, shsec_main,
+ {
+ { 'h', "hardcode", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-hv] name prov prov ..."
+ },
+ { "stop", G_FLAG_VERBOSE, NULL,
+ {
+ { 'f', "force", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-fv] name ..."
+ },
+ G_CMD_SENTINEL
+};
+
+static int verbose = 0;
+
+static void
+shsec_main(struct gctl_req *req, unsigned flags)
+{
+ const char *name;
+
+ if ((flags & G_FLAG_VERBOSE) != 0)
+ verbose = 1;
+
+ name = gctl_get_ascii(req, "verb");
+ if (name == NULL) {
+ gctl_error(req, "No '%s' argument.", "verb");
+ return;
+ }
+ if (strcmp(name, "label") == 0)
+ shsec_label(req);
+ else if (strcmp(name, "clear") == 0)
+ shsec_clear(req);
+ else if (strcmp(name, "dump") == 0)
+ shsec_dump(req);
+ else
+ gctl_error(req, "Unknown command: %s.", name);
+}
+
+static void
+shsec_label(struct gctl_req *req)
+{
+ struct g_shsec_metadata md;
+ off_t compsize, msize;
+ u_char sector[512];
+ unsigned ssize, secsize;
+ const char *name;
+ int error, i, nargs, hardcode;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs <= 2) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+ hardcode = gctl_get_int(req, "hardcode");
+
+ /*
+ * Clear last sector first to spoil all components if device exists.
+ */
+ compsize = 0;
+ secsize = 0;
+ for (i = 1; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ msize = g_get_mediasize(name);
+ ssize = g_get_sectorsize(name);
+ if (msize == 0 || ssize == 0) {
+ gctl_error(req, "Can't get informations about %s: %s.",
+ name, strerror(errno));
+ return;
+ }
+ msize -= ssize;
+ if (compsize == 0 || (compsize > 0 && msize < compsize))
+ compsize = msize;
+ if (secsize == 0)
+ secsize = ssize;
+ else
+ secsize = g_lcm(secsize, ssize);
+
+ error = g_metadata_clear(name, NULL);
+ if (error != 0) {
+ gctl_error(req, "Can't store metadata on %s: %s.", name,
+ strerror(error));
+ return;
+ }
+ }
+
+ strlcpy(md.md_magic, G_SHSEC_MAGIC, sizeof(md.md_magic));
+ md.md_version = G_SHSEC_VERSION;
+ name = gctl_get_ascii(req, "arg0");
+ strlcpy(md.md_name, name, sizeof(md.md_name));
+ md.md_id = arc4random();
+ md.md_all = nargs - 1;
+
+ /*
+ * Ok, store metadata.
+ */
+ for (i = 1; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ msize = g_get_mediasize(name);
+ ssize = g_get_sectorsize(name);
+ if (compsize < msize - ssize) {
+ fprintf(stderr,
+ "warning: %s: only %jd bytes from %jd bytes used.\n",
+ name, (intmax_t)compsize, (intmax_t)(msize - ssize));
+ }
+
+ md.md_no = i - 1;
+ md.md_provsize = msize;
+ if (!hardcode)
+ bzero(md.md_provider, sizeof(md.md_provider));
+ else {
+ if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) == 0)
+ name += strlen(_PATH_DEV);
+ strlcpy(md.md_provider, name, sizeof(md.md_provider));
+ }
+ shsec_metadata_encode(&md, sector);
+ error = g_metadata_store(name, sector, sizeof(sector));
+ if (error != 0) {
+ fprintf(stderr, "Can't store metadata on %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (verbose)
+ printf("Metadata value stored on %s.\n", name);
+ }
+}
+
+static void
+shsec_clear(struct gctl_req *req)
+{
+ const char *name;
+ int error, i, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+
+ for (i = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_clear(name, G_SHSEC_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Can't clear metadata on %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (verbose)
+ printf("Metadata cleared on %s.\n", name);
+ }
+}
+
+static void
+shsec_metadata_dump(const struct g_shsec_metadata *md)
+{
+
+ printf(" Magic string: %s\n", md->md_magic);
+ printf(" Metadata version: %u\n", (u_int)md->md_version);
+ printf(" Device name: %s\n", md->md_name);
+ printf(" Device ID: %u\n", (u_int)md->md_id);
+ printf(" Disk number: %u\n", (u_int)md->md_no);
+ printf("Total number of disks: %u\n", (u_int)md->md_all);
+ printf(" Hardcoded provider: %s\n", md->md_provider);
+}
+
+static void
+shsec_dump(struct gctl_req *req)
+{
+ struct g_shsec_metadata md, tmpmd;
+ const char *name;
+ int error, i, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+
+ for (i = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_read(name, (u_char *)&tmpmd, sizeof(tmpmd),
+ G_SHSEC_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Can't read metadata from %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ shsec_metadata_decode((u_char *)&tmpmd, &md);
+ printf("Metadata on %s:\n", name);
+ shsec_metadata_dump(&md);
+ printf("\n");
+ }
+}
diff --git a/sbin/geom/class/shsec/gshsec.8 b/sbin/geom/class/shsec/gshsec.8
new file mode 100644
index 0000000..67dff5d
--- /dev/null
+++ b/sbin/geom/class/shsec/gshsec.8
@@ -0,0 +1,130 @@
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 8, 2005
+.Dt GSHSEC 8
+.Os
+.Sh NAME
+.Nm gshsec
+.Nd "control utility for shared secret devices"
+.Sh SYNOPSIS
+.Nm
+.Cm label
+.Op Fl hv
+.Ar name
+.Ar prov prov ...
+.Nm
+.Cm stop
+.Op Fl fv
+.Ar name ...
+.Nm
+.Cm clear
+.Op Fl v
+.Ar prov ...
+.Nm
+.Cm dump
+.Ar prov ...
+.Nm
+.Cm list
+.Nm
+.Cm status
+.Nm
+.Cm load
+.Nm
+.Cm unload
+.Sh DESCRIPTION
+The
+.Nm
+utility is used for setting up a device which contains a shared secret.
+The secret is shared between the given providers.
+To collect the secret, all providers are needed.
+If one of the components is missing, there is no way to get any useful data from
+the rest of them.
+The first argument to
+.Nm
+indicates an action to be performed:
+.Bl -tag -width ".Cm destroy"
+.It Cm label
+Set up a shared secret device from the given components with the specified
+.Ar name .
+Metadata are stored in the last sector of every component.
+.It Cm stop
+Turn off an existing shared secret device by its
+.Ar name .
+This command does not touch on-disk metadata!
+.It Cm clear
+Clear metadata on the given providers.
+.It Cm dump
+Dump metadata stored on the given providers.
+.It Cm list
+See
+.Xr geom 8 .
+.It Cm status
+See
+.Xr geom 8 .
+.It Cm load
+See
+.Xr geom 8 .
+.It Cm unload
+See
+.Xr geom 8 .
+.El
+.Pp
+Additional options:
+.Bl -tag -width ".Fl f"
+.It Fl f
+Force the removal of the specified shared secret device.
+.It Fl h
+Hardcode providers' names in metadata.
+.It Fl v
+Be more verbose.
+.El
+.Sh EXIT STATUS
+Exit status is 0 on success, and 1 if the command fails.
+.Sh EXAMPLES
+The following example shows how to create a shared secret device.
+The secret will be split between a slice on a local disk and a USB Pen drive.
+.Bd -literal -offset indent
+gshsec label -v secret /dev/ad0s1 /dev/da0
+newfs /dev/shsec/secret
+.Ed
+.Pp
+From now on, when the USB Pen drive is inserted, it will be automatically
+detected and connected, making the secret available via the
+.Pa /dev/shsec/secret
+device.
+.Sh SEE ALSO
+.Xr geom 4 ,
+.Xr gbde 8 ,
+.Xr geom 8 ,
+.Xr newfs 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 5.4 .
+.Sh AUTHORS
+.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org
diff --git a/sbin/geom/class/stripe/Makefile b/sbin/geom/class/stripe/Makefile
new file mode 100644
index 0000000..53a35ff
--- /dev/null
+++ b/sbin/geom/class/stripe/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../misc
+
+CLASS= stripe
+
+.include <bsd.lib.mk>
diff --git a/sbin/geom/class/stripe/geom_stripe.c b/sbin/geom/class/stripe/geom_stripe.c
new file mode 100644
index 0000000..f98e964
--- /dev/null
+++ b/sbin/geom/class/stripe/geom_stripe.c
@@ -0,0 +1,285 @@
+/*-
+ * Copyright (c) 2004-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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <errno.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <assert.h>
+#include <libgeom.h>
+#include <geom/stripe/g_stripe.h>
+
+#include "core/geom.h"
+#include "misc/subr.h"
+
+
+uint32_t lib_version = G_LIB_VERSION;
+uint32_t version = G_STRIPE_VERSION;
+
+static intmax_t default_stripesize = 4096;
+
+static void stripe_main(struct gctl_req *req, unsigned flags);
+static void stripe_clear(struct gctl_req *req);
+static void stripe_dump(struct gctl_req *req);
+static void stripe_label(struct gctl_req *req);
+
+struct g_command class_commands[] = {
+ { "clear", G_FLAG_VERBOSE, stripe_main, G_NULL_OPTS,
+ "[-v] prov ..."
+ },
+ { "create", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL,
+ {
+ { 's', "stripesize", &default_stripesize, G_TYPE_NUMBER },
+ G_OPT_SENTINEL
+ },
+ "[-hv] [-s stripesize] name prov prov ..."
+ },
+ { "destroy", G_FLAG_VERBOSE, NULL,
+ {
+ { 'f', "force", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-fv] name ..."
+ },
+ { "dump", 0, stripe_main, G_NULL_OPTS,
+ "dump prov ..."
+ },
+ { "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, stripe_main,
+ {
+ { 'h', "hardcode", NULL, G_TYPE_NONE },
+ { 's', "stripesize", &default_stripesize, G_TYPE_NUMBER },
+ G_OPT_SENTINEL
+ },
+ "[-hv] [-s stripesize] name prov prov ..."
+ },
+ { "stop", G_FLAG_VERBOSE, NULL,
+ {
+ { 'f', "force", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-fv] name ..."
+ },
+ G_CMD_SENTINEL
+};
+
+static int verbose = 0;
+
+static void
+stripe_main(struct gctl_req *req, unsigned flags)
+{
+ const char *name;
+
+ if ((flags & G_FLAG_VERBOSE) != 0)
+ verbose = 1;
+
+ name = gctl_get_ascii(req, "verb");
+ if (name == NULL) {
+ gctl_error(req, "No '%s' argument.", "verb");
+ return;
+ }
+ if (strcmp(name, "label") == 0)
+ stripe_label(req);
+ else if (strcmp(name, "clear") == 0)
+ stripe_clear(req);
+ else if (strcmp(name, "dump") == 0)
+ stripe_dump(req);
+ else
+ gctl_error(req, "Unknown command: %s.", name);
+}
+
+static void
+stripe_label(struct gctl_req *req)
+{
+ struct g_stripe_metadata md;
+ intmax_t stripesize;
+ off_t compsize, msize;
+ u_char sector[512];
+ unsigned ssize, secsize;
+ const char *name;
+ int error, i, nargs, hardcode;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 3) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+ hardcode = gctl_get_int(req, "hardcode");
+
+ /*
+ * Clear last sector first to spoil all components if device exists.
+ */
+ compsize = 0;
+ secsize = 0;
+ for (i = 1; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ msize = g_get_mediasize(name);
+ ssize = g_get_sectorsize(name);
+ if (msize == 0 || ssize == 0) {
+ gctl_error(req, "Can't get informations about %s: %s.",
+ name, strerror(errno));
+ return;
+ }
+ msize -= ssize;
+ if (compsize == 0 || (compsize > 0 && msize < compsize))
+ compsize = msize;
+ if (secsize == 0)
+ secsize = ssize;
+ else
+ secsize = g_lcm(secsize, ssize);
+
+ error = g_metadata_clear(name, NULL);
+ if (error != 0) {
+ gctl_error(req, "Can't store metadata on %s: %s.", name,
+ strerror(error));
+ return;
+ }
+ }
+
+ strlcpy(md.md_magic, G_STRIPE_MAGIC, sizeof(md.md_magic));
+ md.md_version = G_STRIPE_VERSION;
+ name = gctl_get_ascii(req, "arg0");
+ strlcpy(md.md_name, name, sizeof(md.md_name));
+ md.md_id = arc4random();
+ md.md_all = nargs - 1;
+ stripesize = gctl_get_intmax(req, "stripesize");
+ if ((stripesize % secsize) != 0) {
+ gctl_error(req, "Stripesize should be multiple of %u.",
+ secsize);
+ return;
+ }
+ md.md_stripesize = stripesize;
+
+ /*
+ * Ok, store metadata.
+ */
+ for (i = 1; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ msize = g_get_mediasize(name);
+ ssize = g_get_sectorsize(name);
+ if (compsize < msize - ssize) {
+ fprintf(stderr,
+ "warning: %s: only %jd bytes from %jd bytes used.\n",
+ name, (intmax_t)compsize, (intmax_t)(msize - ssize));
+ }
+
+ md.md_no = i - 1;
+ md.md_provsize = msize;
+ if (!hardcode)
+ bzero(md.md_provider, sizeof(md.md_provider));
+ else {
+ if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) == 0)
+ name += strlen(_PATH_DEV);
+ strlcpy(md.md_provider, name, sizeof(md.md_provider));
+ }
+ stripe_metadata_encode(&md, sector);
+ error = g_metadata_store(name, sector, sizeof(sector));
+ if (error != 0) {
+ fprintf(stderr, "Can't store metadata on %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (verbose)
+ printf("Metadata value stored on %s.\n", name);
+ }
+}
+
+static void
+stripe_clear(struct gctl_req *req)
+{
+ const char *name;
+ int error, i, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+
+ for (i = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_clear(name, G_STRIPE_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Can't clear metadata on %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ if (verbose)
+ printf("Metadata cleared on %s.\n", name);
+ }
+}
+
+static void
+stripe_metadata_dump(const struct g_stripe_metadata *md)
+{
+
+ printf(" Magic string: %s\n", md->md_magic);
+ printf(" Metadata version: %u\n", (u_int)md->md_version);
+ printf(" Device name: %s\n", md->md_name);
+ printf(" Device ID: %u\n", (u_int)md->md_id);
+ printf(" Disk number: %u\n", (u_int)md->md_no);
+ printf("Total number of disks: %u\n", (u_int)md->md_all);
+ printf(" Stripe size: %u\n", (u_int)md->md_stripesize);
+ printf(" Hardcoded provider: %s\n", md->md_provider);
+}
+
+static void
+stripe_dump(struct gctl_req *req)
+{
+ struct g_stripe_metadata md, tmpmd;
+ const char *name;
+ int error, i, nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+
+ for (i = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ error = g_metadata_read(name, (u_char *)&tmpmd, sizeof(tmpmd),
+ G_STRIPE_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Can't read metadata from %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req, "Not fully done.");
+ continue;
+ }
+ stripe_metadata_decode((u_char *)&tmpmd, &md);
+ printf("Metadata on %s:\n", name);
+ stripe_metadata_dump(&md);
+ printf("\n");
+ }
+}
diff --git a/sbin/geom/class/stripe/gstripe.8 b/sbin/geom/class/stripe/gstripe.8
new file mode 100644
index 0000000..ee70184
--- /dev/null
+++ b/sbin/geom/class/stripe/gstripe.8
@@ -0,0 +1,246 @@
+.\" Copyright (c) 2004-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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 21, 2004
+.Dt GSTRIPE 8
+.Os
+.Sh NAME
+.Nm gstripe
+.Nd "control utility for striped devices"
+.Sh SYNOPSIS
+.Nm
+.Cm create
+.Op Fl v
+.Op Fl s Ar stripesize
+.Ar name
+.Ar prov prov ...
+.Nm
+.Cm destroy
+.Op Fl fv
+.Ar name ...
+.Nm
+.Cm label
+.Op Fl hv
+.Op Fl s Ar stripesize
+.Ar name
+.Ar prov prov ...
+.Nm
+.Cm stop
+.Op Fl fv
+.Ar name ...
+.Nm
+.Cm clear
+.Op Fl v
+.Ar prov ...
+.Nm
+.Cm dump
+.Ar prov ...
+.Nm
+.Cm list
+.Nm
+.Cm status
+.Nm
+.Cm load
+.Nm
+.Cm unload
+.Sh DESCRIPTION
+The
+.Nm
+utility is used for setting up a stripe on two or more disks.
+The striped device can be configured using two different methods:
+.Dq manual
+or
+.Dq automatic .
+When using the
+.Dq manual
+method, no metadata are stored on the devices, so the striped
+device has to be configured by hand every time it is needed.
+The
+.Dq automatic
+method uses on-disk metadata to detect devices.
+Once devices are labeled, they will be automatically detected and
+configured.
+.Pp
+The first argument to
+.Nm
+indicates an action to be performed:
+.Bl -tag -width ".Cm destroy"
+.It Cm create
+Set up a striped device from the given devices with specified
+.Ar name .
+This is the
+.Dq manual
+method and the stripe will not exist after a reboot (see
+.Sx DESCRIPTION
+above).
+The kernel module
+.Pa geom_stripe.ko
+will be loaded if it is not loaded already.
+.It Cm label
+Set up a striped device from the given devices with the specified
+.Ar name .
+This is the
+.Dq automatic
+method, where metadata are stored in every device's last sector.
+The kernel module
+.Pa geom_stripe.ko
+will be loaded if it is not loaded already.
+.It Cm stop
+Turn off an existing striped device by its
+.Ar name .
+This command does not touch on-disk metadata!
+.It Cm destroy
+Same as
+.Cm stop .
+.It Cm clear
+Clear metadata on the given devices.
+.It Cm dump
+Dump metadata stored on the given devices.
+.It Cm list
+See
+.Xr geom 8 .
+.It Cm status
+See
+.Xr geom 8 .
+.It Cm load
+See
+.Xr geom 8 .
+.It Cm unload
+See
+.Xr geom 8 .
+.El
+.Pp
+Additional options:
+.Bl -tag -width ".Fl s Ar stripesize"
+.It Fl f
+Force the removal of the specified striped device.
+.It Fl h
+Hardcode providers' names in metadata.
+.It Fl s Ar stripesize
+Specifies size of stripe block in bytes.
+The
+.Ar stripesize
+must be a multiple of the largest sector size of all the providers.
+.It Fl v
+Be more verbose.
+.El
+.Sh SYSCTL VARIABLES
+The following
+.Xr sysctl 8
+variables can be used to control the behavior of the
+.Nm STRIPE
+GEOM class.
+The default value is shown next to each variable.
+.Bl -tag -width indent
+.It Va kern.geom.stripe.debug : No 0
+Debug level of the
+.Nm STRIPE
+GEOM class.
+This can be set to a number between 0 and 3 inclusive.
+If set to 0 minimal debug information is printed, and if set to 3 the
+maximum amount of debug information is printed.
+.It Va kern.geom.stripe.fast : No 0
+If set to a non-zero value enable
+.Dq "fast mode"
+instead of the normal
+.Dq "economic mode" .
+Compared to
+.Dq "economic mode" ,
+.Dq "fast mode"
+uses more memory, but it is much faster for smaller stripe sizes.
+If enough memory cannot be allocated,
+.Nm STRIPE
+will fall back to
+.Dq "economic mode" .
+.It Va kern.geom.stripe.maxmem : No 13107200
+Maximum amount of memory that can be consumed by
+.Dq "fast mode"
+(in bytes).
+This
+.Xr sysctl 8
+variable is read-only and can only be set as a tunable in
+.Xr loader.conf 5 .
+.It Va kern.geom.stripe.fast_failed
+A count of how many times
+.Dq "fast mode"
+has failed due to an insufficient amount of memory.
+If this value is large, you should consider increasing the
+.Va kern.geom.stripe.maxmem
+value.
+.El
+.Sh EXIT STATUS
+Exit status is 0 on success, and 1 if the command fails.
+.Sh EXAMPLES
+The following example shows how to set up a striped device from four disks with a
+128KB stripe size for automatic configuration,
+create a file system on it,
+and mount it:
+.Bd -literal -offset indent
+gstripe label -v -s 131072 data /dev/da0 /dev/da1 /dev/da2 /dev/da3
+newfs /dev/stripe/data
+mount /dev/stripe/data /mnt
+[...]
+umount /mnt
+gstripe stop data
+gstripe unload
+.Ed
+.Sh COMPATIBILITY
+The
+.Nm
+interleave is in number of bytes,
+unlike
+.Xr ccdconfig 8
+and
+.Xr atacontrol 8
+which use the number of sectors.
+A
+.Xr ccdconfig 8
+.Ar ileave
+of
+.Ql 128
+is 64 KB (128 512B sectors).
+The same stripe interleave would be specified as
+.Ql 65536
+for
+.Nm .
+.Sh SEE ALSO
+.Xr geom 4 ,
+.Xr loader.conf 5 ,
+.Xr atacontrol 8 ,
+.Xr ccdconfig 8 ,
+.Xr geom 8 ,
+.Xr mount 8 ,
+.Xr newfs 8 ,
+.Xr sysctl 8 ,
+.Xr umount 8 ,
+.Xr vinum 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 5.3 .
+.Sh AUTHORS
+.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org
diff --git a/sbin/geom/core/Makefile b/sbin/geom/core/Makefile
new file mode 100644
index 0000000..d76c35b
--- /dev/null
+++ b/sbin/geom/core/Makefile
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../misc
+
+PROG= geom
+MAN= geom.8
+SRCS= geom.c subr.c
+
+NO_SHARED=NO
+
+CFLAGS+= -DCLASS_DIR=\"${CLASS_DIR}\"
+CFLAGS+= -I${.CURDIR}/../../../sys -I${.CURDIR} -I${.CURDIR}/..
+
+DPADD= ${LIBGEOM} ${LIBSBUF} ${LIBBSDXML} ${LIBUTIL}
+LDADD= -lgeom -lsbuf -lbsdxml -lutil
+
+.include <bsd.prog.mk>
diff --git a/sbin/geom/core/geom.8 b/sbin/geom/core/geom.8
new file mode 100644
index 0000000..7ab57ef
--- /dev/null
+++ b/sbin/geom/core/geom.8
@@ -0,0 +1,171 @@
+.\" Copyright (c) 2004-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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 21, 2004
+.Dt GEOM 8
+.Os
+.Sh NAME
+.Nm geom
+.Nd "universal control utility for GEOM classes"
+.Sh SYNOPSIS
+.Nm
+.Ar class
+.Cm help
+.Nm
+.Ar class
+.Cm list
+.Op Ar name ...
+.Nm
+.Ar class
+.Cm status
+.Op Fl s
+.Op Ar name ...
+.Nm
+.Ar class
+.Cm load
+.Op Fl v
+.Nm
+.Ar class
+.Cm unload
+.Op Fl v
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to control various GEOM classes.
+A class has to be aware of
+.Xr geom 8
+comunication methods, but there are also some standard commands
+which can be used for existing
+.Xr geom 8
+unaware classes.
+Here is the list of standard commands:
+.Bl -tag -width ".Cm status"
+.It Cm help
+List all available commands for the given class.
+.It Cm list
+Print detailed information (within the given class) about all geoms
+(if no additional arguments were specified) or the given geoms.
+This command is only available if the given class exists in the kernel.
+.It Cm status
+Print general information (within the given class) about all geoms
+(if no additional arguments were specified) or the given geoms.
+This command is only available if the given class exists in the kernel.
+.Pp
+Additional options include:
+.Bl -tag -width ".Fl s"
+.It Fl s
+Produce script-friendly output.
+.El
+.It Cm load
+Load the kernel module that implements the given class.
+This command is only available if the class does not yet exist in the kernel and
+the file
+.Pa geom_ Ns Ao Ar class Ac Ns Pa .ko
+can be found in one of the directories specifed in
+.Va kern.module_path
+sysctl.
+.It Cm unload
+Unload the kernel module which implements the given class.
+This command is only available if the given class is loaded as a
+kernel module.
+.El
+.Pp
+Class-specific commands are implemented as shared libraries which
+are stored in
+.Pa /lib/geom/
+directory and are loaded via
+.Xr dlopen 3
+function when the class name is known.
+When a class-specific shared library exists, a direct utility should also be
+available under the name of
+.Nm g Ns Ar class .
+.Pp
+Currently available classes which are aware of
+.Xr geom 8 :
+.Pp
+.Bl -bullet -offset indent -compact
+.It
+CONCAT
+.It
+ELI
+.It
+LABEL
+.It
+MIRROR
+.It
+NOP
+.It
+RAID3
+.It
+SHSEC
+.It
+STRIPE
+.El
+.Sh ENVIRONMENT
+The following environment variables affect the execution of
+.Nm :
+.Bl -tag -width ".Ev GEOM_LIBRARY_PATH"
+.It Ev GEOM_LIBRARY_PATH
+Specifies the path where shared libraries are stored instead of
+.Pa /lib/geom/ .
+.El
+.Sh EXIT STATUS
+Exit status is 0 on success, and 1 if the command fails.
+.Sh EXAMPLES
+The following example shows how to set up a stripe on three disks for automatic
+configuration:
+.Bd -literal -offset indent
+geom stripe label -v -s 65536 data /dev/da0 /dev/da1 /dev/da2
+or:
+gstripe label -v -s 65536 data /dev/da0 /dev/da1 /dev/da2
+.Ed
+.Pp
+Print the list of all providers from the DISK class:
+.Bd -literal -offset indent
+geom disk list
+.Ed
+.Pp
+Unload a kernel module which implements the MD class:
+.Bd -literal -offset indent
+geom md unload
+.Ed
+.Sh SEE ALSO
+.Xr geom 4 ,
+.Xr gconcat 8 ,
+.Xr geli 8 ,
+.Xr glabel 8 ,
+.Xr gmirror 8 ,
+.Xr gnop 8 ,
+.Xr graid3 8 ,
+.Xr gshsec 8 ,
+.Xr gstripe 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 5.3 .
+.Sh AUTHORS
+.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org
diff --git a/sbin/geom/core/geom.c b/sbin/geom/core/geom.c
new file mode 100644
index 0000000..402b5c3
--- /dev/null
+++ b/sbin/geom/core/geom.c
@@ -0,0 +1,1000 @@
+/*-
+ * Copyright (c) 2004-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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <libutil.h>
+#include <inttypes.h>
+#include <dlfcn.h>
+#include <assert.h>
+#include <libgeom.h>
+#include <geom.h>
+
+#include "misc/subr.h"
+
+
+static char comm[MAXPATHLEN], *class_name = NULL, *gclass_name = NULL;
+static uint32_t *version = NULL;
+static int verbose = 0;
+static struct g_command *class_commands = NULL;
+
+#define GEOM_CLASS_CMDS 0x01
+#define GEOM_STD_CMDS 0x02
+static struct g_command *find_command(const char *cmdstr, int flags);
+static int std_available(const char *name);
+
+static void std_help(struct gctl_req *req, unsigned flags);
+static void std_list(struct gctl_req *req, unsigned flags);
+static void std_status(struct gctl_req *req, unsigned flags);
+static void std_load(struct gctl_req *req, unsigned flags);
+static void std_unload(struct gctl_req *req, unsigned flags);
+
+struct g_command std_commands[] = {
+ { "help", 0, std_help, G_NULL_OPTS, NULL },
+ { "list", 0, std_list, G_NULL_OPTS,
+ "[name ...]"
+ },
+ { "status", 0, std_status,
+ {
+ { 's', "script", NULL, G_TYPE_NONE },
+ G_OPT_SENTINEL
+ },
+ "[-s] [name ...]"
+ },
+ { "load", G_FLAG_VERBOSE | G_FLAG_LOADKLD, std_load, G_NULL_OPTS, NULL },
+ { "unload", G_FLAG_VERBOSE, std_unload, G_NULL_OPTS, NULL },
+ G_CMD_SENTINEL
+};
+
+static void
+usage_command(struct g_command *cmd, const char *prefix)
+{
+ struct g_option *opt;
+ unsigned i;
+
+ fprintf(stderr, "%s %s %s", prefix, comm, cmd->gc_name);
+ if (cmd->gc_usage != NULL) {
+ fprintf(stderr, " %s\n", cmd->gc_usage);
+ return;
+ }
+ if ((cmd->gc_flags & G_FLAG_VERBOSE) != 0)
+ fprintf(stderr, " [-v]");
+ for (i = 0; ; i++) {
+ opt = &cmd->gc_options[i];
+ if (opt->go_name == NULL)
+ break;
+ if (opt->go_val != NULL || opt->go_type == G_TYPE_NONE)
+ fprintf(stderr, " [");
+ else
+ fprintf(stderr, " ");
+ fprintf(stderr, "-%c", opt->go_char);
+ if (opt->go_type != G_TYPE_NONE)
+ fprintf(stderr, " %s", opt->go_name);
+ if (opt->go_val != NULL || opt->go_type == G_TYPE_NONE)
+ fprintf(stderr, "]");
+ }
+ fprintf(stderr, "\n");
+}
+
+static void
+usage(void)
+{
+
+ if (class_name == NULL) {
+ errx(EXIT_FAILURE, "usage: %s <class> <command> [options]",
+ "geom");
+ } else {
+ struct g_command *cmd;
+ const char *prefix;
+ unsigned i;
+
+ prefix = "usage:";
+ if (class_commands != NULL) {
+ for (i = 0; ; i++) {
+ cmd = &class_commands[i];
+ if (cmd->gc_name == NULL)
+ break;
+ usage_command(cmd, prefix);
+ prefix = " ";
+ }
+ }
+ for (i = 0; ; i++) {
+ cmd = &std_commands[i];
+ if (cmd->gc_name == NULL)
+ break;
+ /*
+ * If class defines command, which has the same name as
+ * standard command, skip it, because it was already
+ * shown on usage().
+ */
+ if (find_command(cmd->gc_name, GEOM_CLASS_CMDS) != NULL)
+ continue;
+ usage_command(cmd, prefix);
+ prefix = " ";
+ }
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void
+load_module(void)
+{
+ char name1[64], name2[64];
+
+ snprintf(name1, sizeof(name1), "g_%s", class_name);
+ snprintf(name2, sizeof(name2), "geom_%s", class_name);
+ if (modfind(name1) < 0) {
+ /* Not present in kernel, try loading it. */
+ if (kldload(name2) < 0 || modfind(name1) < 0) {
+ if (errno != EEXIST) {
+ errx(EXIT_FAILURE,
+ "%s module not available!", name2);
+ }
+ }
+ }
+}
+
+static int
+strlcatf(char *str, size_t size, const char *format, ...)
+{
+ size_t len;
+ va_list ap;
+ int ret;
+
+ len = strlen(str);
+ str += len;
+ size -= len;
+
+ va_start(ap, format);
+ ret = vsnprintf(str, size, format, ap);
+ va_end(ap);
+
+ return (ret);
+}
+
+/*
+ * Find given option in options available for given command.
+ */
+static struct g_option *
+find_option(struct g_command *cmd, char ch)
+{
+ struct g_option *opt;
+ unsigned i;
+
+ for (i = 0; ; i++) {
+ opt = &cmd->gc_options[i];
+ if (opt->go_name == NULL)
+ return (NULL);
+ if (opt->go_char == ch)
+ return (opt);
+ }
+ /* NOTREACHED */
+ return (NULL);
+}
+
+/*
+ * Add given option to gctl_req.
+ */
+static void
+set_option(struct gctl_req *req, struct g_option *opt, const char *val)
+{
+
+ if (opt->go_type == G_TYPE_NUMBER) {
+ intmax_t number;
+
+ errno = 0;
+ number = strtoimax(optarg, NULL, 0);
+ if (errno != 0) {
+ err(EXIT_FAILURE, "Invalid value for '%c' argument.",
+ opt->go_char);
+ }
+ opt->go_val = malloc(sizeof(intmax_t));
+ if (opt->go_val == NULL)
+ errx(EXIT_FAILURE, "No memory.");
+ *(intmax_t *)opt->go_val = number;
+
+ gctl_ro_param(req, opt->go_name, sizeof(intmax_t), opt->go_val);
+ } else if (opt->go_type == G_TYPE_STRING) {
+ gctl_ro_param(req, opt->go_name, -1, optarg);
+ } else /* if (opt->go_type == G_TYPE_NONE) */ {
+ opt->go_val = malloc(sizeof(int));
+ if (opt->go_val == NULL)
+ errx(EXIT_FAILURE, "No memory.");
+ *(int *)opt->go_val = *val - '0';
+
+ gctl_ro_param(req, opt->go_name, sizeof(int),
+ opt->go_val);
+ }
+}
+
+/*
+ * 1. Add given argument by caller.
+ * 2. Add default values of not given arguments.
+ * 3. Add the rest of arguments.
+ */
+static void
+parse_arguments(struct g_command *cmd, struct gctl_req *req, int *argc,
+ char ***argv)
+{
+ struct g_option *opt;
+ char opts[64];
+ unsigned i;
+ int ch;
+
+ *opts = '\0';
+ if ((cmd->gc_flags & G_FLAG_VERBOSE) != 0)
+ strlcat(opts, "v", sizeof(opts));
+ for (i = 0; ; i++) {
+ opt = &cmd->gc_options[i];
+ if (opt->go_name == NULL)
+ break;
+ strlcatf(opts, sizeof(opts), "%c", opt->go_char);
+ if (opt->go_type != G_TYPE_NONE)
+ strlcat(opts, ":", sizeof(opts));
+ }
+
+ /*
+ * Add specified arguments.
+ */
+ while ((ch = getopt(*argc, *argv, opts)) != -1) {
+ /* Standard (not passed to kernel) options. */
+ switch (ch) {
+ case 'v':
+ verbose = 1;
+ continue;
+ }
+ /* Options passed to kernel. */
+ opt = find_option(cmd, ch);
+ if (opt == NULL)
+ usage();
+ if (G_OPT_ISDONE(opt)) {
+ fprintf(stderr, "Flag '%c' specified twice.\n",
+ opt->go_char);
+ usage();
+ }
+ G_OPT_DONE(opt);
+
+ if (opt->go_type == G_TYPE_NONE)
+ set_option(req, opt, "1");
+ else
+ set_option(req, opt, optarg);
+ }
+ *argc -= optind;
+ *argv += optind;
+
+ /*
+ * Add not specified arguments, but with default values.
+ */
+ for (i = 0; ; i++) {
+ opt = &cmd->gc_options[i];
+ if (opt->go_name == NULL)
+ break;
+ if (G_OPT_ISDONE(opt))
+ continue;
+
+ if (opt->go_type == G_TYPE_NONE) {
+ assert(opt->go_val == NULL);
+ set_option(req, opt, "0");
+ } else {
+ if (opt->go_val == NULL) {
+ fprintf(stderr, "Flag '%c' not specified.\n",
+ opt->go_char);
+ usage();
+ } else {
+ if (opt->go_type == G_TYPE_NUMBER) {
+ gctl_ro_param(req, opt->go_name,
+ sizeof(intmax_t), opt->go_val);
+ } else /* if (opt->go_type == G_TYPE_STRING)*/ {
+ gctl_ro_param(req, opt->go_name, -1,
+ opt->go_val);
+ }
+ }
+ }
+ }
+ /*
+ * Add rest of given arguments.
+ */
+ gctl_ro_param(req, "nargs", sizeof(int), argc);
+ for (i = 0; i < (unsigned)*argc; i++) {
+ char argname[16];
+
+ snprintf(argname, sizeof(argname), "arg%u", i);
+ gctl_ro_param(req, argname, -1, (*argv)[i]);
+ }
+}
+
+/*
+ * Find given command in commands available for given class.
+ */
+static struct g_command *
+find_command(const char *cmdstr, int flags)
+{
+ struct g_command *cmd;
+ unsigned i;
+
+ /*
+ * First try to find command defined by loaded library.
+ */
+ if ((flags & GEOM_CLASS_CMDS) != 0 && class_commands != NULL) {
+ for (i = 0; ; i++) {
+ cmd = &class_commands[i];
+ if (cmd->gc_name == NULL)
+ break;
+ if (strcmp(cmd->gc_name, cmdstr) == 0)
+ return (cmd);
+ }
+ }
+ /*
+ * Now try to find in standard commands.
+ */
+ if ((flags & GEOM_STD_CMDS) != 0) {
+ for (i = 0; ; i++) {
+ cmd = &std_commands[i];
+ if (cmd->gc_name == NULL)
+ break;
+ if (strcmp(cmd->gc_name, cmdstr) == 0)
+ return (cmd);
+ }
+ }
+ return (NULL);
+}
+
+static unsigned
+set_flags(struct g_command *cmd)
+{
+ unsigned flags = 0;
+
+ if ((cmd->gc_flags & G_FLAG_VERBOSE) != 0 && verbose)
+ flags |= G_FLAG_VERBOSE;
+
+ return (flags);
+}
+
+/*
+ * Run command.
+ */
+static void
+run_command(int argc, char *argv[])
+{
+ struct g_command *cmd;
+ struct gctl_req *req;
+ const char *errstr;
+ char buf[4096];
+
+ /* First try to find a command defined by a class. */
+ cmd = find_command(argv[0], GEOM_CLASS_CMDS);
+ if (cmd == NULL) {
+ /* Now, try to find a standard command. */
+ cmd = find_command(argv[0], GEOM_STD_CMDS);
+ if (cmd == NULL) {
+ fprintf(stderr, "Unknown command: %s\n", argv[0]);
+ usage();
+ }
+ if (!std_available(cmd->gc_name)) {
+ fprintf(stderr, "Command '%s' not available.\n",
+ argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ }
+ if ((cmd->gc_flags & G_FLAG_LOADKLD) != 0)
+ load_module();
+
+ req = gctl_get_handle();
+ gctl_ro_param(req, "class", -1, gclass_name);
+ gctl_ro_param(req, "verb", -1, argv[0]);
+ if (version != NULL)
+ gctl_ro_param(req, "version", sizeof(*version), version);
+ parse_arguments(cmd, req, &argc, &argv);
+
+ bzero(buf, sizeof(buf));
+ if (cmd->gc_func != NULL) {
+ unsigned flags;
+
+ flags = set_flags(cmd);
+ cmd->gc_func(req, flags);
+ errstr = req->error;
+ } else {
+ gctl_rw_param(req, "output", sizeof(buf), buf);
+ errstr = gctl_issue(req);
+ }
+ if (errstr != NULL && errstr[0] != '\0') {
+ fprintf(stderr, "%s\n", errstr);
+ if (strncmp(errstr, "warning: ", strlen("warning: ")) != 0) {
+ gctl_free(req);
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (buf[0] != '\0')
+ printf("%s", buf);
+ gctl_free(req);
+ if (verbose)
+ printf("Done.\n");
+ exit(EXIT_SUCCESS);
+}
+
+static const char *
+library_path(void)
+{
+ const char *path;
+
+ path = getenv("GEOM_LIBRARY_PATH");
+ if (path == NULL)
+ path = CLASS_DIR;
+ return (path);
+}
+
+static void
+load_library(void)
+{
+ char path[MAXPATHLEN];
+ uint32_t *lib_version;
+ void *dlh;
+
+ snprintf(path, sizeof(path), "%s/geom_%s.so", library_path(),
+ class_name);
+ if (access(path, F_OK) == -1) {
+ if (errno == ENOENT) {
+ /*
+ * If we cannot find library, that's ok, standard
+ * commands can still be used.
+ */
+ return;
+ }
+ err(EXIT_FAILURE, "Cannot access library");
+ }
+ dlh = dlopen(path, RTLD_NOW);
+ if (dlh == NULL)
+ errx(EXIT_FAILURE, "Cannot open library: %s.", dlerror());
+ lib_version = dlsym(dlh, "lib_version");
+ if (lib_version == NULL) {
+ fprintf(stderr, "Cannot find symbol %s: %s.\n", "lib_version",
+ dlerror());
+ dlclose(dlh);
+ exit(EXIT_FAILURE);
+ }
+ if (*lib_version != G_LIB_VERSION) {
+ dlclose(dlh);
+ errx(EXIT_FAILURE, "%s and %s are not synchronized.",
+ getprogname(), path);
+ }
+ version = dlsym(dlh, "version");
+ if (version == NULL) {
+ fprintf(stderr, "Cannot find symbol %s: %s.\n", "version",
+ dlerror());
+ dlclose(dlh);
+ exit(EXIT_FAILURE);
+ }
+ class_commands = dlsym(dlh, "class_commands");
+ if (class_commands == NULL) {
+ fprintf(stderr, "Cannot find symbol %s: %s.\n",
+ "class_commands", dlerror());
+ dlclose(dlh);
+ exit(EXIT_FAILURE);
+ }
+}
+
+/*
+ * Class name should be all capital letters.
+ */
+static void
+set_class_name(void)
+{
+ char *s1, *s2;
+
+ s1 = class_name;
+ for (; *s1 != '\0'; s1++)
+ *s1 = tolower(*s1);
+ gclass_name = malloc(strlen(class_name));
+ if (gclass_name == NULL)
+ errx(EXIT_FAILURE, "No memory");
+ s1 = gclass_name;
+ s2 = class_name;
+ for (; *s2 != '\0'; s2++)
+ *s1++ = toupper(*s2);
+ *s1 = '\0';
+}
+
+static void
+get_class(int *argc, char ***argv)
+{
+
+ snprintf(comm, sizeof(comm), "%s", basename((*argv)[0]));
+ if (strcmp(comm, "geom") == 0) {
+ if (*argc < 2)
+ usage();
+ else if (*argc == 2) {
+ if (strcmp((*argv)[1], "-h") == 0 ||
+ strcmp((*argv)[1], "help") == 0) {
+ usage();
+ }
+ }
+ strlcatf(comm, sizeof(comm), " %s", (*argv)[1]);
+ class_name = (*argv)[1];
+ *argc -= 2;
+ *argv += 2;
+ } else if (*comm == 'g') {
+ class_name = comm + 1;
+ *argc -= 1;
+ *argv += 1;
+ } else {
+ errx(EXIT_FAILURE, "Invalid utility name.");
+ }
+ set_class_name();
+ load_library();
+ if (*argc < 1)
+ usage();
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ get_class(&argc, &argv);
+ run_command(argc, argv);
+ /* NOTREACHED */
+
+ exit(EXIT_FAILURE);
+}
+
+static struct gclass *
+find_class(struct gmesh *mesh, const char *name)
+{
+ struct gclass *classp;
+
+ LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
+ if (strcmp(classp->lg_name, name) == 0)
+ return (classp);
+ }
+ return (NULL);
+}
+
+static struct ggeom *
+find_geom(struct gclass *classp, const char *name)
+{
+ struct ggeom *gp;
+
+ LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
+ if (strcmp(gp->lg_name, name) == 0)
+ return (gp);
+ }
+ return (NULL);
+}
+
+static void
+list_one_provider(struct gprovider *pp, const char *prefix)
+{
+ struct gconfig *conf;
+ char buf[5];
+
+ printf("Name: %s\n", pp->lg_name);
+ humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "",
+ HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+ printf("%sMediasize: %jd (%s)\n", prefix, (intmax_t)pp->lg_mediasize,
+ buf);
+ printf("%sSectorsize: %u\n", prefix, pp->lg_sectorsize);
+ printf("%sMode: %s\n", prefix, pp->lg_mode);
+ LIST_FOREACH(conf, &pp->lg_config, lg_config) {
+ printf("%s%s: %s\n", prefix, conf->lg_name, conf->lg_val);
+ }
+}
+
+static void
+list_one_consumer(struct gconsumer *cp, const char *prefix)
+{
+ struct gprovider *pp;
+ struct gconfig *conf;
+
+ pp = cp->lg_provider;
+ if (pp == NULL)
+ printf("[no provider]\n");
+ else {
+ char buf[5];
+
+ printf("Name: %s\n", pp->lg_name);
+ humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "",
+ HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+ printf("%sMediasize: %jd (%s)\n", prefix,
+ (intmax_t)pp->lg_mediasize, buf);
+ printf("%sSectorsize: %u\n", prefix, pp->lg_sectorsize);
+ printf("%sMode: %s\n", prefix, cp->lg_mode);
+ }
+ LIST_FOREACH(conf, &cp->lg_config, lg_config) {
+ printf("%s%s: %s\n", prefix, conf->lg_name, conf->lg_val);
+ }
+}
+
+static void
+list_one_geom(struct ggeom *gp)
+{
+ struct gprovider *pp;
+ struct gconsumer *cp;
+ struct gconfig *conf;
+ unsigned n;
+
+ printf("Geom name: %s\n", gp->lg_name);
+ LIST_FOREACH(conf, &gp->lg_config, lg_config) {
+ printf("%s: %s\n", conf->lg_name, conf->lg_val);
+ }
+ if (!LIST_EMPTY(&gp->lg_provider)) {
+ printf("Providers:\n");
+ n = 1;
+ LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
+ printf("%u. ", n++);
+ list_one_provider(pp, " ");
+ }
+ }
+ if (!LIST_EMPTY(&gp->lg_consumer)) {
+ printf("Consumers:\n");
+ n = 1;
+ LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
+ printf("%u. ", n++);
+ list_one_consumer(cp, " ");
+ }
+ }
+ printf("\n");
+}
+
+static void
+std_help(struct gctl_req *req __unused, unsigned flags __unused)
+{
+
+ usage();
+}
+
+static int
+std_list_available(void)
+{
+ struct gmesh mesh;
+ struct gclass *classp;
+ int error;
+
+ error = geom_gettree(&mesh);
+ if (error != 0) {
+ fprintf(stderr, "Cannot get GEOM tree: %s.\n", strerror(error));
+ exit(EXIT_FAILURE);
+ }
+ classp = find_class(&mesh, gclass_name);
+ geom_deletetree(&mesh);
+ if (classp != NULL)
+ return (1);
+ return (0);
+}
+
+static void
+std_list(struct gctl_req *req, unsigned flags __unused)
+{
+ struct gmesh mesh;
+ struct gclass *classp;
+ struct ggeom *gp;
+ const char *name;
+ int error, i, nargs;
+
+ error = geom_gettree(&mesh);
+ if (error != 0) {
+ fprintf(stderr, "Cannot get GEOM tree: %s.\n", strerror(error));
+ exit(EXIT_FAILURE);
+ }
+ classp = find_class(&mesh, gclass_name);
+ if (classp == NULL) {
+ geom_deletetree(&mesh);
+ fprintf(stderr, "Class %s not found.\n", gclass_name);
+ return;
+ }
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs > 0) {
+ for (i = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ gp = find_geom(classp, name);
+ if (gp != NULL)
+ list_one_geom(gp);
+ else
+ fprintf(stderr, "No such geom: %s.\n", name);
+ }
+ } else {
+ LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
+ if (LIST_EMPTY(&gp->lg_provider))
+ continue;
+ list_one_geom(gp);
+ }
+ }
+ geom_deletetree(&mesh);
+}
+
+static int
+std_status_available(void)
+{
+
+ /* 'status' command is available when 'list' command is. */
+ return (std_list_available());
+}
+
+static void
+status_update_len(struct ggeom *gp, int *name_len, int *status_len)
+{
+ struct gprovider *pp;
+ struct gconfig *conf;
+ int len;
+
+ assert(gp != NULL);
+ assert(name_len != NULL);
+ assert(status_len != NULL);
+
+ pp = LIST_FIRST(&gp->lg_provider);
+ if (pp != NULL)
+ len = strlen(pp->lg_name);
+ else
+ len = strlen(gp->lg_name);
+ if (*name_len < len)
+ *name_len = len;
+ LIST_FOREACH(conf, &gp->lg_config, lg_config) {
+ if (strcasecmp(conf->lg_name, "state") == 0) {
+ len = strlen(conf->lg_val);
+ if (*status_len < len)
+ *status_len = len;
+ }
+ }
+}
+
+static char *
+status_one_consumer(struct gconsumer *cp)
+{
+ static char buf[256];
+ struct gprovider *pp;
+ struct gconfig *conf;
+
+ pp = cp->lg_provider;
+ if (pp == NULL)
+ return (NULL);
+ LIST_FOREACH(conf, &cp->lg_config, lg_config) {
+ if (strcasecmp(conf->lg_name, "synchronized") == 0)
+ break;
+ }
+ if (conf == NULL)
+ snprintf(buf, sizeof(buf), "%s", pp->lg_name);
+ else {
+ snprintf(buf, sizeof(buf), "%s (%s)", pp->lg_name,
+ conf->lg_val);
+ }
+ return (buf);
+}
+
+static void
+status_one_geom(struct ggeom *gp, int script, int name_len, int status_len)
+{
+ struct gprovider *pp;
+ struct gconsumer *cp;
+ struct gconfig *conf;
+ const char *name, *status, *component;
+ int gotone;
+
+ pp = LIST_FIRST(&gp->lg_provider);
+ if (pp != NULL)
+ name = pp->lg_name;
+ else
+ name = gp->lg_name;
+ LIST_FOREACH(conf, &gp->lg_config, lg_config) {
+ if (strcasecmp(conf->lg_name, "state") == 0)
+ break;
+ }
+ if (conf == NULL)
+ status = "N/A";
+ else
+ status = conf->lg_val;
+ gotone = 0;
+ LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
+ component = status_one_consumer(cp);
+ if (component == NULL)
+ continue;
+ gotone = 1;
+ printf("%*s %*s %s\n", name_len, name, status_len, status,
+ component);
+ if (!script)
+ name = status = "";
+ }
+ if (!gotone) {
+ printf("%*s %*s %s\n", name_len, name, status_len, status,
+ "N/A");
+ }
+}
+
+static void
+std_status(struct gctl_req *req, unsigned flags __unused)
+{
+ struct gmesh mesh;
+ struct gclass *classp;
+ struct ggeom *gp;
+ const char *name;
+ int name_len, status_len;
+ int error, i, n, nargs, script;
+
+ error = geom_gettree(&mesh);
+ if (error != 0) {
+ fprintf(stderr, "Cannot get GEOM tree: %s.\n", strerror(error));
+ exit(EXIT_FAILURE);
+ }
+ classp = find_class(&mesh, gclass_name);
+ if (classp == NULL) {
+ fprintf(stderr, "Class %s not found.\n", gclass_name);
+ goto end;
+ }
+ nargs = gctl_get_int(req, "nargs");
+ script = gctl_get_int(req, "script");
+ name_len = strlen("Name");
+ status_len = strlen("Status");
+ if (nargs > 0) {
+ for (i = 0, n = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ gp = find_geom(classp, name);
+ if (gp == NULL)
+ fprintf(stderr, "No such geom: %s.\n", name);
+ else {
+ status_update_len(gp, &name_len, &status_len);
+ n++;
+ }
+ }
+ if (n == 0)
+ goto end;
+ } else {
+ n = 0;
+ LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
+ if (LIST_EMPTY(&gp->lg_provider))
+ continue;
+ status_update_len(gp, &name_len, &status_len);
+ n++;
+ }
+ if (n == 0)
+ goto end;
+ }
+ if (!script) {
+ printf("%*s %*s %s\n", name_len, "Name", status_len, "Status",
+ "Components");
+ }
+ if (nargs > 0) {
+ for (i = 0; i < nargs; i++) {
+ name = gctl_get_ascii(req, "arg%d", i);
+ gp = find_geom(classp, name);
+ if (gp != NULL) {
+ status_one_geom(gp, script, name_len,
+ status_len);
+ }
+ }
+ } else {
+ LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
+ if (LIST_EMPTY(&gp->lg_provider))
+ continue;
+ status_one_geom(gp, script, name_len, status_len);
+ }
+ }
+end:
+ geom_deletetree(&mesh);
+}
+
+static int
+std_load_available(void)
+{
+ char name[MAXPATHLEN], paths[MAXPATHLEN * 8], *p;
+ struct stat sb;
+ size_t len;
+
+ snprintf(name, sizeof(name), "g_%s", class_name);
+ /*
+ * If already in kernel, "load" command is not available.
+ */
+ if (modfind(name) >= 0)
+ return (0);
+ bzero(paths, sizeof(paths));
+ len = sizeof(paths);
+ if (sysctlbyname("kern.module_path", paths, &len, NULL, 0) < 0)
+ err(EXIT_FAILURE, "sysctl(kern.module_path)");
+ for (p = strtok(paths, ";"); p != NULL; p = strtok(NULL, ";")) {
+ snprintf(name, sizeof(name), "%s/geom_%s.ko", p, class_name);
+ /*
+ * If geom_<name>.ko file exists, "load" command is available.
+ */
+ if (stat(name, &sb) == 0)
+ return (1);
+ }
+ return (0);
+}
+
+static void
+std_load(struct gctl_req *req __unused, unsigned flags)
+{
+
+ /*
+ * Do nothing special here, because of G_FLAG_LOADKLD flag,
+ * module is already loaded.
+ */
+ if ((flags & G_FLAG_VERBOSE) != 0)
+ printf("Module available.\n");
+}
+
+static int
+std_unload_available(void)
+{
+ char name[64];
+ int id;
+
+ snprintf(name, sizeof(name), "geom_%s", class_name);
+ id = kldfind(name);
+ if (id >= 0)
+ return (1);
+ return (0);
+}
+
+static void
+std_unload(struct gctl_req *req, unsigned flags __unused)
+{
+ char name[64];
+ int id;
+
+ snprintf(name, sizeof(name), "geom_%s", class_name);
+ id = kldfind(name);
+ if (id < 0) {
+ gctl_error(req, "Could not find module: %s.", strerror(errno));
+ return;
+ }
+ if (kldunload(id) < 0) {
+ gctl_error(req, "Could not unload module: %s.",
+ strerror(errno));
+ return;
+ }
+}
+
+static int
+std_available(const char *name)
+{
+
+ if (strcmp(name, "help") == 0)
+ return (1);
+ else if (strcmp(name, "list") == 0)
+ return (std_list_available());
+ else if (strcmp(name, "status") == 0)
+ return (std_status_available());
+ else if (strcmp(name, "load") == 0)
+ return (std_load_available());
+ else if (strcmp(name, "unload") == 0)
+ return (std_unload_available());
+ else
+ assert(!"Unknown standard command.");
+ return (0);
+}
diff --git a/sbin/geom/core/geom.h b/sbin/geom/core/geom.h
new file mode 100644
index 0000000..e4d94be
--- /dev/null
+++ b/sbin/geom/core/geom.h
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 2004 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _GEOM_H_
+#define _GEOM_H_
+#define G_LIB_VERSION 1
+
+#define G_FLAG_NONE 0x0000
+#define G_FLAG_VERBOSE 0x0001
+#define G_FLAG_LOADKLD 0x0002
+
+#define G_TYPE_NONE 0
+#define G_TYPE_STRING 1
+#define G_TYPE_NUMBER 2
+
+#define G_OPT_MAX 16
+#define G_OPT_DONE(opt) (opt)->go_char = '\0'
+#define G_OPT_ISDONE(opt) ((opt)->go_char == '\0')
+
+#define G_OPT_SENTINEL { '\0', NULL, NULL, G_TYPE_NONE }
+#define G_NULL_OPTS { G_OPT_SENTINEL }
+#define G_CMD_SENTINEL { NULL, 0, NULL, G_NULL_OPTS, NULL }
+
+struct g_option {
+ char go_char;
+ const char *go_name;
+ void *go_val;
+ unsigned go_type;
+};
+
+struct g_command {
+ const char *gc_name;
+ unsigned gc_flags;
+ void (*gc_func)(struct gctl_req *, unsigned);
+ struct g_option gc_options[G_OPT_MAX];
+ const char *gc_usage;
+};
+#endif /* !_GEOM_H_ */
diff --git a/sbin/geom/misc/subr.c b/sbin/geom/misc/subr.c
new file mode 100644
index 0000000..f47523e
--- /dev/null
+++ b/sbin/geom/misc/subr.c
@@ -0,0 +1,390 @@
+/*-
+ * Copyright (c) 2004 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/disk.h>
+#include <sys/endian.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <assert.h>
+#include <libgeom.h>
+
+#include "misc/subr.h"
+
+
+struct std_metadata {
+ char md_magic[16];
+ uint32_t md_version;
+};
+
+static void
+std_metadata_decode(const u_char *data, struct std_metadata *md)
+{
+
+ bcopy(data, md->md_magic, sizeof(md->md_magic));
+ md->md_version = le32dec(data + 16);
+}
+
+static void
+pathgen(const char *name, char *path, size_t size)
+{
+
+ if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) != 0)
+ snprintf(path, size, "%s%s", _PATH_DEV, name);
+ else
+ strlcpy(path, name, size);
+}
+
+/*
+ * Greatest Common Divisor.
+ */
+static unsigned
+gcd(unsigned a, unsigned b)
+{
+ u_int c;
+
+ while (b != 0) {
+ c = a;
+ a = b;
+ b = (c % b);
+ }
+ return (a);
+}
+
+/*
+ * Least Common Multiple.
+ */
+unsigned
+g_lcm(unsigned a, unsigned b)
+{
+
+ return ((a * b) / gcd(a, b));
+}
+
+uint32_t
+bitcount32(uint32_t x)
+{
+
+ x = (x & 0x55555555) + ((x & 0xaaaaaaaa) >> 1);
+ x = (x & 0x33333333) + ((x & 0xcccccccc) >> 2);
+ x = (x & 0x0f0f0f0f) + ((x & 0xf0f0f0f0) >> 4);
+ x = (x & 0x00ff00ff) + ((x & 0xff00ff00) >> 8);
+ x = (x & 0x0000ffff) + ((x & 0xffff0000) >> 16);
+ return (x);
+}
+
+off_t
+g_get_mediasize(const char *name)
+{
+ char path[MAXPATHLEN];
+ off_t mediasize;
+ int fd;
+
+ pathgen(name, path, sizeof(path));
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return (0);
+ if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) {
+ close(fd);
+ return (0);
+ }
+ close(fd);
+ return (mediasize);
+}
+
+unsigned
+g_get_sectorsize(const char *name)
+{
+ char path[MAXPATHLEN];
+ unsigned sectorsize;
+ int fd;
+
+ pathgen(name, path, sizeof(path));
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return (0);
+ if (ioctl(fd, DIOCGSECTORSIZE, &sectorsize) < 0) {
+ close(fd);
+ return (0);
+ }
+ close(fd);
+ return (sectorsize);
+}
+
+int
+g_metadata_read(const char *name, u_char *md, size_t size, const char *magic)
+{
+ struct std_metadata stdmd;
+ char path[MAXPATHLEN];
+ unsigned sectorsize;
+ off_t mediasize;
+ u_char *sector;
+ int error, fd;
+
+ pathgen(name, path, sizeof(path));
+ sector = NULL;
+ error = 0;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return (errno);
+ mediasize = g_get_mediasize(name);
+ if (mediasize == 0) {
+ error = errno;
+ goto out;
+ }
+ sectorsize = g_get_sectorsize(name);
+ if (sectorsize == 0) {
+ error = errno;
+ goto out;
+ }
+ assert(sectorsize >= size);
+ sector = malloc(sectorsize);
+ if (sector == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ if (pread(fd, sector, sectorsize, mediasize - sectorsize) !=
+ (ssize_t)sectorsize) {
+ error = errno;
+ goto out;
+ }
+ if (magic != NULL) {
+ std_metadata_decode(sector, &stdmd);
+ if (strcmp(stdmd.md_magic, magic) != 0) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ bcopy(sector, md, size);
+out:
+ if (sector != NULL)
+ free(sector);
+ close(fd);
+ return (error);
+}
+
+int
+g_metadata_store(const char *name, u_char *md, size_t size)
+{
+ char path[MAXPATHLEN];
+ unsigned sectorsize;
+ off_t mediasize;
+ u_char *sector;
+ int error, fd;
+
+ pathgen(name, path, sizeof(path));
+ sector = NULL;
+ error = 0;
+
+ fd = open(path, O_WRONLY);
+ if (fd == -1)
+ return (errno);
+ mediasize = g_get_mediasize(name);
+ if (mediasize == 0) {
+ error = errno;
+ goto out;
+ }
+ sectorsize = g_get_sectorsize(name);
+ if (sectorsize == 0) {
+ error = errno;
+ goto out;
+ }
+ assert(sectorsize >= size);
+ sector = malloc(sectorsize);
+ if (sector == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ bcopy(md, sector, size);
+ if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) !=
+ (ssize_t)sectorsize) {
+ error = errno;
+ goto out;
+ }
+out:
+ if (sector != NULL)
+ free(sector);
+ close(fd);
+ return (error);
+}
+
+int
+g_metadata_clear(const char *name, const char *magic)
+{
+ struct std_metadata md;
+ char path[MAXPATHLEN];
+ unsigned sectorsize;
+ off_t mediasize;
+ u_char *sector;
+ int error, fd;
+
+ pathgen(name, path, sizeof(path));
+ sector = NULL;
+ error = 0;
+
+ fd = open(path, O_RDWR);
+ if (fd == -1)
+ return (errno);
+ mediasize = g_get_mediasize(name);
+ if (mediasize == 0) {
+ error = errno;
+ goto out;
+ }
+ sectorsize = g_get_sectorsize(name);
+ if (sectorsize == 0) {
+ error = errno;
+ goto out;
+ }
+ sector = malloc(sectorsize);
+ if (sector == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ if (magic != NULL) {
+ if (pread(fd, sector, sectorsize, mediasize - sectorsize) !=
+ (ssize_t)sectorsize) {
+ error = errno;
+ goto out;
+ }
+ std_metadata_decode(sector, &md);
+ if (strcmp(md.md_magic, magic) != 0) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ bzero(sector, sectorsize);
+ if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) !=
+ (ssize_t)sectorsize) {
+ error = errno;
+ goto out;
+ }
+out:
+ if (sector != NULL)
+ free(sector);
+ close(fd);
+ return (error);
+}
+
+/*
+ * Set an error message, if one does not already exist.
+ */
+void
+gctl_error(struct gctl_req *req, const char *error, ...)
+{
+ va_list ap;
+
+ if (req->error != NULL)
+ return;
+ va_start(ap, error);
+ vasprintf(&req->error, error, ap);
+ va_end(ap);
+}
+
+static void *
+gctl_get_param(struct gctl_req *req, size_t len, const char *pfmt, va_list ap)
+{
+ struct gctl_req_arg *argp;
+ char param[256];
+ void *p;
+ unsigned i;
+
+ vsnprintf(param, sizeof(param), pfmt, ap);
+ for (i = 0; i < req->narg; i++) {
+ argp = &req->arg[i];
+ if (strcmp(param, argp->name))
+ continue;
+ if (!(argp->flag & GCTL_PARAM_RD))
+ continue;
+ p = argp->value;
+ if (len == 0) {
+ /* We are looking for a string. */
+ if (argp->len < 1) {
+ fprintf(stderr, "No length argument (%s).\n",
+ param);
+ abort();
+ }
+ if (((char *)p)[argp->len - 1] != '\0') {
+ fprintf(stderr, "Unterminated argument (%s).\n",
+ param);
+ abort();
+ }
+ } else if ((int)len != argp->len) {
+ fprintf(stderr, "Wrong length %s argument.\n", param);
+ abort();
+ }
+ return (p);
+ }
+ fprintf(stderr, "No such argument (%s).\n", param);
+ abort();
+}
+
+int
+gctl_get_int(struct gctl_req *req, const char *pfmt, ...)
+{
+ int *p;
+ va_list ap;
+
+ va_start(ap, pfmt);
+ p = gctl_get_param(req, sizeof(int), pfmt, ap);
+ va_end(ap);
+ return (*p);
+}
+
+intmax_t
+gctl_get_intmax(struct gctl_req *req, const char *pfmt, ...)
+{
+ intmax_t *p;
+ va_list ap;
+
+ va_start(ap, pfmt);
+ p = gctl_get_param(req, sizeof(intmax_t), pfmt, ap);
+ va_end(ap);
+ return (*p);
+}
+
+const char *
+gctl_get_ascii(struct gctl_req *req, const char *pfmt, ...)
+{
+ const char *p;
+ va_list ap;
+
+ va_start(ap, pfmt);
+ p = gctl_get_param(req, 0, pfmt, ap);
+ va_end(ap);
+ return (p);
+}
diff --git a/sbin/geom/misc/subr.h b/sbin/geom/misc/subr.h
new file mode 100644
index 0000000..ad02969
--- /dev/null
+++ b/sbin/geom/misc/subr.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2004 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SUBR_H_
+#define _SUBR_H_
+#include <stdint.h>
+
+unsigned g_lcm(unsigned a, unsigned b);
+uint32_t bitcount32(uint32_t x);
+
+off_t g_get_mediasize(const char *name);
+unsigned g_get_sectorsize(const char *name);
+
+int g_metadata_read(const char *name, u_char *md, size_t size,
+ const char *magic);
+int g_metadata_store(const char *name, u_char *md, size_t size);
+int g_metadata_clear(const char *name, const char *magic);
+
+void gctl_error(struct gctl_req *req, const char *error, ...);
+int gctl_get_int(struct gctl_req *req, const char *pfmt, ...);
+intmax_t gctl_get_intmax(struct gctl_req *req, const char *pfmt, ...);
+const char *gctl_get_ascii(struct gctl_req *req, const char *pfmt, ...);
+#endif /* !_SUBR_H_ */
diff --git a/sbin/ggate/Makefile b/sbin/ggate/Makefile
new file mode 100644
index 0000000..97094d6
--- /dev/null
+++ b/sbin/ggate/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+SUBDIR= ${_ggatec} \
+ ${_ggated} \
+ ggatel
+
+.if !defined(NO_LIBPTHREAD)
+_ggatec= ggatec
+_ggated= ggated
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/sbin/ggate/Makefile.inc b/sbin/ggate/Makefile.inc
new file mode 100644
index 0000000..e55271f
--- /dev/null
+++ b/sbin/ggate/Makefile.inc
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+WARNS?= 6
+
+.include "../Makefile.inc"
diff --git a/sbin/ggate/ggatec/Makefile b/sbin/ggate/ggatec/Makefile
new file mode 100644
index 0000000..c49cfe8
--- /dev/null
+++ b/sbin/ggate/ggatec/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../shared
+
+PROG= ggatec
+MAN= ggatec.8
+SRCS= ggatec.c ggate.c
+
+CFLAGS+= -DLIBGEOM
+CFLAGS+= -I${.CURDIR}/../shared
+
+DPADD= ${LIBGEOM} ${LIBSBUF} ${LIBBSDXML} ${LIBUTIL} ${LIBPTHREAD}
+LDADD= -lgeom -lsbuf -lbsdxml -lutil -lpthread
+
+.include <bsd.prog.mk>
diff --git a/sbin/ggate/ggatec/ggatec.8 b/sbin/ggate/ggatec/ggatec.8
new file mode 100644
index 0000000..a88e0cb
--- /dev/null
+++ b/sbin/ggate/ggatec/ggatec.8
@@ -0,0 +1,181 @@
+.\" Copyright (c) 2004 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 26, 2004
+.Dt GGATEC 8
+.Os
+.Sh NAME
+.Nm ggatec
+.Nd "GEOM Gate network client and control utility"
+.Sh SYNOPSIS
+.Nm
+.Cm create
+.Op Fl n
+.Op Fl v
+.Op Fl o Cm ro | wo | rw
+.Op Fl p Ar port
+.Op Fl q Ar queue_size
+.Op Fl R Ar rcvbuf
+.Op Fl S Ar sndbuf
+.Op Fl s Ar sectorsize
+.Op Fl t Ar timeout
+.Op Fl u Ar unit
+.Ar host
+.Ar path
+.Nm
+.Cm rescue
+.Op Fl n
+.Op Fl v
+.Op Fl o Cm ro | wo | rw
+.Op Fl p Ar port
+.Op Fl R Ar rcvbuf
+.Op Fl S Ar sndbuf
+.Fl u Ar unit
+.Ar host
+.Ar path
+.Nm
+.Cm destroy
+.Op Fl f
+.Fl u Ar unit
+.Nm
+.Cm list
+.Op Fl v
+.Op Fl u Ar unit
+.Sh DESCRIPTION
+The
+.Nm
+utility is a network client for GEOM Gate class.
+It is responsible for creation of
+.Nm ggate
+devices and forwarding I/O requests between
+.Nm geom_gate.ko
+kernel module and
+.Xr ggated 8
+network daemon.
+Available commands:
+.Bl -tag -width ".Cm destroy"
+.It Cm create
+Connect to given
+.Xr ggated 8
+daemon and create a
+.Nm ggate
+provider related to the given remote file or device.
+.It Cm rescue
+If
+.Nm
+process died/has been killed, you can save situation with this
+command, which creates new connection to the
+.Xr ggated 8
+daemon and will handle pending and future requests.
+.It Cm destroy
+Destroy the given
+.Nm ggate
+provider.
+.It Cm list
+List
+.Nm ggate
+providers.
+.El
+.Pp
+Available options:
+.Bl -tag -width ".Fl s Cm ro | wo | rw"
+.It Fl f
+Forcibly destroy
+.Nm ggate
+provider (cancels all pending requests).
+.It Fl n
+Do not use
+.Dv TCP_NODELAY
+option on TCP sockets.
+.It Fl o Cm ro | wo | rw
+Specify permission to use when opening the file or device: read-only
+.Pq Cm ro ,
+write-only
+.Pq Cm wo ,
+or read-write
+.Pq Cm rw .
+Default is
+.Cm rw .
+.It Fl p Ar port
+Port to connect to on the remote host.
+Default is 3080.
+.It Fl q Ar queue_size
+Number of pending I/O requests that can be queued before they will
+start to be canceled.
+Default is 1024.
+.It Fl R Ar rcvbuf
+Size of receive buffer to use.
+Default is 131072 (128kB).
+.It Fl S Ar sndbuf
+Size of send buffer to use.
+Default is 131072 (128kB).
+.It Fl s Ar sectorsize
+Sector size for
+.Nm ggate
+provider.
+If not specified, it is taken from device, or set to 512 bytes for files.
+.It Fl t Ar timeout
+Number of seconds to wait before an I/O request will be canceled.
+0 means no timeout.
+Default is 0.
+.It Fl u Ar unit
+Unit number to use.
+.It Fl v
+Do not fork, run in foreground and print debug informations on standard
+output.
+.It Ar host
+Remote host to connect to.
+.It Ar path
+Path to a regular file or device.
+.El
+.Sh EXIT STATUS
+Exit status is 0 on success, or 1 if the command fails.
+To get details about the failure,
+.Nm
+should be called with the
+.Fl v
+option.
+.Sh EXAMPLES
+Made use of CD-ROM device from remote host.
+.Bd -literal -offset indent
+server# cat /etc/gg.exports
+client RO /dev/acd0
+server# ggated
+
+client# ggatec create -o ro server /dev/acd0
+ggate0
+client# mount_cd9660 /dev/ggate0 /cdrom
+.Ed
+.Sh SEE ALSO
+.Xr geom 4 ,
+.Xr ggated 8 ,
+.Xr ggatel 8 ,
+.Xr mount_cd9660 8
+.Sh AUTHORS
+The
+.Nm
+utility as well as this manual page was written by
+.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org .
diff --git a/sbin/ggate/ggatec/ggatec.c b/sbin/ggate/ggatec/ggatec.c
new file mode 100644
index 0000000..70f667d
--- /dev/null
+++ b/sbin/ggate/ggatec/ggatec.c
@@ -0,0 +1,640 @@
+/*-
+ * Copyright (c) 2004 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <signal.h>
+#include <err.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/syslog.h>
+#include <sys/time.h>
+#include <sys/bio.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <geom/gate/g_gate.h>
+#include "ggate.h"
+
+
+enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET;
+
+static const char *path = NULL;
+static const char *host = NULL;
+static int unit = -1;
+static unsigned flags = 0;
+static int force = 0;
+static unsigned queue_size = G_GATE_QUEUE_SIZE;
+static unsigned port = G_GATE_PORT;
+static off_t mediasize;
+static unsigned sectorsize = 0;
+static unsigned timeout = G_GATE_TIMEOUT;
+static int sendfd, recvfd;
+static uint32_t token;
+static pthread_t sendtd, recvtd;
+static int reconnect;
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: %s create [-nv] [-o <ro|wo|rw>] [-p port] "
+ "[-q queue_size] [-R rcvbuf] [-S sndbuf] [-s sectorsize] "
+ "[-t timeout] [-u unit] <host> <path>\n", getprogname());
+ fprintf(stderr, " %s rescue [-nv] [-o <ro|wo|rw>] [-p port] "
+ "[-R rcvbuf] [-S sndbuf] <-u unit> <host> <path>\n", getprogname());
+ fprintf(stderr, " %s destroy [-f] <-u unit>\n", getprogname());
+ fprintf(stderr, " %s list [-v] [-u unit]\n", getprogname());
+ exit(EXIT_FAILURE);
+}
+
+static void *
+send_thread(void *arg __unused)
+{
+ struct g_gate_ctl_io ggio;
+ struct g_gate_hdr hdr;
+ char buf[MAXPHYS];
+ ssize_t data;
+ int error;
+
+ g_gate_log(LOG_NOTICE, "%s: started!", __func__);
+
+ ggio.gctl_version = G_GATE_VERSION;
+ ggio.gctl_unit = unit;
+ ggio.gctl_data = buf;
+
+ for (;;) {
+ ggio.gctl_length = sizeof(buf);
+ ggio.gctl_error = 0;
+ g_gate_ioctl(G_GATE_CMD_START, &ggio);
+ error = ggio.gctl_error;
+ switch (error) {
+ case 0:
+ break;
+ case ECANCELED:
+ if (reconnect)
+ break;
+ /* Exit gracefully. */
+ g_gate_close_device();
+ exit(EXIT_SUCCESS);
+#if 0
+ case ENOMEM:
+ /* Buffer too small. */
+ ggio.gctl_data = realloc(ggio.gctl_data,
+ ggio.gctl_length);
+ if (ggio.gctl_data != NULL) {
+ bsize = ggio.gctl_length;
+ goto once_again;
+ }
+ /* FALLTHROUGH */
+#endif
+ case ENXIO:
+ default:
+ g_gate_xlog("ioctl(/dev/%s): %s.", G_GATE_CTL_NAME,
+ strerror(error));
+ }
+
+ if (reconnect)
+ break;
+
+ switch (ggio.gctl_cmd) {
+ case BIO_READ:
+ hdr.gh_cmd = GGATE_CMD_READ;
+ break;
+ case BIO_WRITE:
+ hdr.gh_cmd = GGATE_CMD_WRITE;
+ break;
+ }
+ hdr.gh_seq = ggio.gctl_seq;
+ hdr.gh_offset = ggio.gctl_offset;
+ hdr.gh_length = ggio.gctl_length;
+ hdr.gh_error = 0;
+ g_gate_swap2n_hdr(&hdr);
+
+ data = g_gate_send(sendfd, &hdr, sizeof(hdr), MSG_NOSIGNAL);
+ g_gate_log(LOG_DEBUG, "Sent hdr packet.");
+ g_gate_swap2h_hdr(&hdr);
+ if (reconnect)
+ break;
+ if (data != sizeof(hdr)) {
+ g_gate_log(LOG_ERR, "Lost connection 1.");
+ reconnect = 1;
+ pthread_kill(recvtd, SIGUSR1);
+ break;
+ }
+
+ if (hdr.gh_cmd == GGATE_CMD_WRITE) {
+ data = g_gate_send(sendfd, ggio.gctl_data,
+ ggio.gctl_length, MSG_NOSIGNAL);
+ if (reconnect)
+ break;
+ if (data != ggio.gctl_length) {
+ g_gate_log(LOG_ERR, "Lost connection 2 (%zd != %zd).", data, (ssize_t)ggio.gctl_length);
+ reconnect = 1;
+ pthread_kill(recvtd, SIGUSR1);
+ break;
+ }
+ g_gate_log(LOG_DEBUG, "Sent %zd bytes (offset=%llu, "
+ "size=%u).", data, hdr.gh_offset, hdr.gh_length);
+ }
+ }
+ g_gate_log(LOG_DEBUG, "%s: Died.", __func__);
+ return (NULL);
+}
+
+static void *
+recv_thread(void *arg __unused)
+{
+ struct g_gate_ctl_io ggio;
+ struct g_gate_hdr hdr;
+ char buf[MAXPHYS];
+ ssize_t data;
+
+ g_gate_log(LOG_NOTICE, "%s: started!", __func__);
+
+ ggio.gctl_version = G_GATE_VERSION;
+ ggio.gctl_unit = unit;
+ ggio.gctl_data = buf;
+
+ for (;;) {
+ data = g_gate_recv(recvfd, &hdr, sizeof(hdr), MSG_WAITALL);
+ if (reconnect)
+ break;
+ g_gate_swap2h_hdr(&hdr);
+ if (data != sizeof(hdr)) {
+ if (data == -1 && errno == EAGAIN)
+ continue;
+ g_gate_log(LOG_ERR, "Lost connection 3.");
+ reconnect = 1;
+ pthread_kill(sendtd, SIGUSR1);
+ break;
+ }
+ g_gate_log(LOG_DEBUG, "Received hdr packet.");
+
+ ggio.gctl_seq = hdr.gh_seq;
+ ggio.gctl_cmd = hdr.gh_cmd;
+ ggio.gctl_offset = hdr.gh_offset;
+ ggio.gctl_length = hdr.gh_length;
+ ggio.gctl_error = hdr.gh_error;
+
+ if (ggio.gctl_error == 0 && ggio.gctl_cmd == GGATE_CMD_READ) {
+ data = g_gate_recv(recvfd, ggio.gctl_data,
+ ggio.gctl_length, MSG_WAITALL);
+ if (reconnect)
+ break;
+ g_gate_log(LOG_DEBUG, "Received data packet.");
+ if (data != ggio.gctl_length) {
+ g_gate_log(LOG_ERR, "Lost connection 4.");
+ reconnect = 1;
+ pthread_kill(sendtd, SIGUSR1);
+ break;
+ }
+ g_gate_log(LOG_DEBUG, "Received %d bytes (offset=%ju, "
+ "size=%zu).", data, (uintmax_t)hdr.gh_offset,
+ (size_t)hdr.gh_length);
+ }
+
+ g_gate_ioctl(G_GATE_CMD_DONE, &ggio);
+ }
+ g_gate_log(LOG_DEBUG, "%s: Died.", __func__);
+ pthread_exit(NULL);
+}
+
+static int
+handshake(int dir)
+{
+ struct g_gate_version ver;
+ struct g_gate_cinit cinit;
+ struct g_gate_sinit sinit;
+ struct sockaddr_in serv;
+ int sfd;
+
+ /*
+ * Do the network stuff.
+ */
+ bzero(&serv, sizeof(serv));
+ serv.sin_family = AF_INET;
+ serv.sin_addr.s_addr = g_gate_str2ip(host);
+ if (serv.sin_addr.s_addr == INADDR_NONE) {
+ g_gate_log(LOG_DEBUG, "Invalid IP/host name: %s.", host);
+ return (-1);
+ }
+ serv.sin_port = htons(port);
+ sfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sfd == -1) {
+ g_gate_log(LOG_DEBUG, "Cannot open socket: %s.",
+ strerror(errno));
+ return (-1);
+ }
+
+ g_gate_socket_settings(sfd);
+
+ if (connect(sfd, (struct sockaddr *)&serv, sizeof(serv)) == -1) {
+ g_gate_log(LOG_DEBUG, "Cannot connect to server: %s.",
+ strerror(errno));
+ close(sfd);
+ return (-1);
+ }
+
+ g_gate_log(LOG_INFO, "Connected to the server: %s:%d.", host, port);
+
+ /*
+ * Create and send version packet.
+ */
+ g_gate_log(LOG_DEBUG, "Sending version packet.");
+ assert(strlen(GGATE_MAGIC) == sizeof(ver.gv_magic));
+ bcopy(GGATE_MAGIC, ver.gv_magic, sizeof(ver.gv_magic));
+ ver.gv_version = GGATE_VERSION;
+ ver.gv_error = 0;
+ g_gate_swap2n_version(&ver);
+ if (g_gate_send(sfd, &ver, sizeof(ver), MSG_NOSIGNAL) == -1) {
+ g_gate_log(LOG_DEBUG, "Error while sending version packet: %s.",
+ strerror(errno));
+ close(sfd);
+ return (-1);
+ }
+ bzero(&ver, sizeof(ver));
+ if (g_gate_recv(sfd, &ver, sizeof(ver), MSG_WAITALL) == -1) {
+ g_gate_log(LOG_DEBUG, "Error while receiving data: %s.",
+ strerror(errno));
+ close(sfd);
+ return (-1);
+ }
+ if (ver.gv_error != 0) {
+ g_gate_log(LOG_DEBUG, "Version verification problem: %s.",
+ strerror(errno));
+ close(sfd);
+ return (-1);
+ }
+
+ /*
+ * Create and send initial packet.
+ */
+ g_gate_log(LOG_DEBUG, "Sending initial packet.");
+ if (strlcpy(cinit.gc_path, path, sizeof(cinit.gc_path)) >=
+ sizeof(cinit.gc_path)) {
+ g_gate_log(LOG_DEBUG, "Path name too long.");
+ close(sfd);
+ return (-1);
+ }
+ cinit.gc_flags = flags | dir;
+ cinit.gc_token = token;
+ cinit.gc_nconn = 2;
+ g_gate_swap2n_cinit(&cinit);
+ if (g_gate_send(sfd, &cinit, sizeof(cinit), MSG_NOSIGNAL) == -1) {
+ g_gate_log(LOG_DEBUG, "Error while sending initial packet: %s.",
+ strerror(errno));
+ close(sfd);
+ return (-1);
+ }
+ g_gate_swap2h_cinit(&cinit);
+
+ /*
+ * Receiving initial packet from server.
+ */
+ g_gate_log(LOG_DEBUG, "Receiving initial packet.");
+ if (g_gate_recv(sfd, &sinit, sizeof(sinit), MSG_WAITALL) == -1) {
+ g_gate_log(LOG_DEBUG, "Error while receiving data: %s.",
+ strerror(errno));
+ close(sfd);
+ return (-1);
+ }
+ g_gate_swap2h_sinit(&sinit);
+ if (sinit.gs_error != 0) {
+ g_gate_log(LOG_DEBUG, "Error from server: %s.",
+ strerror(sinit.gs_error));
+ close(sfd);
+ return (-1);
+ }
+ g_gate_log(LOG_DEBUG, "Received initial packet.");
+
+ mediasize = sinit.gs_mediasize;
+ if (sectorsize == 0)
+ sectorsize = sinit.gs_sectorsize;
+
+ return (sfd);
+}
+
+static void
+mydaemon(void)
+{
+
+ if (g_gate_verbose > 0)
+ return;
+ if (daemon(0, 0) == 0)
+ return;
+ if (action == CREATE)
+ g_gate_destroy(unit, 1);
+ err(EXIT_FAILURE, "Cannot daemonize");
+}
+
+static int
+g_gatec_connect(void)
+{
+
+ token = arc4random();
+ /*
+ * Our receive descriptor is connected to the send descriptor on the
+ * server side.
+ */
+ recvfd = handshake(GGATE_FLAG_SEND);
+ if (recvfd == -1)
+ return (0);
+ /*
+ * Our send descriptor is connected to the receive descriptor on the
+ * server side.
+ */
+ sendfd = handshake(GGATE_FLAG_RECV);
+ if (sendfd == -1)
+ return (0);
+ return (1);
+}
+
+static void
+g_gatec_start(void)
+{
+ int error;
+
+ reconnect = 0;
+ error = pthread_create(&recvtd, NULL, recv_thread, NULL);
+ if (error != 0) {
+ g_gate_destroy(unit, 1);
+ g_gate_xlog("pthread_create(recv_thread): %s.",
+ strerror(error));
+ }
+ sendtd = pthread_self();
+ send_thread(NULL);
+ /* Disconnected. */
+ close(sendfd);
+ close(recvfd);
+}
+
+static void
+signop(int sig __unused)
+{
+
+ /* Do nothing. */
+}
+
+static void
+g_gatec_loop(void)
+{
+ struct g_gate_ctl_cancel ggioc;
+
+ signal(SIGUSR1, signop);
+ for (;;) {
+ g_gatec_start();
+ g_gate_log(LOG_NOTICE, "Disconnected [%s %s]. Connecting...",
+ host, path);
+ while (!g_gatec_connect()) {
+ sleep(2);
+ g_gate_log(LOG_NOTICE, "Connecting [%s %s]...", host,
+ path);
+ }
+ ggioc.gctl_version = G_GATE_VERSION;
+ ggioc.gctl_unit = unit;
+ ggioc.gctl_seq = 0;
+ g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc);
+ }
+}
+
+static void
+g_gatec_create(void)
+{
+ struct g_gate_ctl_create ggioc;
+
+ if (!g_gatec_connect())
+ g_gate_xlog("Cannot connect: %s.", strerror(errno));
+
+ /*
+ * Ok, got both sockets, time to create provider.
+ */
+ ggioc.gctl_version = G_GATE_VERSION;
+ ggioc.gctl_mediasize = mediasize;
+ ggioc.gctl_sectorsize = sectorsize;
+ ggioc.gctl_flags = flags;
+ ggioc.gctl_maxcount = queue_size;
+ ggioc.gctl_timeout = timeout;
+ ggioc.gctl_unit = unit;
+ snprintf(ggioc.gctl_info, sizeof(ggioc.gctl_info), "%s:%u %s", host,
+ port, path);
+ g_gate_ioctl(G_GATE_CMD_CREATE, &ggioc);
+ if (unit == -1)
+ printf("%s%u\n", G_GATE_PROVIDER_NAME, ggioc.gctl_unit);
+ unit = ggioc.gctl_unit;
+
+ mydaemon();
+ g_gatec_loop();
+}
+
+static void
+g_gatec_rescue(void)
+{
+ struct g_gate_ctl_cancel ggioc;
+
+ if (!g_gatec_connect())
+ g_gate_xlog("Cannot connect: %s.", strerror(errno));
+
+ ggioc.gctl_version = G_GATE_VERSION;
+ ggioc.gctl_unit = unit;
+ ggioc.gctl_seq = 0;
+ g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc);
+
+ mydaemon();
+ g_gatec_loop();
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ if (argc < 2)
+ usage();
+ if (strcasecmp(argv[1], "create") == 0)
+ action = CREATE;
+ else if (strcasecmp(argv[1], "destroy") == 0)
+ action = DESTROY;
+ else if (strcasecmp(argv[1], "list") == 0)
+ action = LIST;
+ else if (strcasecmp(argv[1], "rescue") == 0)
+ action = RESCUE;
+ else
+ usage();
+ argc -= 1;
+ argv += 1;
+ for (;;) {
+ int ch;
+
+ ch = getopt(argc, argv, "fno:p:q:R:S:s:t:u:v");
+ if (ch == -1)
+ break;
+ switch (ch) {
+ case 'f':
+ if (action != DESTROY)
+ usage();
+ force = 1;
+ break;
+ case 'n':
+ if (action != CREATE && action != RESCUE)
+ usage();
+ nagle = 0;
+ break;
+ case 'o':
+ if (action != CREATE && action != RESCUE)
+ usage();
+ if (strcasecmp("ro", optarg) == 0)
+ flags = G_GATE_FLAG_READONLY;
+ else if (strcasecmp("wo", optarg) == 0)
+ flags = G_GATE_FLAG_WRITEONLY;
+ else if (strcasecmp("rw", optarg) == 0)
+ flags = 0;
+ else {
+ errx(EXIT_FAILURE,
+ "Invalid argument for '-o' option.");
+ }
+ break;
+ case 'p':
+ if (action != CREATE && action != RESCUE)
+ usage();
+ errno = 0;
+ port = strtoul(optarg, NULL, 10);
+ if (port == 0 && errno != 0)
+ errx(EXIT_FAILURE, "Invalid port.");
+ break;
+ case 'q':
+ if (action != CREATE)
+ usage();
+ errno = 0;
+ queue_size = strtoul(optarg, NULL, 10);
+ if (queue_size == 0 && errno != 0)
+ errx(EXIT_FAILURE, "Invalid queue_size.");
+ break;
+ case 'R':
+ if (action != CREATE && action != RESCUE)
+ usage();
+ errno = 0;
+ rcvbuf = strtoul(optarg, NULL, 10);
+ if (rcvbuf == 0 && errno != 0)
+ errx(EXIT_FAILURE, "Invalid rcvbuf.");
+ break;
+ case 'S':
+ if (action != CREATE && action != RESCUE)
+ usage();
+ errno = 0;
+ sndbuf = strtoul(optarg, NULL, 10);
+ if (sndbuf == 0 && errno != 0)
+ errx(EXIT_FAILURE, "Invalid sndbuf.");
+ break;
+ case 's':
+ if (action != CREATE)
+ usage();
+ errno = 0;
+ sectorsize = strtoul(optarg, NULL, 10);
+ if (sectorsize == 0 && errno != 0)
+ errx(EXIT_FAILURE, "Invalid sectorsize.");
+ break;
+ case 't':
+ if (action != CREATE)
+ usage();
+ errno = 0;
+ timeout = strtoul(optarg, NULL, 10);
+ if (timeout == 0 && errno != 0)
+ errx(EXIT_FAILURE, "Invalid timeout.");
+ break;
+ case 'u':
+ errno = 0;
+ unit = strtol(optarg, NULL, 10);
+ if (unit == 0 && errno != 0)
+ errx(EXIT_FAILURE, "Invalid unit number.");
+ break;
+ case 'v':
+ if (action == DESTROY)
+ usage();
+ g_gate_verbose++;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch (action) {
+ case CREATE:
+ if (argc != 2)
+ usage();
+ g_gate_load_module();
+ g_gate_open_device();
+ host = argv[0];
+ path = argv[1];
+ g_gatec_create();
+ break;
+ case DESTROY:
+ if (unit == -1) {
+ fprintf(stderr, "Required unit number.\n");
+ usage();
+ }
+ g_gate_verbose = 1;
+ g_gate_open_device();
+ g_gate_destroy(unit, force);
+ break;
+ case LIST:
+ g_gate_list(unit, g_gate_verbose);
+ break;
+ case RESCUE:
+ if (argc != 2)
+ usage();
+ if (unit == -1) {
+ fprintf(stderr, "Required unit number.\n");
+ usage();
+ }
+ g_gate_open_device();
+ host = argv[0];
+ path = argv[1];
+ g_gatec_rescue();
+ break;
+ case UNSET:
+ default:
+ usage();
+ }
+ g_gate_close_device();
+ exit(EXIT_SUCCESS);
+}
diff --git a/sbin/ggate/ggated/Makefile b/sbin/ggate/ggated/Makefile
new file mode 100644
index 0000000..4e7708e
--- /dev/null
+++ b/sbin/ggate/ggated/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../shared
+
+PROG= ggated
+MAN= ggated.8
+SRCS= ggated.c ggate.c
+
+DPADD= ${LIBPTHREAD}
+LDADD= -lpthread
+
+CFLAGS+= -I${.CURDIR}/../shared
+
+.include <bsd.prog.mk>
diff --git a/sbin/ggate/ggated/ggated.8 b/sbin/ggate/ggated/ggated.8
new file mode 100644
index 0000000..3024a04
--- /dev/null
+++ b/sbin/ggate/ggated/ggated.8
@@ -0,0 +1,111 @@
+.\" Copyright (c) 2004 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 29, 2004
+.Dt GGATED 8
+.Os
+.Sh NAME
+.Nm ggated
+.Nd "GEOM Gate network daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl h
+.Op Fl n
+.Op Fl v
+.Op Fl a Ar address
+.Op Fl p Ar port
+.Op Fl R Ar rcvbuf
+.Op Fl S Ar sndbuf
+.Op Ar "exports file"
+.Sh DESCRIPTION
+The
+.Nm
+utility is a network server for GEOM Gate class.
+It runs on a server machine to service GEOM Gate requests from workers
+placed on a client machine.
+Keep in mind, that connection between
+.Xr ggatec 8
+and
+.Nm
+is not encrypted.
+.Pp
+Available options:
+.Bl -tag -width ".Ar exports\ file"
+.It Fl a Ar address
+Specifies an IP address to bind to.
+.It Fl h
+Print available options.
+.It Fl n
+Do not use
+.Dv TCP_NODELAY
+option on TCP sockets.
+.It Fl p Ar port
+Port on which
+.Nm
+listens for connection.
+Default is 3080.
+.It Fl R Ar rcvbuf
+Size of receive buffer to use.
+Default is 131072 (128kB).
+.It Fl S Ar sndbuf
+Size of send buffer to use.
+Default is 131072 (128kB).
+.It Fl v
+Do not fork, run in foreground and print debug informations on standard
+output.
+.It Ar "exports file"
+An alternate location for the exports file.
+.El
+.Pp
+The format of an exports file is as follows:
+.Bd -literal -offset indent
+1.2.3.4 RO /dev/acd0
+1.2.3.0/24 RW /tmp/test.img
+hostname WO /tmp/image
+.Ed
+.Sh EXIT STATUS
+Exit status is 0 on success, or 1 if the command fails.
+To get details about the failure,
+.Nm
+should be called with the
+.Fl v
+option.
+.Sh EXAMPLES
+Export CD-ROM device and a file:
+.Bd -literal -offset indent
+# echo "1.2.3.0/24 RO /dev/acd0" > /etc/gg.exports
+# echo "client RW /image" >> /etc/gg.exports
+# ggated
+.Ed
+.Sh SEE ALSO
+.Xr geom 4 ,
+.Xr ggatec 8 ,
+.Xr ggatel 8
+.Sh AUTHORS
+The
+.Nm
+utility as well as this manual page was written by
+.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org .
diff --git a/sbin/ggate/ggated/ggated.c b/sbin/ggate/ggated/ggated.c
new file mode 100644
index 0000000..82d66e1
--- /dev/null
+++ b/sbin/ggate/ggated/ggated.c
@@ -0,0 +1,1044 @@
+/*-
+ * Copyright (c) 2004 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/endian.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/disk.h>
+#include <sys/bio.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <signal.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <libgen.h>
+#include <syslog.h>
+#include <stdarg.h>
+
+#include "ggate.h"
+
+
+#define GGATED_EXPORT_FILE "/etc/gg.exports"
+
+struct ggd_connection {
+ off_t c_mediasize;
+ off_t c_sectorsize;
+ unsigned c_flags; /* flags (RO/RW) */
+ int c_diskfd;
+ int c_sendfd;
+ int c_recvfd;
+ time_t c_birthtime;
+ char *c_path;
+ uint64_t c_token;
+ in_addr_t c_srcip;
+ LIST_ENTRY(ggd_connection) c_next;
+};
+
+struct ggd_request {
+ struct g_gate_hdr r_hdr;
+ char *r_data;
+ TAILQ_ENTRY(ggd_request) r_next;
+};
+#define r_cmd r_hdr.gh_cmd
+#define r_offset r_hdr.gh_offset
+#define r_length r_hdr.gh_length
+#define r_error r_hdr.gh_error
+
+struct ggd_export {
+ char *e_path; /* path to device/file */
+ in_addr_t e_ip; /* remote IP address */
+ in_addr_t e_mask; /* IP mask */
+ unsigned e_flags; /* flags (RO/RW) */
+ SLIST_ENTRY(ggd_export) e_next;
+};
+
+static const char *exports_file = GGATED_EXPORT_FILE;
+static int got_sighup = 0;
+in_addr_t bindaddr;
+
+static TAILQ_HEAD(, ggd_request) inqueue = TAILQ_HEAD_INITIALIZER(inqueue);
+static TAILQ_HEAD(, ggd_request) outqueue = TAILQ_HEAD_INITIALIZER(outqueue);
+pthread_mutex_t inqueue_mtx, outqueue_mtx;
+pthread_cond_t inqueue_cond, outqueue_cond;
+
+static SLIST_HEAD(, ggd_export) exports = SLIST_HEAD_INITIALIZER(&exports);
+static LIST_HEAD(, ggd_connection) connections = LIST_HEAD_INITIALIZER(&connection);
+
+static void *recv_thread(void *arg);
+static void *disk_thread(void *arg);
+static void *send_thread(void *arg);
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: %s [-nv] [-a address] [-p port] [-R rcvbuf] "
+ "[-S sndbuf] [exports file]\n", getprogname());
+ exit(EXIT_FAILURE);
+}
+
+static char *
+ip2str(in_addr_t ip)
+{
+ static char sip[16];
+
+ snprintf(sip, sizeof(sip), "%u.%u.%u.%u",
+ ((ip >> 24) & 0xff),
+ ((ip >> 16) & 0xff),
+ ((ip >> 8) & 0xff),
+ (ip & 0xff));
+ return (sip);
+}
+
+static in_addr_t
+countmask(unsigned m)
+{
+ in_addr_t mask;
+
+ if (m == 0) {
+ mask = 0x0;
+ } else {
+ mask = 1 << (32 - m);
+ mask--;
+ mask = ~mask;
+ }
+ return (mask);
+}
+
+static void
+line_parse(char *line, unsigned lineno)
+{
+ struct ggd_export *ex;
+ char *word, *path, *sflags;
+ unsigned flags, i, vmask;
+ in_addr_t ip, mask;
+
+ ip = mask = flags = vmask = 0;
+ path = NULL;
+ sflags = NULL;
+
+ for (i = 0, word = strtok(line, " \t"); word != NULL;
+ i++, word = strtok(NULL, " \t")) {
+ switch (i) {
+ case 0: /* IP address or host name */
+ ip = g_gate_str2ip(strsep(&word, "/"));
+ if (ip == INADDR_NONE) {
+ g_gate_xlog("Invalid IP/host name at line %u.",
+ lineno);
+ }
+ ip = ntohl(ip);
+ if (word == NULL)
+ vmask = 32;
+ else {
+ errno = 0;
+ vmask = strtoul(word, NULL, 10);
+ if (vmask == 0 && errno != 0) {
+ g_gate_xlog("Invalid IP mask value at "
+ "line %u.", lineno);
+ }
+ if ((unsigned)vmask > 32) {
+ g_gate_xlog("Invalid IP mask value at line %u.",
+ lineno);
+ }
+ }
+ mask = countmask(vmask);
+ break;
+ case 1: /* flags */
+ if (strcasecmp("rd", word) == 0 ||
+ strcasecmp("ro", word) == 0) {
+ flags = O_RDONLY;
+ } else if (strcasecmp("wo", word) == 0) {
+ flags = O_WRONLY;
+ } else if (strcasecmp("rw", word) == 0) {
+ flags = O_RDWR;
+ } else {
+ g_gate_xlog("Invalid value in flags field at "
+ "line %u.", lineno);
+ }
+ sflags = word;
+ break;
+ case 2: /* path */
+ if (strlen(word) >= MAXPATHLEN) {
+ g_gate_xlog("Path too long at line %u. ",
+ lineno);
+ }
+ path = word;
+ break;
+ default:
+ g_gate_xlog("Too many arguments at line %u. ", lineno);
+ }
+ }
+ if (i != 3)
+ g_gate_xlog("Too few arguments at line %u.", lineno);
+
+ ex = malloc(sizeof(*ex));
+ if (ex == NULL)
+ g_gate_xlog("No enough memory.");
+ ex->e_path = strdup(path);
+ if (ex->e_path == NULL)
+ g_gate_xlog("No enough memory.");
+
+ /* Made 'and' here. */
+ ex->e_ip = (ip & mask);
+ ex->e_mask = mask;
+ ex->e_flags = flags;
+
+ SLIST_INSERT_HEAD(&exports, ex, e_next);
+
+ g_gate_log(LOG_DEBUG, "Added %s/%u %s %s to exports list.",
+ ip2str(ex->e_ip), vmask, path, sflags);
+}
+
+static void
+exports_clear(void)
+{
+ struct ggd_export *ex;
+
+ while (!SLIST_EMPTY(&exports)) {
+ ex = SLIST_FIRST(&exports);
+ SLIST_REMOVE_HEAD(&exports, e_next);
+ free(ex);
+ }
+}
+
+#define EXPORTS_LINE_SIZE 2048
+static void
+exports_get(void)
+{
+ char buf[EXPORTS_LINE_SIZE], *line;
+ unsigned lineno = 0, objs = 0, len;
+ FILE *fd;
+
+ exports_clear();
+
+ fd = fopen(exports_file, "r");
+ if (fd == NULL) {
+ g_gate_xlog("Cannot open exports file (%s): %s.", exports_file,
+ strerror(errno));
+ }
+
+ g_gate_log(LOG_INFO, "Reading exports file (%s).", exports_file);
+
+ for (;;) {
+ if (fgets(buf, sizeof(buf), fd) == NULL) {
+ if (feof(fd))
+ break;
+
+ g_gate_xlog("Error while reading exports file: %s.",
+ strerror(errno));
+ }
+
+ /* Increase line count. */
+ lineno++;
+
+ /* Skip spaces and tabs. */
+ for (line = buf; *line == ' ' || *line == '\t'; ++line)
+ ;
+
+ /* Empty line, comment or empty line at the end of file. */
+ if (*line == '\n' || *line == '#' || *line == '\0')
+ continue;
+
+ len = strlen(line);
+ if (line[len - 1] == '\n') {
+ /* Remove new line char. */
+ line[len - 1] = '\0';
+ } else {
+ if (!feof(fd))
+ g_gate_xlog("Line %u too long.", lineno);
+ }
+
+ line_parse(line, lineno);
+ objs++;
+ }
+
+ fclose(fd);
+
+ if (objs == 0)
+ g_gate_xlog("There are no objects to export.");
+
+ g_gate_log(LOG_INFO, "Exporting %u object(s).", objs);
+}
+
+static int
+exports_check(struct ggd_export *ex, struct g_gate_cinit *cinit,
+ struct ggd_connection *conn)
+{
+ char ipmask[32]; /* 32 == strlen("xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx")+1 */
+ int error = 0, flags;
+
+ strlcpy(ipmask, ip2str(ex->e_ip), sizeof(ipmask));
+ strlcat(ipmask, "/", sizeof(ipmask));
+ strlcat(ipmask, ip2str(ex->e_mask), sizeof(ipmask));
+ if ((cinit->gc_flags & GGATE_FLAG_RDONLY) != 0) {
+ if (ex->e_flags == O_WRONLY) {
+ g_gate_log(LOG_WARNING, "Read-only access requested, "
+ "but %s (%s) is exported write-only.", ex->e_path,
+ ipmask);
+ return (EPERM);
+ } else {
+ conn->c_flags |= GGATE_FLAG_RDONLY;
+ }
+ } else if ((cinit->gc_flags & GGATE_FLAG_WRONLY) != 0) {
+ if (ex->e_flags == O_RDONLY) {
+ g_gate_log(LOG_WARNING, "Write-only access requested, "
+ "but %s (%s) is exported read-only.", ex->e_path,
+ ipmask);
+ return (EPERM);
+ } else {
+ conn->c_flags |= GGATE_FLAG_WRONLY;
+ }
+ } else {
+ if (ex->e_flags == O_RDONLY) {
+ g_gate_log(LOG_WARNING, "Read-write access requested, "
+ "but %s (%s) is exported read-only.", ex->e_path,
+ ipmask);
+ return (EPERM);
+ } else if (ex->e_flags == O_WRONLY) {
+ g_gate_log(LOG_WARNING, "Read-write access requested, "
+ "but %s (%s) is exported write-only.", ex->e_path,
+ ipmask);
+ return (EPERM);
+ }
+ }
+ if ((conn->c_flags & GGATE_FLAG_RDONLY) != 0)
+ flags = O_RDONLY;
+ else if ((conn->c_flags & GGATE_FLAG_WRONLY) != 0)
+ flags = O_WRONLY;
+ else
+ flags = O_RDWR;
+ conn->c_diskfd = open(ex->e_path, flags);
+ if (conn->c_diskfd == -1) {
+ error = errno;
+ g_gate_log(LOG_ERR, "Cannot open %s: %s.", ex->e_path,
+ strerror(error));
+ return (error);
+ }
+ return (0);
+}
+
+static struct ggd_export *
+exports_find(struct sockaddr *s, struct g_gate_cinit *cinit,
+ struct ggd_connection *conn)
+{
+ struct ggd_export *ex;
+ in_addr_t ip;
+ int error;
+
+ ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr);
+ SLIST_FOREACH(ex, &exports, e_next) {
+ if ((ip & ex->e_mask) != ex->e_ip) {
+ g_gate_log(LOG_DEBUG, "exports[%s]: IP mismatch.",
+ ex->e_path);
+ continue;
+ }
+ if (strcmp(cinit->gc_path, ex->e_path) != 0) {
+ g_gate_log(LOG_DEBUG, "exports[%s]: Path mismatch.",
+ ex->e_path);
+ continue;
+ }
+ error = exports_check(ex, cinit, conn);
+ if (error == 0)
+ return (ex);
+ else {
+ errno = error;
+ return (NULL);
+ }
+ }
+ g_gate_log(LOG_WARNING, "Unauthorized connection from: %s.",
+ ip2str(ip));
+ errno = EPERM;
+ return (NULL);
+}
+
+/*
+ * Remove timed out connections.
+ */
+static void
+connection_cleanups(void)
+{
+ struct ggd_connection *conn, *tconn;
+ time_t now;
+
+ time(&now);
+ LIST_FOREACH_SAFE(conn, &connections, c_next, tconn) {
+ if (now - conn->c_birthtime > 10) {
+ LIST_REMOVE(conn, c_next);
+ g_gate_log(LOG_NOTICE,
+ "Connection from %s [%s] removed.",
+ ip2str(conn->c_srcip), conn->c_path);
+ close(conn->c_diskfd);
+ close(conn->c_sendfd);
+ close(conn->c_recvfd);
+ free(conn->c_path);
+ free(conn);
+ }
+ }
+}
+
+static struct ggd_connection *
+connection_find(struct g_gate_cinit *cinit)
+{
+ struct ggd_connection *conn;
+
+ LIST_FOREACH(conn, &connections, c_next) {
+ if (conn->c_token == cinit->gc_token)
+ break;
+ }
+ return (conn);
+}
+
+static struct ggd_connection *
+connection_new(struct g_gate_cinit *cinit, struct sockaddr *s, int sfd)
+{
+ struct ggd_connection *conn;
+ in_addr_t ip;
+
+ /*
+ * First, look for old connections.
+ * We probably should do it every X seconds, but what for?
+ * It is only dangerous if an attacker wants to overload connections
+ * queue, so here is a good place to do the cleanups.
+ */
+ connection_cleanups();
+
+ conn = malloc(sizeof(*conn));
+ if (conn == NULL)
+ return (NULL);
+ conn->c_path = strdup(cinit->gc_path);
+ if (conn->c_path == NULL) {
+ free(conn);
+ return (NULL);
+ }
+ conn->c_token = cinit->gc_token;
+ ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr);
+ conn->c_srcip = ip;
+ conn->c_sendfd = conn->c_recvfd = -1;
+ if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0)
+ conn->c_sendfd = sfd;
+ else
+ conn->c_recvfd = sfd;
+ conn->c_mediasize = 0;
+ conn->c_sectorsize = 0;
+ time(&conn->c_birthtime);
+ conn->c_flags = cinit->gc_flags;
+ LIST_INSERT_HEAD(&connections, conn, c_next);
+ g_gate_log(LOG_DEBUG, "Connection created [%s, %s].", ip2str(ip),
+ conn->c_path);
+ return (conn);
+}
+
+static int
+connection_add(struct ggd_connection *conn, struct g_gate_cinit *cinit,
+ struct sockaddr *s, int sfd)
+{
+ in_addr_t ip;
+
+ ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr);
+ if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0) {
+ if (conn->c_sendfd != -1) {
+ g_gate_log(LOG_WARNING,
+ "Send socket already exists [%s, %s].", ip2str(ip),
+ conn->c_path);
+ return (EEXIST);
+ }
+ conn->c_sendfd = sfd;
+ } else {
+ if (conn->c_recvfd != -1) {
+ g_gate_log(LOG_WARNING,
+ "Receive socket already exists [%s, %s].",
+ ip2str(ip), conn->c_path);
+ return (EEXIST);
+ }
+ conn->c_recvfd = sfd;
+ }
+ g_gate_log(LOG_DEBUG, "Connection added [%s, %s].", ip2str(ip),
+ conn->c_path);
+ return (0);
+}
+
+/*
+ * Remove one socket from the given connection or the whole
+ * connection if sfd == -1.
+ */
+static void
+connection_remove(struct ggd_connection *conn)
+{
+
+ LIST_REMOVE(conn, c_next);
+ g_gate_log(LOG_DEBUG, "Connection removed [%s %s].",
+ ip2str(conn->c_srcip), conn->c_path);
+ if (conn->c_sendfd != -1)
+ close(conn->c_sendfd);
+ if (conn->c_recvfd != -1)
+ close(conn->c_recvfd);
+ free(conn->c_path);
+ free(conn);
+}
+
+static int
+connection_ready(struct ggd_connection *conn)
+{
+
+ return (conn->c_sendfd != -1 && conn->c_recvfd != -1);
+}
+
+static void
+connection_launch(struct ggd_connection *conn)
+{
+ pthread_t td;
+ int error, pid;
+
+ pid = fork();
+ if (pid > 0)
+ return;
+ else if (pid == -1) {
+ g_gate_log(LOG_ERR, "Cannot fork: %s.", strerror(errno));
+ return;
+ }
+ g_gate_log(LOG_DEBUG, "Process created [%s].", conn->c_path);
+
+ /*
+ * Create condition variables and mutexes for in-queue and out-queue
+ * synchronization.
+ */
+ error = pthread_mutex_init(&inqueue_mtx, NULL);
+ if (error != 0) {
+ g_gate_xlog("pthread_mutex_init(inqueue_mtx): %s.",
+ strerror(error));
+ }
+ error = pthread_cond_init(&inqueue_cond, NULL);
+ if (error != 0) {
+ g_gate_xlog("pthread_cond_init(inqueue_cond): %s.",
+ strerror(error));
+ }
+ error = pthread_mutex_init(&outqueue_mtx, NULL);
+ if (error != 0) {
+ g_gate_xlog("pthread_mutex_init(outqueue_mtx): %s.",
+ strerror(error));
+ }
+ error = pthread_cond_init(&outqueue_cond, NULL);
+ if (error != 0) {
+ g_gate_xlog("pthread_cond_init(outqueue_cond): %s.",
+ strerror(error));
+ }
+
+ /*
+ * Create threads:
+ * recvtd - thread for receiving I/O request
+ * diskio - thread for doing I/O request
+ * sendtd - thread for sending I/O requests back
+ */
+ error = pthread_create(&td, NULL, send_thread, conn);
+ if (error != 0) {
+ g_gate_xlog("pthread_create(send_thread): %s.",
+ strerror(error));
+ }
+ error = pthread_create(&td, NULL, recv_thread, conn);
+ if (error != 0) {
+ g_gate_xlog("pthread_create(recv_thread): %s.",
+ strerror(error));
+ }
+ disk_thread(conn);
+}
+
+static void
+sendfail(int sfd, int error, const char *fmt, ...)
+{
+ struct g_gate_sinit sinit;
+ va_list ap;
+ ssize_t data;
+
+ sinit.gs_error = error;
+ g_gate_swap2n_sinit(&sinit);
+ data = g_gate_send(sfd, &sinit, sizeof(sinit), 0);
+ g_gate_swap2h_sinit(&sinit);
+ if (data != sizeof(sinit)) {
+ g_gate_log(LOG_WARNING, "Cannot send initial packet: %s.",
+ strerror(errno));
+ return;
+ }
+ if (fmt != NULL) {
+ va_start(ap, fmt);
+ g_gate_vlog(LOG_WARNING, fmt, ap);
+ va_end(ap);
+ }
+}
+
+static void *
+malloc_waitok(size_t size)
+{
+ void *p;
+
+ while ((p = malloc(size)) == NULL) {
+ g_gate_log(LOG_DEBUG, "Cannot allocate %zu bytes.", size);
+ sleep(1);
+ }
+ return (p);
+}
+
+static void *
+recv_thread(void *arg)
+{
+ struct ggd_connection *conn;
+ struct ggd_request *req;
+ ssize_t data;
+ int error, fd;
+
+ conn = arg;
+ g_gate_log(LOG_NOTICE, "%s: started [%s]!", __func__, conn->c_path);
+ fd = conn->c_recvfd;
+ for (;;) {
+ /*
+ * Get header packet.
+ */
+ req = malloc_waitok(sizeof(*req));
+ data = g_gate_recv(fd, &req->r_hdr, sizeof(req->r_hdr),
+ MSG_WAITALL);
+ if (data == 0) {
+ g_gate_log(LOG_DEBUG, "Process %u exiting.", getpid());
+ exit(EXIT_SUCCESS);
+ } else if (data == -1) {
+ g_gate_xlog("Error while receiving hdr packet: %s.",
+ strerror(errno));
+ } else if (data != sizeof(req->r_hdr)) {
+ g_gate_xlog("Malformed hdr packet received.");
+ }
+ g_gate_log(LOG_DEBUG, "Received hdr packet.");
+ g_gate_swap2h_hdr(&req->r_hdr);
+
+ g_gate_log(LOG_DEBUG, "%s: offset=%jd length=%u", __func__,
+ (intmax_t)req->r_offset, (unsigned)req->r_length);
+
+ /*
+ * Allocate memory for data.
+ */
+ req->r_data = malloc_waitok(req->r_length);
+
+ /*
+ * Receive data to write for WRITE request.
+ */
+ if (req->r_cmd == GGATE_CMD_WRITE) {
+ g_gate_log(LOG_DEBUG, "Waiting for %u bytes of data...",
+ req->r_length);
+ data = g_gate_recv(fd, req->r_data, req->r_length,
+ MSG_WAITALL);
+ if (data == -1) {
+ g_gate_xlog("Error while receiving data: %s.",
+ strerror(errno));
+ }
+ }
+
+ /*
+ * Put the request onto the incoming queue.
+ */
+ error = pthread_mutex_lock(&inqueue_mtx);
+ assert(error == 0);
+ TAILQ_INSERT_TAIL(&inqueue, req, r_next);
+ error = pthread_cond_signal(&inqueue_cond);
+ assert(error == 0);
+ error = pthread_mutex_unlock(&inqueue_mtx);
+ assert(error == 0);
+ }
+}
+
+static void *
+disk_thread(void *arg)
+{
+ struct ggd_connection *conn;
+ struct ggd_request *req;
+ ssize_t data;
+ int error, fd;
+
+ conn = arg;
+ g_gate_log(LOG_NOTICE, "%s: started [%s]!", __func__, conn->c_path);
+ fd = conn->c_diskfd;
+ for (;;) {
+ /*
+ * Get a request from the incoming queue.
+ */
+ error = pthread_mutex_lock(&inqueue_mtx);
+ assert(error == 0);
+ while ((req = TAILQ_FIRST(&inqueue)) == NULL) {
+ error = pthread_cond_wait(&inqueue_cond, &inqueue_mtx);
+ assert(error == 0);
+ }
+ TAILQ_REMOVE(&inqueue, req, r_next);
+ error = pthread_mutex_unlock(&inqueue_mtx);
+ assert(error == 0);
+
+ /*
+ * Check the request.
+ */
+ assert(req->r_cmd == GGATE_CMD_READ || req->r_cmd == GGATE_CMD_WRITE);
+ assert(req->r_offset + req->r_length <= (uintmax_t)conn->c_mediasize);
+ assert((req->r_offset % conn->c_sectorsize) == 0);
+ assert((req->r_length % conn->c_sectorsize) == 0);
+
+ g_gate_log(LOG_DEBUG, "%s: offset=%jd length=%u", __func__,
+ (intmax_t)req->r_offset, (unsigned)req->r_length);
+
+ /*
+ * Do the request.
+ */
+ data = 0;
+ switch (req->r_cmd) {
+ case GGATE_CMD_READ:
+ data = pread(fd, req->r_data, req->r_length,
+ req->r_offset);
+ break;
+ case GGATE_CMD_WRITE:
+ data = pwrite(fd, req->r_data, req->r_length,
+ req->r_offset);
+ /* Free data memory here - better sooner. */
+ free(req->r_data);
+ req->r_data = NULL;
+ break;
+ }
+ if (data != (ssize_t)req->r_length) {
+ /* Report short reads/writes as I/O errors. */
+ if (errno == 0)
+ errno = EIO;
+ g_gate_log(LOG_ERR, "Disk error: %s", strerror(errno));
+ req->r_error = errno;
+ if (req->r_data != NULL) {
+ free(req->r_data);
+ req->r_data = NULL;
+ }
+ }
+
+ /*
+ * Put the request onto the outgoing queue.
+ */
+ error = pthread_mutex_lock(&outqueue_mtx);
+ assert(error == 0);
+ TAILQ_INSERT_TAIL(&outqueue, req, r_next);
+ error = pthread_cond_signal(&outqueue_cond);
+ assert(error == 0);
+ error = pthread_mutex_unlock(&outqueue_mtx);
+ assert(error == 0);
+ }
+}
+
+static void *
+send_thread(void *arg)
+{
+ struct ggd_connection *conn;
+ struct ggd_request *req;
+ ssize_t data;
+ int error, fd;
+
+ conn = arg;
+ g_gate_log(LOG_NOTICE, "%s: started [%s]!", __func__, conn->c_path);
+ fd = conn->c_sendfd;
+ for (;;) {
+ /*
+ * Get a request from the outgoing queue.
+ */
+ error = pthread_mutex_lock(&outqueue_mtx);
+ assert(error == 0);
+ while ((req = TAILQ_FIRST(&outqueue)) == NULL) {
+ error = pthread_cond_wait(&outqueue_cond,
+ &outqueue_mtx);
+ assert(error == 0);
+ }
+ TAILQ_REMOVE(&outqueue, req, r_next);
+ error = pthread_mutex_unlock(&outqueue_mtx);
+ assert(error == 0);
+
+ g_gate_log(LOG_DEBUG, "%s: offset=%jd length=%u", __func__,
+ (intmax_t)req->r_offset, (unsigned)req->r_length);
+
+ /*
+ * Send the request.
+ */
+ g_gate_swap2n_hdr(&req->r_hdr);
+ if (g_gate_send(fd, &req->r_hdr, sizeof(req->r_hdr), 0) == -1) {
+ g_gate_xlog("Error while sending hdr packet: %s.",
+ strerror(errno));
+ }
+ g_gate_log(LOG_DEBUG, "Sent hdr packet.");
+ g_gate_swap2h_hdr(&req->r_hdr);
+ if (req->r_data != NULL) {
+ data = g_gate_send(fd, req->r_data, req->r_length, 0);
+ if (data != (ssize_t)req->r_length) {
+ g_gate_xlog("Error while sending data: %s.",
+ strerror(errno));
+ }
+ g_gate_log(LOG_DEBUG,
+ "Sent %zd bytes (offset=%ju, size=%zu).", data,
+ (uintmax_t)req->r_offset, (size_t)req->r_length);
+ free(req->r_data);
+ }
+ free(req);
+ }
+}
+
+static void
+log_connection(struct sockaddr *from)
+{
+ in_addr_t ip;
+
+ ip = htonl(((struct sockaddr_in *)(void *)from)->sin_addr.s_addr);
+ g_gate_log(LOG_INFO, "Connection from: %s.", ip2str(ip));
+}
+
+static int
+handshake(struct sockaddr *from, int sfd)
+{
+ struct g_gate_version ver;
+ struct g_gate_cinit cinit;
+ struct g_gate_sinit sinit;
+ struct ggd_connection *conn;
+ struct ggd_export *ex;
+ ssize_t data;
+
+ log_connection(from);
+ /*
+ * Phase 1: Version verification.
+ */
+ g_gate_log(LOG_DEBUG, "Receiving version packet.");
+ data = g_gate_recv(sfd, &ver, sizeof(ver), MSG_WAITALL);
+ g_gate_swap2h_version(&ver);
+ if (data != sizeof(ver)) {
+ g_gate_log(LOG_WARNING, "Malformed version packet.");
+ return (0);
+ }
+ g_gate_log(LOG_DEBUG, "Version packet received.");
+ if (memcmp(ver.gv_magic, GGATE_MAGIC, strlen(GGATE_MAGIC)) != 0) {
+ g_gate_log(LOG_WARNING, "Invalid magic field.");
+ return (0);
+ }
+ if (ver.gv_version != GGATE_VERSION) {
+ g_gate_log(LOG_WARNING, "Version %u is not supported.",
+ ver.gv_version);
+ return (0);
+ }
+ ver.gv_error = 0;
+ g_gate_swap2n_version(&ver);
+ data = g_gate_send(sfd, &ver, sizeof(ver), 0);
+ g_gate_swap2h_version(&ver);
+ if (data == -1) {
+ sendfail(sfd, errno, "Error while sending version packet: %s.",
+ strerror(errno));
+ return (0);
+ }
+
+ /*
+ * Phase 2: Request verification.
+ */
+ g_gate_log(LOG_DEBUG, "Receiving initial packet.");
+ data = g_gate_recv(sfd, &cinit, sizeof(cinit), MSG_WAITALL);
+ g_gate_swap2h_cinit(&cinit);
+ if (data != sizeof(cinit)) {
+ g_gate_log(LOG_WARNING, "Malformed initial packet.");
+ return (0);
+ }
+ g_gate_log(LOG_DEBUG, "Initial packet received.");
+ conn = connection_find(&cinit);
+ if (conn != NULL) {
+ /*
+ * Connection should already exists.
+ */
+ g_gate_log(LOG_DEBUG, "Found existing connection (token=%lu).",
+ (unsigned long)conn->c_token);
+ if (connection_add(conn, &cinit, from, sfd) == -1) {
+ connection_remove(conn);
+ return (0);
+ }
+ } else {
+ /*
+ * New connection, allocate space.
+ */
+ conn = connection_new(&cinit, from, sfd);
+ if (conn == NULL) {
+ sendfail(sfd, ENOMEM,
+ "Cannot allocate new connection.");
+ return (0);
+ }
+ g_gate_log(LOG_DEBUG, "New connection created (token=%lu).",
+ (unsigned long)conn->c_token);
+ }
+
+ ex = exports_find(from, &cinit, conn);
+ if (ex == NULL) {
+ connection_remove(conn);
+ sendfail(sfd, errno, NULL);
+ return (0);
+ }
+ if (conn->c_mediasize == 0) {
+ conn->c_mediasize = g_gate_mediasize(conn->c_diskfd);
+ conn->c_sectorsize = g_gate_sectorsize(conn->c_diskfd);
+ }
+ sinit.gs_mediasize = conn->c_mediasize;
+ sinit.gs_sectorsize = conn->c_sectorsize;
+ sinit.gs_error = 0;
+
+ g_gate_log(LOG_DEBUG, "Sending initial packet.");
+
+ g_gate_swap2n_sinit(&sinit);
+ data = g_gate_send(sfd, &sinit, sizeof(sinit), 0);
+ g_gate_swap2h_sinit(&sinit);
+ if (data == -1) {
+ sendfail(sfd, errno, "Error while sending initial packet: %s.",
+ strerror(errno));
+ return (0);
+ }
+
+ if (connection_ready(conn)) {
+ connection_launch(conn);
+ connection_remove(conn);
+ }
+ return (1);
+}
+
+static void
+huphandler(int sig __unused)
+{
+
+ got_sighup = 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct sockaddr_in serv;
+ struct sockaddr from;
+ socklen_t fromlen;
+ int sfd, tmpsfd;
+ unsigned port;
+
+ bindaddr = htonl(INADDR_ANY);
+ port = G_GATE_PORT;
+ for (;;) {
+ int ch;
+
+ ch = getopt(argc, argv, "a:hnp:R:S:v");
+ if (ch == -1)
+ break;
+ switch (ch) {
+ case 'a':
+ bindaddr = g_gate_str2ip(optarg);
+ if (bindaddr == INADDR_NONE) {
+ errx(EXIT_FAILURE,
+ "Invalid IP/host name to bind to.");
+ }
+ break;
+ case 'n':
+ nagle = 0;
+ break;
+ case 'p':
+ errno = 0;
+ port = strtoul(optarg, NULL, 10);
+ if (port == 0 && errno != 0)
+ errx(EXIT_FAILURE, "Invalid port.");
+ break;
+ case 'R':
+ errno = 0;
+ rcvbuf = strtoul(optarg, NULL, 10);
+ if (rcvbuf == 0 && errno != 0)
+ errx(EXIT_FAILURE, "Invalid rcvbuf.");
+ break;
+ case 'S':
+ errno = 0;
+ sndbuf = strtoul(optarg, NULL, 10);
+ if (sndbuf == 0 && errno != 0)
+ errx(EXIT_FAILURE, "Invalid sndbuf.");
+ break;
+ case 'v':
+ g_gate_verbose++;
+ break;
+ case 'h':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argv[0] != NULL)
+ exports_file = argv[0];
+ exports_get();
+
+ if (!g_gate_verbose) {
+ /* Run in daemon mode. */
+ if (daemon(0, 0) == -1)
+ g_gate_xlog("Cannot daemonize: %s", strerror(errno));
+ }
+
+ signal(SIGCHLD, SIG_IGN);
+
+ sfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sfd == -1)
+ g_gate_xlog("Cannot open stream socket: %s.", strerror(errno));
+ bzero(&serv, sizeof(serv));
+ serv.sin_family = AF_INET;
+ serv.sin_addr.s_addr = bindaddr;
+ serv.sin_port = htons(port);
+
+ g_gate_socket_settings(sfd);
+
+ if (bind(sfd, (struct sockaddr *)&serv, sizeof(serv)) == -1)
+ g_gate_xlog("bind(): %s.", strerror(errno));
+ if (listen(sfd, 5) == -1)
+ g_gate_xlog("listen(): %s.", strerror(errno));
+
+ g_gate_log(LOG_INFO, "Listen on port: %d.", port);
+
+ signal(SIGHUP, huphandler);
+
+ for (;;) {
+ fromlen = sizeof(from);
+ tmpsfd = accept(sfd, &from, &fromlen);
+ if (tmpsfd == -1)
+ g_gate_xlog("accept(): %s.", strerror(errno));
+
+ if (got_sighup) {
+ got_sighup = 0;
+ exports_get();
+ }
+
+ if (!handshake(&from, tmpsfd))
+ close(tmpsfd);
+ }
+ close(sfd);
+ exit(EXIT_SUCCESS);
+}
diff --git a/sbin/ggate/ggatel/Makefile b/sbin/ggate/ggatel/Makefile
new file mode 100644
index 0000000..604a754
--- /dev/null
+++ b/sbin/ggate/ggatel/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../shared
+
+PROG= ggatel
+MAN= ggatel.8
+SRCS= ggatel.c ggate.c
+
+CFLAGS+= -DLIBGEOM
+CFLAGS+= -I${.CURDIR}/../shared
+
+DPADD= ${LIBGEOM} ${LIBSBUF} ${LIBBSDXML} ${LIBUTIL}
+LDADD= -lgeom -lsbuf -lbsdxml -lutil
+
+.include <bsd.prog.mk>
diff --git a/sbin/ggate/ggatel/ggatel.8 b/sbin/ggate/ggatel/ggatel.8
new file mode 100644
index 0000000..01b964a
--- /dev/null
+++ b/sbin/ggate/ggatel/ggatel.8
@@ -0,0 +1,160 @@
+.\" Copyright (c) 2004 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 26, 2004
+.Dt GGATEL 8
+.Os
+.Sh NAME
+.Nm ggatel
+.Nd "GEOM Gate local control utility"
+.Sh SYNOPSIS
+.Nm
+.Cm create
+.Op Fl v
+.Op Fl o Cm ro | wo | rw
+.Op Fl q Ar queue_size
+.Op Fl s Ar sectorsize
+.Op Fl t Ar timeout
+.Op Fl u Ar unit
+.Ar path
+.Nm
+.Cm attach
+.Op Fl v
+.Op Fl o Cm ro | wo | rw
+.Fl u Ar unit
+.Ar path
+.Nm
+.Cm destroy
+.Op Fl f
+.Fl u Ar unit
+.Nm
+.Cm list
+.Op Fl v
+.Op Fl u Ar unit
+.Sh DESCRIPTION
+The
+.Nm
+utility is a local GEOM Gate class consumer.
+It can be used as a replacement for
+.Xr md 4
+devices or as a
+.Dq GEOMificator
+for non GEOM-aware devices, but it was mainly created as an example
+on how to use and how to communicate with the GEOM Gate kernel module.
+.Pp
+Available commands:
+.Bl -tag -width ".Cm destroy"
+.It Cm create
+Create a
+.Nm ggate
+provider related to the given regular file or device.
+.It Cm attach
+Attach a worker process to an existing
+.Nm ggate
+provider.
+.It Cm destroy
+Destroy the given
+.Nm ggate
+provider.
+.It Cm list
+List
+.Nm ggate
+providers.
+.El
+.Pp
+Available options:
+.Bl -tag -width ".Fl s Cm ro | wo | rw"
+.It Fl f
+Forcibly destroy
+.Nm ggate
+provider (cancels all pending requests).
+.It Fl o Cm ro | wo | rw
+Specify permission to use when opening the file or device: read-only
+.Pq Cm ro ,
+write-only
+.Pq Cm wo ,
+or read-write
+.Pq Cm rw .
+Default is
+.Cm rw .
+.It Fl q Ar queue_size
+Number of pending I/O requests that can be queued before they will
+start to be canceled.
+Default is 1024.
+.It Fl s Ar sectorsize
+Sector size for
+.Nm ggate
+provider.
+If not specified, it is taken from device, or set to 512 bytes for files.
+.It Fl t Ar timeout
+Number of seconds to wait before an I/O request will be canceled.
+0 means no timeout.
+Default is 30.
+.It Fl u Ar unit
+Unit number to use.
+.It Fl v
+Do not fork, run in foreground and print debug informations on standard
+output.
+.It Ar path
+Path to a regular file or device.
+.El
+.Sh EXIT STATUS
+Exit status is 0 on success, or 1 if the command fails.
+To get details about the failure,
+.Nm
+should be called with the
+.Fl v
+option.
+.Sh EXAMPLES
+.Dq GEOMify
+the
+.Dq Li fd0
+device and use
+.Xr gbde 8
+to encrypt data on a floppy.
+.Bd -literal -offset indent
+ggatel create -u 5 /dev/fd0
+gbde init /dev/ggate5
+gbde attach ggate5
+newfs /dev/ggate5.bde
+mount /dev/ggate5.bde /secret
+cp /private/foo /secret/
+umount /secret
+gbde detach ggate5
+ggatel destroy -u 5
+.Ed
+.Sh SEE ALSO
+.Xr geom 4 ,
+.Xr gbde 8 ,
+.Xr ggatec 8 ,
+.Xr ggated 8 ,
+.Xr mount 8 ,
+.Xr newfs 8
+.Sh AUTHORS
+The
+.Nm
+utility as well as this manual page was written by
+.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org .
diff --git a/sbin/ggate/ggatel/ggatel.c b/sbin/ggate/ggatel/ggatel.c
new file mode 100644
index 0000000..03979c3
--- /dev/null
+++ b/sbin/ggate/ggatel/ggatel.c
@@ -0,0 +1,336 @@
+/*-
+ * Copyright (c) 2004 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <err.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/bio.h>
+#include <sys/disk.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
+
+#include <geom/gate/g_gate.h>
+#include "ggate.h"
+
+
+enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET;
+
+static const char *path = NULL;
+static int unit = -1;
+static unsigned flags = 0;
+static int force = 0;
+static unsigned queue_size = G_GATE_QUEUE_SIZE;
+static unsigned sectorsize = 0;
+static unsigned timeout = G_GATE_TIMEOUT;
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: %s create [-v] [-o <ro|wo|rw>] [-q queue_size] "
+ "[-s sectorsize] [-t timeout] [-u unit] <path>\n", getprogname());
+ fprintf(stderr, " %s rescue [-v] [-o <ro|wo|rw>] <-u unit> "
+ "<path>\n", getprogname());
+ fprintf(stderr, " %s destroy [-f] <-u unit>\n", getprogname());
+ fprintf(stderr, " %s list [-v] [-u unit]\n", getprogname());
+ exit(EXIT_FAILURE);
+}
+
+static int
+g_gate_openflags(unsigned ggflags)
+{
+
+ if ((ggflags & G_GATE_FLAG_READONLY) != 0)
+ return (O_RDONLY);
+ else if ((ggflags & G_GATE_FLAG_WRITEONLY) != 0)
+ return (O_WRONLY);
+ return (O_RDWR);
+}
+
+static void
+g_gatel_serve(int fd)
+{
+ struct g_gate_ctl_io ggio;
+ size_t bsize;
+
+ if (g_gate_verbose == 0) {
+ if (daemon(0, 0) == -1) {
+ g_gate_destroy(unit, 1);
+ err(EXIT_FAILURE, "Cannot daemonize");
+ }
+ }
+ g_gate_log(LOG_DEBUG, "Worker created: %u.", getpid());
+ ggio.gctl_version = G_GATE_VERSION;
+ ggio.gctl_unit = unit;
+ bsize = sectorsize;
+ ggio.gctl_data = malloc(bsize);
+ for (;;) {
+ int error;
+once_again:
+ ggio.gctl_length = bsize;
+ ggio.gctl_error = 0;
+ g_gate_ioctl(G_GATE_CMD_START, &ggio);
+ error = ggio.gctl_error;
+ switch (error) {
+ case 0:
+ break;
+ case ECANCELED:
+ /* Exit gracefully. */
+ free(ggio.gctl_data);
+ g_gate_close_device();
+ close(fd);
+ exit(EXIT_SUCCESS);
+ case ENOMEM:
+ /* Buffer too small. */
+ assert(ggio.gctl_cmd == BIO_DELETE ||
+ ggio.gctl_cmd == BIO_WRITE);
+ ggio.gctl_data = realloc(ggio.gctl_data,
+ ggio.gctl_length);
+ if (ggio.gctl_data != NULL) {
+ bsize = ggio.gctl_length;
+ goto once_again;
+ }
+ /* FALLTHROUGH */
+ case ENXIO:
+ default:
+ g_gate_xlog("ioctl(/dev/%s): %s.", G_GATE_CTL_NAME,
+ strerror(error));
+ }
+
+ error = 0;
+ switch (ggio.gctl_cmd) {
+ case BIO_READ:
+ if ((size_t)ggio.gctl_length > bsize) {
+ ggio.gctl_data = realloc(ggio.gctl_data,
+ ggio.gctl_length);
+ if (ggio.gctl_data != NULL)
+ bsize = ggio.gctl_length;
+ else
+ error = ENOMEM;
+ }
+ if (error == 0) {
+ if (pread(fd, ggio.gctl_data, ggio.gctl_length,
+ ggio.gctl_offset) == -1) {
+ error = errno;
+ }
+ }
+ break;
+ case BIO_DELETE:
+ case BIO_WRITE:
+ if (pwrite(fd, ggio.gctl_data, ggio.gctl_length,
+ ggio.gctl_offset) == -1) {
+ error = errno;
+ }
+ break;
+ default:
+ error = EOPNOTSUPP;
+ }
+
+ ggio.gctl_error = error;
+ g_gate_ioctl(G_GATE_CMD_DONE, &ggio);
+ }
+}
+
+static void
+g_gatel_create(void)
+{
+ struct g_gate_ctl_create ggioc;
+ int fd;
+
+ fd = open(path, g_gate_openflags(flags) | O_DIRECT | O_FSYNC);
+ if (fd == -1)
+ err(EXIT_FAILURE, "Cannot open %s", path);
+ ggioc.gctl_version = G_GATE_VERSION;
+ ggioc.gctl_unit = unit;
+ ggioc.gctl_mediasize = g_gate_mediasize(fd);
+ if (sectorsize == 0)
+ sectorsize = g_gate_sectorsize(fd);
+ ggioc.gctl_sectorsize = sectorsize;
+ ggioc.gctl_timeout = timeout;
+ ggioc.gctl_flags = flags;
+ ggioc.gctl_maxcount = queue_size;
+ strlcpy(ggioc.gctl_info, path, sizeof(ggioc.gctl_info));
+ g_gate_ioctl(G_GATE_CMD_CREATE, &ggioc);
+ if (unit == -1)
+ printf("%s%u\n", G_GATE_PROVIDER_NAME, ggioc.gctl_unit);
+ unit = ggioc.gctl_unit;
+ g_gatel_serve(fd);
+}
+
+static void
+g_gatel_rescue(void)
+{
+ struct g_gate_ctl_cancel ggioc;
+ int fd;
+
+ fd = open(path, g_gate_openflags(flags));
+ if (fd == -1)
+ err(EXIT_FAILURE, "Cannot open %s", path);
+
+ ggioc.gctl_version = G_GATE_VERSION;
+ ggioc.gctl_unit = unit;
+ ggioc.gctl_seq = 0;
+ g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc);
+
+ g_gatel_serve(fd);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ if (argc < 2)
+ usage();
+ if (strcasecmp(argv[1], "create") == 0)
+ action = CREATE;
+ else if (strcasecmp(argv[1], "rescue") == 0)
+ action = RESCUE;
+ else if (strcasecmp(argv[1], "destroy") == 0)
+ action = DESTROY;
+ else if (strcasecmp(argv[1], "list") == 0)
+ action = LIST;
+ else
+ usage();
+ argc -= 1;
+ argv += 1;
+ for (;;) {
+ int ch;
+
+ ch = getopt(argc, argv, "fo:q:s:t:u:v");
+ if (ch == -1)
+ break;
+ switch (ch) {
+ case 'f':
+ if (action != DESTROY)
+ usage();
+ force = 1;
+ break;
+ case 'o':
+ if (action != CREATE && action != RESCUE)
+ usage();
+ if (strcasecmp("ro", optarg) == 0)
+ flags = G_GATE_FLAG_READONLY;
+ else if (strcasecmp("wo", optarg) == 0)
+ flags = G_GATE_FLAG_WRITEONLY;
+ else if (strcasecmp("rw", optarg) == 0)
+ flags = 0;
+ else {
+ errx(EXIT_FAILURE,
+ "Invalid argument for '-o' option.");
+ }
+ break;
+ case 'q':
+ if (action != CREATE)
+ usage();
+ errno = 0;
+ queue_size = strtoul(optarg, NULL, 10);
+ if (queue_size == 0 && errno != 0)
+ errx(EXIT_FAILURE, "Invalid queue_size.");
+ break;
+ case 's':
+ if (action != CREATE)
+ usage();
+ errno = 0;
+ sectorsize = strtoul(optarg, NULL, 10);
+ if (sectorsize == 0 && errno != 0)
+ errx(EXIT_FAILURE, "Invalid sectorsize.");
+ break;
+ case 't':
+ if (action != CREATE)
+ usage();
+ errno = 0;
+ timeout = strtoul(optarg, NULL, 10);
+ if (timeout == 0 && errno != 0)
+ errx(EXIT_FAILURE, "Invalid timeout.");
+ break;
+ case 'u':
+ errno = 0;
+ unit = strtol(optarg, NULL, 10);
+ if (unit == 0 && errno != 0)
+ errx(EXIT_FAILURE, "Invalid unit number.");
+ break;
+ case 'v':
+ if (action == DESTROY)
+ usage();
+ g_gate_verbose++;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch (action) {
+ case CREATE:
+ if (argc != 1)
+ usage();
+ g_gate_load_module();
+ g_gate_open_device();
+ path = argv[0];
+ g_gatel_create();
+ break;
+ case RESCUE:
+ if (argc != 1)
+ usage();
+ if (unit == -1) {
+ fprintf(stderr, "Required unit number.\n");
+ usage();
+ }
+ g_gate_open_device();
+ path = argv[0];
+ g_gatel_rescue();
+ break;
+ case DESTROY:
+ if (unit == -1) {
+ fprintf(stderr, "Required unit number.\n");
+ usage();
+ }
+ g_gate_verbose = 1;
+ g_gate_open_device();
+ g_gate_destroy(unit, force);
+ break;
+ case LIST:
+ g_gate_list(unit, g_gate_verbose);
+ break;
+ case UNSET:
+ default:
+ usage();
+ }
+ g_gate_close_device();
+ exit(EXIT_SUCCESS);
+}
diff --git a/sbin/ggate/shared/ggate.c b/sbin/ggate/shared/ggate.c
new file mode 100644
index 0000000..c8428a6
--- /dev/null
+++ b/sbin/ggate/shared/ggate.c
@@ -0,0 +1,395 @@
+/*-
+ * Copyright (c) 2004 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/disk.h>
+#include <sys/stat.h>
+#include <sys/endian.h>
+#include <sys/socket.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <signal.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <libgen.h>
+#include <libutil.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <libgeom.h>
+
+#include <geom/gate/g_gate.h>
+#include "ggate.h"
+
+
+int g_gate_devfd = -1;
+int g_gate_verbose = 0;
+
+
+void
+g_gate_vlog(int priority, const char *message, va_list ap)
+{
+
+ if (g_gate_verbose) {
+ const char *prefix;
+
+ switch (priority) {
+ case LOG_ERR:
+ prefix = "error";
+ break;
+ case LOG_WARNING:
+ prefix = "warning";
+ break;
+ case LOG_NOTICE:
+ prefix = "notice";
+ break;
+ case LOG_INFO:
+ prefix = "info";
+ break;
+ case LOG_DEBUG:
+ prefix = "debug";
+ break;
+ default:
+ prefix = "unknown";
+ }
+
+ printf("%s: ", prefix);
+ vprintf(message, ap);
+ printf("\n");
+ } else {
+ if (priority != LOG_DEBUG)
+ vsyslog(priority, message, ap);
+ }
+}
+
+void
+g_gate_log(int priority, const char *message, ...)
+{
+ va_list ap;
+
+ va_start(ap, message);
+ g_gate_vlog(priority, message, ap);
+ va_end(ap);
+}
+
+void
+g_gate_xvlog(const char *message, va_list ap)
+{
+
+ g_gate_vlog(LOG_ERR, message, ap);
+ g_gate_vlog(LOG_ERR, "Exiting.", ap);
+ exit(EXIT_FAILURE);
+}
+
+void
+g_gate_xlog(const char *message, ...)
+{
+ va_list ap;
+
+ va_start(ap, message);
+ g_gate_xvlog(message, ap);
+ /* NOTREACHED */
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+off_t
+g_gate_mediasize(int fd)
+{
+ off_t mediasize;
+ struct stat sb;
+
+ if (fstat(fd, &sb) == -1)
+ g_gate_xlog("fstat(): %s.", strerror(errno));
+ if (S_ISCHR(sb.st_mode)) {
+ if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) == -1) {
+ g_gate_xlog("Can't get media size: %s.",
+ strerror(errno));
+ }
+ } else if (S_ISREG(sb.st_mode)) {
+ mediasize = sb.st_size;
+ } else {
+ g_gate_xlog("Unsupported file system object.");
+ }
+ return (mediasize);
+}
+
+size_t
+g_gate_sectorsize(int fd)
+{
+ size_t secsize;
+ struct stat sb;
+
+ if (fstat(fd, &sb) == -1)
+ g_gate_xlog("fstat(): %s.", strerror(errno));
+ if (S_ISCHR(sb.st_mode)) {
+ if (ioctl(fd, DIOCGSECTORSIZE, &secsize) == -1) {
+ g_gate_xlog("Can't get sector size: %s.",
+ strerror(errno));
+ }
+ } else if (S_ISREG(sb.st_mode)) {
+ secsize = 512;
+ } else {
+ g_gate_xlog("Unsupported file system object.");
+ }
+ return (secsize);
+}
+
+void
+g_gate_open_device(void)
+{
+
+ g_gate_devfd = open("/dev/" G_GATE_CTL_NAME, O_RDWR, 0);
+ if (g_gate_devfd == -1)
+ err(EXIT_FAILURE, "open(/dev/%s)", G_GATE_CTL_NAME);
+}
+
+void
+g_gate_close_device(void)
+{
+
+ close(g_gate_devfd);
+}
+
+void
+g_gate_ioctl(unsigned long req, void *data)
+{
+
+ if (ioctl(g_gate_devfd, req, data) == -1) {
+ g_gate_xlog("%s: ioctl(/dev/%s): %s.", getprogname(),
+ G_GATE_CTL_NAME, strerror(errno));
+ }
+}
+
+void
+g_gate_destroy(int unit, int force)
+{
+ struct g_gate_ctl_destroy ggio;
+
+ ggio.gctl_version = G_GATE_VERSION;
+ ggio.gctl_unit = unit;
+ ggio.gctl_force = force;
+ g_gate_ioctl(G_GATE_CMD_DESTROY, &ggio);
+}
+
+void
+g_gate_load_module(void)
+{
+
+ if (modfind("g_gate") == -1) {
+ /* Not present in kernel, try loading it. */
+ if (kldload("geom_gate") == -1 || modfind("g_gate") == -1) {
+ if (errno != EEXIST) {
+ errx(EXIT_FAILURE,
+ "geom_gate module not available!");
+ }
+ }
+ }
+}
+
+ssize_t
+g_gate_send(int s, const void *buf, size_t len, int flags)
+{
+ ssize_t done = 0, done2;
+ const unsigned char *p = buf;
+
+ while (len > 0) {
+ done2 = send(s, p, len, flags);
+ if (done2 == 0)
+ break;
+ else if (done2 == -1) {
+ if (errno == EAGAIN) {
+ printf("%s: EAGAIN\n", __func__);
+ continue;
+ }
+ done = -1;
+ break;
+ }
+ done += done2;
+ p += done2;
+ len -= done2;
+ }
+ return (done);
+}
+
+ssize_t
+g_gate_recv(int s, void *buf, size_t len, int flags)
+{
+
+ return (recv(s, buf, len, flags));
+}
+
+int nagle = 1;
+unsigned rcvbuf = G_GATE_RCVBUF;
+unsigned sndbuf = G_GATE_SNDBUF;
+
+void
+g_gate_socket_settings(int sfd)
+{
+ struct timeval tv;
+ int bsize, on;
+
+ /* Socket settings. */
+ on = 1;
+ if (nagle) {
+ if (setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, &on,
+ sizeof(on)) == -1) {
+ g_gate_xlog("setsockopt() error: %s.", strerror(errno));
+ }
+ }
+ if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
+ g_gate_xlog("setsockopt(SO_REUSEADDR): %s.", strerror(errno));
+ bsize = rcvbuf;
+ if (setsockopt(sfd, SOL_SOCKET, SO_RCVBUF, &bsize, sizeof(bsize)) == -1)
+ g_gate_xlog("setsockopt(SO_RCVBUF): %s.", strerror(errno));
+ bsize = sndbuf;
+ if (setsockopt(sfd, SOL_SOCKET, SO_SNDBUF, &bsize, sizeof(bsize)) == -1)
+ g_gate_xlog("setsockopt(SO_SNDBUF): %s.", strerror(errno));
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ if (setsockopt(sfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
+ g_gate_log(LOG_ERR, "setsockopt(SO_SNDTIMEO) error: %s.",
+ strerror(errno));
+ }
+ if (setsockopt(sfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
+ g_gate_log(LOG_ERR, "setsockopt(SO_RCVTIMEO) error: %s.",
+ strerror(errno));
+ }
+}
+
+#ifdef LIBGEOM
+static struct gclass *
+find_class(struct gmesh *mesh, const char *name)
+{
+ struct gclass *class;
+
+ LIST_FOREACH(class, &mesh->lg_class, lg_class) {
+ if (strcmp(class->lg_name, name) == 0)
+ return (class);
+ }
+ return (NULL);
+}
+
+static const char *
+get_conf(struct ggeom *gp, const char *name)
+{
+ struct gconfig *conf;
+
+ LIST_FOREACH(conf, &gp->lg_config, lg_config) {
+ if (strcmp(conf->lg_name, name) == 0)
+ return (conf->lg_val);
+ }
+ return (NULL);
+}
+
+static void
+show_config(struct ggeom *gp, int verbose)
+{
+ struct gprovider *pp;
+ char buf[5];
+
+ pp = LIST_FIRST(&gp->lg_provider);
+ if (pp == NULL)
+ return;
+ if (!verbose) {
+ printf("%s\n", pp->lg_name);
+ return;
+ }
+ printf(" NAME: %s\n", pp->lg_name);
+ printf(" info: %s\n", get_conf(gp, "info"));
+ printf(" access: %s\n", get_conf(gp, "access"));
+ printf(" timeout: %s\n", get_conf(gp, "timeout"));
+ printf("queue_count: %s\n", get_conf(gp, "queue_count"));
+ printf(" queue_size: %s\n", get_conf(gp, "queue_size"));
+ printf(" references: %s\n", get_conf(gp, "ref"));
+ humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "",
+ HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+ printf(" mediasize: %jd (%s)\n", (intmax_t)pp->lg_mediasize, buf);
+ printf(" sectorsize: %u\n", pp->lg_sectorsize);
+ printf(" mode: %s\n", pp->lg_mode);
+ printf("\n");
+}
+
+void
+g_gate_list(int unit, int verbose)
+{
+ struct gmesh mesh;
+ struct gclass *class;
+ struct ggeom *gp;
+ char name[64];
+ int error;
+
+ error = geom_gettree(&mesh);
+ if (error != 0)
+ exit(EXIT_FAILURE);
+ class = find_class(&mesh, G_GATE_CLASS_NAME);
+ if (class == NULL) {
+ geom_deletetree(&mesh);
+ exit(EXIT_SUCCESS);
+ }
+ if (unit >= 0) {
+ snprintf(name, sizeof(name), "%s%d", G_GATE_PROVIDER_NAME,
+ unit);
+ }
+ LIST_FOREACH(gp, &class->lg_geom, lg_geom) {
+ if (unit != -1 && strcmp(gp->lg_name, name) != 0)
+ continue;
+ show_config(gp, verbose);
+ }
+ geom_deletetree(&mesh);
+ exit(EXIT_SUCCESS);
+}
+#endif /* LIBGEOM */
+
+in_addr_t
+g_gate_str2ip(const char *str)
+{
+ struct hostent *hp;
+ in_addr_t ip;
+
+ ip = inet_addr(str);
+ if (ip != INADDR_NONE) {
+ /* It is a valid IP address. */
+ return (ip);
+ }
+ /* Check if it is a valid host name. */
+ hp = gethostbyname(str);
+ if (hp == NULL)
+ return (INADDR_NONE);
+ return (((struct in_addr *)(void *)hp->h_addr)->s_addr);
+}
diff --git a/sbin/ggate/shared/ggate.h b/sbin/ggate/shared/ggate.h
new file mode 100644
index 0000000..acbdaaa
--- /dev/null
+++ b/sbin/ggate/shared/ggate.h
@@ -0,0 +1,196 @@
+/*-
+ * Copyright (c) 2004 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _GGATE_H_
+#define _GGATE_H_
+
+#include <sys/endian.h>
+#include <stdarg.h>
+
+#define G_GATE_PORT 3080
+
+#define G_GATE_RCVBUF 131072
+#define G_GATE_SNDBUF 131072
+#define G_GATE_QUEUE_SIZE 1024
+#define G_GATE_TIMEOUT 0
+
+#define GGATE_MAGIC "GEOM_GATE "
+#define GGATE_VERSION 0
+
+#define GGATE_FLAG_RDONLY 0x0001
+#define GGATE_FLAG_WRONLY 0x0002
+/*
+ * If GGATE_FLAG_SEND not GGATE_FLAG_RECV flag is set, this is initial
+ * connection.
+ * If GGATE_FLAG_SEND flag is set - this is socket to send data.
+ * If GGATE_FLAG_RECV flag is set - this is socket to receive data.
+ */
+#define GGATE_FLAG_SEND 0x0004
+#define GGATE_FLAG_RECV 0x0008
+
+#define GGATE_CMD_READ 0
+#define GGATE_CMD_WRITE 1
+
+extern int g_gate_devfd;
+extern int g_gate_verbose;
+
+extern int nagle;
+extern unsigned rcvbuf, sndbuf;
+
+struct g_gate_version {
+ char gv_magic[16];
+ uint16_t gv_version;
+ uint16_t gv_error;
+} __packed;
+
+/* Client's initial packet. */
+struct g_gate_cinit {
+ char gc_path[PATH_MAX + 1];
+ uint64_t gc_flags;
+ uint16_t gc_nconn;
+ uint32_t gc_token;
+} __packed;
+
+/* Server's initial packet. */
+struct g_gate_sinit {
+ uint8_t gs_flags;
+ uint64_t gs_mediasize;
+ uint32_t gs_sectorsize;
+ uint16_t gs_error;
+} __packed;
+
+/* Control struct. */
+struct g_gate_hdr {
+ uint8_t gh_cmd; /* command */
+ uint64_t gh_offset; /* device offset */
+ uint32_t gh_length; /* size of block */
+ uint64_t gh_seq; /* request number */
+ uint16_t gh_error; /* error value (0 if ok) */
+} __packed;
+
+void g_gate_vlog(int priority, const char *message, va_list ap);
+void g_gate_log(int priority, const char *message, ...);
+void g_gate_xvlog(const char *message, va_list ap);
+void g_gate_xlog(const char *message, ...);
+off_t g_gate_mediasize(int fd);
+size_t g_gate_sectorsize(int fd);
+void g_gate_open_device(void);
+void g_gate_close_device(void);
+void g_gate_ioctl(unsigned long req, void *data);
+void g_gate_destroy(int unit, int force);
+void g_gate_load_module(void);
+ssize_t g_gate_recv(int s, void *buf, size_t len, int flags);
+ssize_t g_gate_send(int s, const void *buf, size_t len, int flags);
+void g_gate_socket_settings(int sfd);
+#ifdef LIBGEOM
+void g_gate_list(int unit, int verbose);
+#endif
+in_addr_t g_gate_str2ip(const char *str);
+
+/*
+ * g_gate_swap2h_* - functions swap bytes to host byte order (from big endian).
+ * g_gate_swap2n_* - functions swap bytes to network byte order (actually
+ * to big endian byte order).
+ */
+
+static __inline void
+g_gate_swap2h_version(struct g_gate_version *ver)
+{
+
+ ver->gv_version = be16toh(ver->gv_version);
+ ver->gv_error = be16toh(ver->gv_error);
+}
+
+static __inline void
+g_gate_swap2n_version(struct g_gate_version *ver)
+{
+
+ ver->gv_version = htobe16(ver->gv_version);
+ ver->gv_error = htobe16(ver->gv_error);
+}
+
+static __inline void
+g_gate_swap2h_cinit(struct g_gate_cinit *cinit)
+{
+
+ cinit->gc_flags = be64toh(cinit->gc_flags);
+ cinit->gc_nconn = be16toh(cinit->gc_nconn);
+ cinit->gc_token = be32toh(cinit->gc_token);
+}
+
+static __inline void
+g_gate_swap2n_cinit(struct g_gate_cinit *cinit)
+{
+
+ cinit->gc_flags = htobe64(cinit->gc_flags);
+ cinit->gc_nconn = htobe16(cinit->gc_nconn);
+ cinit->gc_token = htobe32(cinit->gc_token);
+}
+
+static __inline void
+g_gate_swap2h_sinit(struct g_gate_sinit *sinit)
+{
+
+ /* Swap only used fields. */
+ sinit->gs_mediasize = be64toh(sinit->gs_mediasize);
+ sinit->gs_sectorsize = be32toh(sinit->gs_sectorsize);
+ sinit->gs_error = be16toh(sinit->gs_error);
+}
+
+static __inline void
+g_gate_swap2n_sinit(struct g_gate_sinit *sinit)
+{
+
+ /* Swap only used fields. */
+ sinit->gs_mediasize = htobe64(sinit->gs_mediasize);
+ sinit->gs_sectorsize = htobe32(sinit->gs_sectorsize);
+ sinit->gs_error = htobe16(sinit->gs_error);
+}
+
+static __inline void
+g_gate_swap2h_hdr(struct g_gate_hdr *hdr)
+{
+
+ /* Swap only used fields. */
+ hdr->gh_offset = be64toh(hdr->gh_offset);
+ hdr->gh_length = be32toh(hdr->gh_length);
+ hdr->gh_seq = be64toh(hdr->gh_seq);
+ hdr->gh_error = be16toh(hdr->gh_error);
+}
+
+static __inline void
+g_gate_swap2n_hdr(struct g_gate_hdr *hdr)
+{
+
+ /* Swap only used fields. */
+ hdr->gh_offset = htobe64(hdr->gh_offset);
+ hdr->gh_length = htobe32(hdr->gh_length);
+ hdr->gh_seq = htobe64(hdr->gh_seq);
+ hdr->gh_error = htobe16(hdr->gh_error);
+}
+#endif /* _GGATE_H_ */
diff --git a/sbin/gpt/Makefile b/sbin/gpt/Makefile
new file mode 100644
index 0000000..cfae78d
--- /dev/null
+++ b/sbin/gpt/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= gpt
+SRCS= add.c create.c destroy.c gpt.c label.c map.c migrate.c recover.c \
+ remove.c show.c
+WARNS?= 4
+MAN= gpt.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/gpt/add.c b/sbin/gpt/add.c
new file mode 100644
index 0000000..3b60720
--- /dev/null
+++ b/sbin/gpt/add.c
@@ -0,0 +1,234 @@
+/*-
+ * Copyright (c) 2002 Marcel Moolenaar
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "map.h"
+#include "gpt.h"
+
+static uuid_t type;
+static off_t block, size;
+static unsigned int entry;
+
+static void
+usage_add(void)
+{
+
+ fprintf(stderr,
+ "usage: %s [-b lba] [-i index] [-s lba] [-t uuid] device ...\n",
+ getprogname());
+ exit(1);
+}
+
+static void
+add(int fd)
+{
+ map_t *gpt, *tpg;
+ map_t *tbl, *lbt;
+ map_t *map;
+ struct gpt_hdr *hdr;
+ struct gpt_ent *ent;
+ unsigned int i;
+
+ gpt = map_find(MAP_TYPE_PRI_GPT_HDR);
+ if (gpt == NULL) {
+ warnx("%s: error: no primary GPT header; run create or recover",
+ device_name);
+ return;
+ }
+
+ tpg = map_find(MAP_TYPE_SEC_GPT_HDR);
+ if (tpg == NULL) {
+ warnx("%s: error: no secondary GPT header; run recover",
+ device_name);
+ return;
+ }
+
+ tbl = map_find(MAP_TYPE_PRI_GPT_TBL);
+ lbt = map_find(MAP_TYPE_SEC_GPT_TBL);
+ if (tbl == NULL || lbt == NULL) {
+ warnx("%s: error: run recover -- trust me", device_name);
+ return;
+ }
+
+ hdr = gpt->map_data;
+ if (entry > le32toh(hdr->hdr_entries)) {
+ warnx("%s: error: index %u out of range (%u max)", device_name,
+ entry, le32toh(hdr->hdr_entries));
+ return;
+ }
+
+ if (entry > 0) {
+ i = entry - 1;
+ ent = (void*)((char*)tbl->map_data + i *
+ le32toh(hdr->hdr_entsz));
+ if (!uuid_is_nil(&ent->ent_type, NULL)) {
+ warnx("%s: error: entry at index %u is not free",
+ device_name, entry);
+ return;
+ }
+ } else {
+ /* Find empty slot in GPT table. */
+ for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
+ ent = (void*)((char*)tbl->map_data + i *
+ le32toh(hdr->hdr_entsz));
+ if (uuid_is_nil(&ent->ent_type, NULL))
+ break;
+ }
+ if (i == le32toh(hdr->hdr_entries)) {
+ warnx("%s: error: no available table entries",
+ device_name);
+ return;
+ }
+ }
+
+ map = map_alloc(block, size);
+ if (map == NULL) {
+ warnx("%s: error: no space available on device", device_name);
+ return;
+ }
+
+ le_uuid_enc(&ent->ent_type, &type);
+ ent->ent_lba_start = htole64(map->map_start);
+ ent->ent_lba_end = htole64(map->map_start + map->map_size - 1LL);
+
+ hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
+ le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
+ hdr->hdr_crc_self = 0;
+ hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
+
+ gpt_write(fd, gpt);
+ gpt_write(fd, tbl);
+
+ hdr = tpg->map_data;
+ ent = (void*)((char*)lbt->map_data + i * le32toh(hdr->hdr_entsz));
+
+ le_uuid_enc(&ent->ent_type, &type);
+ ent->ent_lba_start = htole64(map->map_start);
+ ent->ent_lba_end = htole64(map->map_start + map->map_size - 1LL);
+
+ hdr->hdr_crc_table = htole32(crc32(lbt->map_data,
+ le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
+ hdr->hdr_crc_self = 0;
+ hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
+
+ gpt_write(fd, lbt);
+ gpt_write(fd, tpg);
+
+ printf("%sp%u added\n", device_name, i + 1);
+}
+
+int
+cmd_add(int argc, char *argv[])
+{
+ char *p;
+ int ch, fd;
+ uint32_t status;
+
+ /* Get the migrate options */
+ while ((ch = getopt(argc, argv, "b:i:s:t:")) != -1) {
+ switch(ch) {
+ case 'b':
+ if (block > 0)
+ usage_add();
+ block = strtol(optarg, &p, 10);
+ if (*p != 0 || block < 1)
+ usage_add();
+ break;
+ case 'i':
+ if (entry > 0)
+ usage_add();
+ entry = strtol(optarg, &p, 10);
+ if (*p != 0 || entry < 1)
+ usage_add();
+ break;
+ case 's':
+ if (size > 0)
+ usage_add();
+ size = strtol(optarg, &p, 10);
+ if (*p != 0 || size < 1)
+ usage_add();
+ break;
+ case 't':
+ if (!uuid_is_nil(&type, NULL))
+ usage_add();
+ uuid_from_string(optarg, &type, &status);
+ if (status != uuid_s_ok) {
+ if (strcmp(optarg, "efi") == 0) {
+ uuid_t efi = GPT_ENT_TYPE_EFI;
+ type = efi;
+ } else if (strcmp(optarg, "swap") == 0) {
+ uuid_t sw = GPT_ENT_TYPE_FREEBSD_SWAP;
+ type = sw;
+ } else if (strcmp(optarg, "ufs") == 0) {
+ uuid_t ufs = GPT_ENT_TYPE_FREEBSD_UFS;
+ type = ufs;
+ } else if (strcmp(optarg, "linux") == 0 ||
+ strcmp(optarg, "windows") == 0) {
+ uuid_t m = GPT_ENT_TYPE_MS_BASIC_DATA;
+ type = m;
+ } else
+ usage_add();
+ }
+ break;
+ default:
+ usage_add();
+ }
+ }
+
+ if (argc == optind)
+ usage_add();
+
+ /* Create UFS partitions by default. */
+ if (uuid_is_nil(&type, NULL)) {
+ uuid_t ufs = GPT_ENT_TYPE_FREEBSD_UFS;
+ type = ufs;
+ }
+
+ while (optind < argc) {
+ fd = gpt_open(argv[optind++]);
+ if (fd == -1) {
+ warn("unable to open device '%s'", device_name);
+ continue;
+ }
+
+ add(fd);
+
+ gpt_close(fd);
+ }
+
+ return (0);
+}
diff --git a/sbin/gpt/create.c b/sbin/gpt/create.c
new file mode 100644
index 0000000..24a6a6d
--- /dev/null
+++ b/sbin/gpt/create.c
@@ -0,0 +1,245 @@
+/*-
+ * Copyright (c) 2002 Marcel Moolenaar
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "map.h"
+#include "gpt.h"
+
+static int force;
+static int primary_only;
+
+static void
+usage_create(void)
+{
+
+ fprintf(stderr,
+ "usage: %s [-fp] device ...\n", getprogname());
+ exit(1);
+}
+
+static void
+create(int fd)
+{
+ uuid_t uuid;
+ off_t blocks, last;
+ map_t *gpt, *tpg;
+ map_t *tbl, *lbt;
+ map_t *map;
+ struct mbr *mbr;
+ struct gpt_hdr *hdr;
+ struct gpt_ent *ent;
+ unsigned int i;
+
+ last = mediasz / secsz - 1LL;
+
+ if (map_find(MAP_TYPE_PRI_GPT_HDR) != NULL ||
+ map_find(MAP_TYPE_SEC_GPT_HDR) != NULL) {
+ warnx("%s: error: device already contains a GPT", device_name);
+ return;
+ }
+ map = map_find(MAP_TYPE_MBR);
+ if (map != NULL) {
+ if (!force) {
+ warnx("%s: error: device contains a MBR", device_name);
+ return;
+ }
+
+ /* Nuke the MBR in our internal map. */
+ map->map_type = MAP_TYPE_UNUSED;
+ }
+
+ /*
+ * Create PMBR.
+ */
+ if (map_find(MAP_TYPE_PMBR) == NULL) {
+ if (map_free(0LL, 1LL) == 0) {
+ warnx("%s: error: no room for the PMBR", device_name);
+ return;
+ }
+ mbr = gpt_read(fd, 0LL, 1);
+ bzero(mbr, sizeof(*mbr));
+ mbr->mbr_sig = htole16(MBR_SIG);
+ mbr->mbr_part[0].part_shd = 0xff;
+ mbr->mbr_part[0].part_ssect = 0xff;
+ mbr->mbr_part[0].part_scyl = 0xff;
+ mbr->mbr_part[0].part_typ = 0xee;
+ mbr->mbr_part[0].part_ehd = 0xff;
+ mbr->mbr_part[0].part_esect = 0xff;
+ mbr->mbr_part[0].part_ecyl = 0xff;
+ mbr->mbr_part[0].part_start_lo = htole16(1);
+ if (last > 0xffffffff) {
+ mbr->mbr_part[0].part_size_lo = htole16(0xffff);
+ mbr->mbr_part[0].part_size_hi = htole16(0xffff);
+ } else {
+ mbr->mbr_part[0].part_size_lo = htole16(last);
+ mbr->mbr_part[0].part_size_hi = htole16(last >> 16);
+ }
+ map = map_add(0LL, 1LL, MAP_TYPE_PMBR, mbr);
+ gpt_write(fd, map);
+ }
+
+ /* Get the amount of free space after the MBR */
+ blocks = map_free(1LL, 0LL);
+ if (blocks == 0LL) {
+ warnx("%s: error: no room for the GPT header", device_name);
+ return;
+ }
+
+ /* Don't create more than parts entries. */
+ if ((uint64_t)(blocks - 1) * secsz > parts * sizeof(struct gpt_ent)) {
+ blocks = (parts * sizeof(struct gpt_ent)) / secsz;
+ if ((parts * sizeof(struct gpt_ent)) % secsz)
+ blocks++;
+ blocks++; /* Don't forget the header itself */
+ }
+
+ /* Never cross the median of the device. */
+ if ((blocks + 1LL) > ((last + 1LL) >> 1))
+ blocks = ((last + 1LL) >> 1) - 1LL;
+
+ /*
+ * Get the amount of free space at the end of the device and
+ * calculate the size for the GPT structures.
+ */
+ map = map_last();
+ if (map->map_type != MAP_TYPE_UNUSED) {
+ warnx("%s: error: no room for the backup header", device_name);
+ return;
+ }
+
+ if (map->map_size < blocks)
+ blocks = map->map_size;
+ if (blocks == 1LL) {
+ warnx("%s: error: no room for the GPT table", device_name);
+ return;
+ }
+
+ blocks--; /* Number of blocks in the GPT table. */
+ gpt = map_add(1LL, 1LL, MAP_TYPE_PRI_GPT_HDR, calloc(1, secsz));
+ tbl = map_add(2LL, blocks, MAP_TYPE_PRI_GPT_TBL,
+ calloc(blocks, secsz));
+ if (gpt == NULL || tbl == NULL)
+ return;
+
+ hdr = gpt->map_data;
+ memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
+ hdr->hdr_revision = htole32(GPT_HDR_REVISION);
+ /*
+ * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
+ * contains padding we must not include in the size.
+ */
+ hdr->hdr_size = htole32(offsetof(struct gpt_hdr, padding));
+ hdr->hdr_lba_self = htole64(gpt->map_start);
+ hdr->hdr_lba_alt = htole64(last);
+ hdr->hdr_lba_start = htole64(tbl->map_start + blocks);
+ hdr->hdr_lba_end = htole64(last - blocks - 1LL);
+ uuid_create(&uuid, NULL);
+ le_uuid_enc(&hdr->hdr_uuid, &uuid);
+ hdr->hdr_lba_table = htole64(tbl->map_start);
+ hdr->hdr_entries = htole32((blocks * secsz) / sizeof(struct gpt_ent));
+ if (le32toh(hdr->hdr_entries) > parts)
+ hdr->hdr_entries = htole32(parts);
+ hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
+
+ ent = tbl->map_data;
+ for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
+ uuid_create(&uuid, NULL);
+ le_uuid_enc(&ent[i].ent_uuid, &uuid);
+ }
+
+ hdr->hdr_crc_table = htole32(crc32(ent, le32toh(hdr->hdr_entries) *
+ le32toh(hdr->hdr_entsz)));
+ hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
+
+ gpt_write(fd, gpt);
+ gpt_write(fd, tbl);
+
+ /*
+ * Create backup GPT if the user didn't suppress it.
+ */
+ if (!primary_only) {
+ tpg = map_add(last, 1LL, MAP_TYPE_SEC_GPT_HDR,
+ calloc(1, secsz));
+ lbt = map_add(last - blocks, blocks, MAP_TYPE_SEC_GPT_TBL,
+ tbl->map_data);
+ memcpy(tpg->map_data, gpt->map_data, secsz);
+ hdr = tpg->map_data;
+ hdr->hdr_lba_self = htole64(tpg->map_start);
+ hdr->hdr_lba_alt = htole64(gpt->map_start);
+ hdr->hdr_lba_table = htole64(lbt->map_start);
+ hdr->hdr_crc_self = 0; /* Don't ever forget this! */
+ hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
+ gpt_write(fd, lbt);
+ gpt_write(fd, tpg);
+ }
+}
+
+int
+cmd_create(int argc, char *argv[])
+{
+ int ch, fd;
+
+ while ((ch = getopt(argc, argv, "fp")) != -1) {
+ switch(ch) {
+ case 'f':
+ force = 1;
+ break;
+ case 'p':
+ primary_only = 1;
+ break;
+ default:
+ usage_create();
+ }
+ }
+
+ if (argc == optind)
+ usage_create();
+
+ while (optind < argc) {
+ fd = gpt_open(argv[optind++]);
+ if (fd == -1) {
+ warn("unable to open device '%s'", device_name);
+ continue;
+ }
+
+ create(fd);
+
+ gpt_close(fd);
+ }
+
+ return (0);
+}
diff --git a/sbin/gpt/destroy.c b/sbin/gpt/destroy.c
new file mode 100644
index 0000000..7c2c198
--- /dev/null
+++ b/sbin/gpt/destroy.c
@@ -0,0 +1,113 @@
+/*-
+ * Copyright (c) 2002 Marcel Moolenaar
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "map.h"
+#include "gpt.h"
+
+static int recoverable;
+
+static void
+usage_destroy(void)
+{
+
+ fprintf(stderr,
+ "usage: %s [-r] device ...\n", getprogname());
+ exit(1);
+}
+
+static void
+destroy(int fd)
+{
+ map_t *pri_hdr, *sec_hdr;
+
+ pri_hdr = map_find(MAP_TYPE_PRI_GPT_HDR);
+ sec_hdr = map_find(MAP_TYPE_SEC_GPT_HDR);
+
+ if (pri_hdr == NULL && sec_hdr == NULL) {
+ warnx("%s: error: device doesn't contain a GPT", device_name);
+ return;
+ }
+
+ if (recoverable && sec_hdr == NULL) {
+ warnx("%s: error: recoverability not possible", device_name);
+ return;
+ }
+
+ if (pri_hdr != NULL) {
+ bzero(pri_hdr->map_data, secsz);
+ gpt_write(fd, pri_hdr);
+ }
+
+ if (!recoverable && sec_hdr != NULL) {
+ bzero(sec_hdr->map_data, secsz);
+ gpt_write(fd, sec_hdr);
+ }
+}
+
+int
+cmd_destroy(int argc, char *argv[])
+{
+ int ch, fd;
+
+ while ((ch = getopt(argc, argv, "r")) != -1) {
+ switch(ch) {
+ case 'r':
+ recoverable = 1;
+ break;
+ default:
+ usage_destroy();
+ }
+ }
+
+ if (argc == optind)
+ usage_destroy();
+
+ while (optind < argc) {
+ fd = gpt_open(argv[optind++]);
+ if (fd == -1) {
+ warn("unable to open device '%s'", device_name);
+ continue;
+ }
+
+ destroy(fd);
+
+ gpt_close(fd);
+ }
+
+ return (0);
+}
diff --git a/sbin/gpt/gpt.8 b/sbin/gpt/gpt.8
new file mode 100644
index 0000000..ab2d87c
--- /dev/null
+++ b/sbin/gpt/gpt.8
@@ -0,0 +1,335 @@
+.\" Copyright (c) 2002 Marcel Moolenaar
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 31, 2005
+.Os
+.Dt GPT 8
+.Sh NAME
+.Nm gpt
+.Nd "GUID partition table maintenance utility"
+.Sh SYNOPSIS
+.Nm
+.Op Ar general_options
+.Ar command
+.Op Ar command_options
+.Ar device ...
+.Sh DESCRIPTION
+The
+.Nm
+utility provides the necessary functionality to manipulate GUID partition
+tables (GPTs), but see
+.Sx BUGS
+below for how and where functionality is missing.
+The basic usage model of the
+.Nm
+tool follows that of the
+.Xr cvs 1
+tool.
+The general options are described in the following paragraph.
+The remaining paragraphs describe the individual commands with their options.
+Here we conclude by mentioning that a
+.Ar device
+is either a special file
+corresponding to a disk-like device or a regular file.
+The command is applied to each
+.Ar device
+listed on the command line.
+.Ss General Options
+The general options allow the user to change default settings or otherwise
+change the behaviour that is applicable to all commands.
+Not all commands use all default settings, so some general options may not
+have an effect on all commands.
+.Pp
+The
+.Fl p Ar count
+option allows the user to change the number of partitions the GPT can
+accomodate.
+This is used whenever a new GPT is created.
+By default, the
+.Nm
+utility will create space for 128 partitions (or 32 sectors of 512 bytes).
+.Pp
+The
+.Fl r
+option causes the
+.Nm
+utility to open the device for reading only.
+Currently this option is primarily useful for the
+.Ic show
+command, but the intent
+is to use it to implement dry-run behaviour.
+.Pp
+The
+.Fl v
+option controls the verbosity level.
+The level increases with every occurrence of this option.
+There is no formalized definition of the different levels yet.
+.Ss Commands
+.Bl -tag -width indent
+.\" ==== add ====
+.It Xo
+.Nm
+.Ic add
+.Op Fl b Ar number
+.Op Fl i Ar index
+.Op Fl s Ar count
+.Op Fl t Ar type
+.Ar device ...
+.Xc
+The
+.Ic add
+command allows the user to add a new partition to an existing table.
+By default, it will create a UFS partition covering the first available block
+of an unused disk space.
+The command-specific options can be used to control this behaviour.
+.Pp
+The
+.Fl b Ar number
+option allows the user to specify the starting (beginning) sector number of
+the partition.
+The minimum sector number is 1, but has to fall inside an unused region of
+disk space that is covered by the GPT.
+.Pp
+The
+.Fl i Ar index
+option allows the user to specify which (free) entry in the GPT table is to
+be used for the new partition.
+By default, the first free entry is selected.
+.Pp
+The
+.Fl s Ar count
+option allows the user to specify the size of the partition in sectors.
+The minimum size is 1.
+.Pp
+The
+.Fl t Ar type
+option allows the user to specify the partition type.
+The type is given as an UUID, but
+.Nm
+accepts
+.Cm efi , swap , ufs , linux
+and
+.Cm windows
+as aliases for the most commonly used partition types.
+.\" ==== create ====
+.It Nm Ic create Oo Fl fp Oc Ar device ...
+The
+.Ic create
+command allows the user to create a new (empty) GPT.
+By default, one cannot create a GPT when the device contains a MBR,
+however this can be overridden with the
+.Fl f
+option.
+If the
+.Fl f
+option is specified, an existing MBR is destroyed and any partitions
+described by the MBR are lost.
+.Pp
+The
+.Fl p
+option tells
+.Nm
+to create only the primary table and not the backup table.
+This option is only useful for debugging and should not be used otherwise.
+.\" ==== destroy ====
+.It Nm Ic destroy Oo Fl r Oc Ar device ...
+The
+.Ic destroy
+command allows the user to destroy an existing, possibly not empty GPT.
+.Pp
+The
+.Fl r
+option instructs
+.Nm
+to destroy the table in a way that it can be recovered.
+.\" ==== label ====
+.It Xo
+.Nm
+.Ic label
+.Op Fl a
+.Aq Fl f Ar file | Fl l Ar label
+.Ar device ...
+.Xc
+.It Xo
+.Nm
+.Ic label
+.Op Fl b Ar number
+.Op Fl i Ar index
+.Op Fl s Ar count
+.Op Fl t Ar type
+.Aq Fl f Ar file | Fl l Ar label
+.Ar device ...
+.Xc
+The
+.Ic label
+command allows the user to label any partitions that match the selection.
+At least one of the following selection options must be specified.
+.Pp
+The
+.Fl a
+option specifies that all partitions should be labeled.
+It is mutually exclusive with all other selection options.
+.Pp
+The
+.Fl b Ar number
+option selects the partition that starts at the given block number.
+.Pp
+The
+.Fl i Ar index
+option selects the partition with the given partition number.
+.Pp
+The
+.Fl s Ar count
+option selects all partitions that have the given size.
+This can cause multiple partitions to be removed.
+.Pp
+The
+.Fl t Ar type
+option selects all partitions that have the given type.
+The type is given as an UUID or by the aliases that the
+.Ic add
+command accepts.
+This can cause multiple partitions to be removed.
+.Pp
+The
+.Fl f Ar file
+or
+.Fl l Ar label
+options specify the new label to be assigned to the selected partitions.
+The
+.Fl f Ar file
+option is used to read the label from the specified file.
+Only the first line is read from the file and the trailing newline
+character is stripped.
+If the file name is the dash or minus sign
+.Pq Fl ,
+the label is read from
+the standard input.
+The
+.Fl l Ar label
+option is used to specify the label in the command line.
+The label is assumed to be encoded in UTF-8.
+.\" ==== migrate ====
+.It Nm Ic migrate Oo Fl fs Oc Ar device ...
+The
+.Ic migrate
+command allows the user to migrate an MBR-based disk partitioning into a
+GPT-based partitioning.
+By default, the MBR is not migrated when it contains partitions of an unknown
+type.
+This can be overridden with the
+.Fl f
+option.
+Specifying the
+.Fl f
+option will cause unknown partitions to be ignored and any data in it
+to be lost.
+.Pp
+The
+.Fl s
+option prevents migrating
+.Bx
+disk labels into GPT partitions by creating
+the GPT equivalent of a slice.
+.\" ==== remove ====
+.It Nm Ic remove Oo Fl a Oc Ar device ...
+.It Xo
+.Nm
+.Ic remove
+.Op Fl b Ar number
+.Op Fl i Ar index
+.Op Fl s Ar count
+.Op Fl t Ar type
+.Ar device ...
+.Xc
+The
+.Ic remove
+command allows the user to remove any and all partitions that match the
+selection.
+It uses the same selection options as the
+.Ic label
+command.
+See above for a description of these options.
+Partitions are removed by clearing the partition type.
+No other information is changed.
+.\" ==== show ====
+.It Nm Ic show Oo Fl lu Oc Ar device ...
+The
+.Ic show
+command displays the current partitioning on the listed devices and gives
+an overall view of the disk contents.
+With the
+.Fl l
+option the GPT partition label will be displayed instead of the GPT partition
+type.
+The option has no effect on non-GPT partitions.
+With the
+.Fl u
+option the GPT partition type is displayed as an UUID instead of in a
+user friendly form.
+The
+.Fl l
+option takes precedence over the
+.Fl u
+option.
+.El
+.Sh SEE ALSO
+.Xr fdisk 8 ,
+.Xr mount 8 ,
+.Xr newfs 8 ,
+.Xr swapon 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 5.0
+for ia64.
+.Sh BUGS
+The development of the
+.Nm
+utility is still work in progress.
+Many necessary features are missing or partially implemented.
+In practice this means that the manual page, supposed to describe these
+features, is farther removed from being complete or useful.
+As such, missing functionality is not even documented as missing.
+However, it is believed that the currently present functionality is reliable
+and stable enough that this tool can be used without bullet-proof footware if
+one thinks one does not make mistakes.
+.Pp
+It is expected that the basic usage model does not change, but it is
+possible that future versions will not be compatible in the strictest sense
+of the word.
+For example, the
+.Fl p Ar count
+option may be changed to a command option rather than a generic option.
+There are only two commands that use it so there is a chance that the natural
+tendency for people is to use it as a command option.
+Also, options primarily intended for diagnostic or debug purposes may be
+removed in future versions.
+.Pp
+Another possibility is that the current usage model is accompanied by
+other interfaces to make the tool usable as a back-end.
+This all depends on demand and thus feedback.
diff --git a/sbin/gpt/gpt.c b/sbin/gpt/gpt.c
new file mode 100644
index 0000000..79b2893
--- /dev/null
+++ b/sbin/gpt/gpt.c
@@ -0,0 +1,635 @@
+/*-
+ * Copyright (c) 2002 Marcel Moolenaar
+ * 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.
+ *
+ * CRC32 code derived from work by Gary S. Brown.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/disk.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "map.h"
+#include "gpt.h"
+
+char device_path[MAXPATHLEN];
+char *device_name;
+
+off_t mediasz;
+
+u_int parts;
+u_int secsz;
+
+int readonly, verbose;
+
+static uint32_t crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+uint32_t
+crc32(const void *buf, size_t size)
+{
+ const uint8_t *p;
+ uint32_t crc;
+
+ p = buf;
+ crc = ~0U;
+
+ while (size--)
+ crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+
+ return crc ^ ~0U;
+}
+
+uint8_t *
+utf16_to_utf8(uint16_t *s16)
+{
+ static uint8_t *s8 = NULL;
+ static size_t s8len = 0;
+ size_t s8idx, s16idx, s16len;
+ uint32_t utfchar;
+ unsigned int c;
+
+ s16len = 0;
+ while (s16[s16len++] != 0)
+ ;
+ if (s8len < s16len * 3) {
+ if (s8 != NULL)
+ free(s8);
+ s8len = s16len * 3;
+ s8 = calloc(s16len, 3);
+ }
+ s8idx = s16idx = 0;
+ while (s16idx < s16len) {
+ utfchar = le16toh(s16[s16idx++]);
+ if ((utfchar & 0xf800) == 0xd800) {
+ c = le16toh(s16[s16idx]);
+ if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00)
+ utfchar = 0xfffd;
+ else
+ s16idx++;
+ }
+ if (utfchar < 0x80) {
+ s8[s8idx++] = utfchar;
+ } else if (utfchar < 0x800) {
+ s8[s8idx++] = 0xc0 | (utfchar >> 6);
+ s8[s8idx++] = 0x80 | (utfchar & 0x3f);
+ } else if (utfchar < 0x10000) {
+ s8[s8idx++] = 0xe0 | (utfchar >> 12);
+ s8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f);
+ s8[s8idx++] = 0x80 | (utfchar & 0x3f);
+ } else if (utfchar < 0x200000) {
+ s8[s8idx++] = 0xf0 | (utfchar >> 18);
+ s8[s8idx++] = 0x80 | ((utfchar >> 12) & 0x3f);
+ s8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f);
+ s8[s8idx++] = 0x80 | (utfchar & 0x3f);
+ }
+ }
+ return (s8);
+}
+
+void
+utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len)
+{
+ size_t s16idx, s8idx, s8len;
+ uint32_t utfchar;
+ unsigned int c, utfbytes;
+
+ s8len = 0;
+ while (s8[s8len++] != 0)
+ ;
+ s8idx = s16idx = 0;
+ utfbytes = 0;
+ do {
+ c = s8[s8idx++];
+ if ((c & 0xc0) != 0x80) {
+ /* Initial characters. */
+ if (utfbytes != 0) {
+ /* Incomplete encoding. */
+ s16[s16idx++] = 0xfffd;
+ if (s16idx == s16len) {
+ s16[--s16idx] = 0;
+ return;
+ }
+ }
+ if ((c & 0xf8) == 0xf0) {
+ utfchar = c & 0x07;
+ utfbytes = 3;
+ } else if ((c & 0xf0) == 0xe0) {
+ utfchar = c & 0x0f;
+ utfbytes = 2;
+ } else if ((c & 0xe0) == 0xc0) {
+ utfchar = c & 0x1f;
+ utfbytes = 1;
+ } else {
+ utfchar = c & 0x7f;
+ utfbytes = 0;
+ }
+ } else {
+ /* Followup characters. */
+ if (utfbytes > 0) {
+ utfchar = (utfchar << 6) + (c & 0x3f);
+ utfbytes--;
+ } else if (utfbytes == 0)
+ utfbytes = -1;
+ }
+ if (utfbytes == 0) {
+ if (utfchar >= 0x10000 && s16idx + 2 >= s16len)
+ utfchar = 0xfffd;
+ if (utfchar >= 0x10000) {
+ s16[s16idx++] = 0xd800 | ((utfchar>>10)-0x40);
+ s16[s16idx++] = 0xdc00 | (utfchar & 0x3ff);
+ } else
+ s16[s16idx++] = utfchar;
+ if (s16idx == s16len) {
+ s16[--s16idx] = 0;
+ return;
+ }
+ }
+ } while (c != 0);
+}
+
+void
+le_uuid_dec(void const *buf, uuid_t *uuid)
+{
+ u_char const *p;
+ int i;
+
+ p = buf;
+ uuid->time_low = le32dec(p);
+ uuid->time_mid = le16dec(p + 4);
+ uuid->time_hi_and_version = le16dec(p + 6);
+ uuid->clock_seq_hi_and_reserved = p[8];
+ uuid->clock_seq_low = p[9];
+ for (i = 0; i < _UUID_NODE_LEN; i++)
+ uuid->node[i] = p[10 + i];
+}
+
+void
+le_uuid_enc(void *buf, uuid_t const *uuid)
+{
+ u_char *p;
+ int i;
+
+ p = buf;
+ le32enc(p, uuid->time_low);
+ le16enc(p + 4, uuid->time_mid);
+ le16enc(p + 6, uuid->time_hi_and_version);
+ p[8] = uuid->clock_seq_hi_and_reserved;
+ p[9] = uuid->clock_seq_low;
+ for (i = 0; i < _UUID_NODE_LEN; i++)
+ p[10 + i] = uuid->node[i];
+}
+
+void*
+gpt_read(int fd, off_t lba, size_t count)
+{
+ off_t ofs;
+ void *buf;
+
+ count *= secsz;
+ buf = malloc(count);
+ if (buf == NULL)
+ return (NULL);
+
+ ofs = lba * secsz;
+ if (lseek(fd, ofs, SEEK_SET) == ofs &&
+ read(fd, buf, count) == (ssize_t)count)
+ return (buf);
+
+ free(buf);
+ return (NULL);
+}
+
+int
+gpt_write(int fd, map_t *map)
+{
+ off_t ofs;
+ size_t count;
+
+ count = map->map_size * secsz;
+ ofs = map->map_start * secsz;
+ if (lseek(fd, ofs, SEEK_SET) == ofs &&
+ write(fd, map->map_data, count) == (ssize_t)count)
+ return (0);
+ return (-1);
+}
+
+static int
+gpt_mbr(int fd, off_t lba)
+{
+ struct mbr *mbr;
+ map_t *m, *p;
+ off_t size, start;
+ unsigned int i, pmbr;
+
+ mbr = gpt_read(fd, lba, 1);
+ if (mbr == NULL)
+ return (-1);
+
+ if (mbr->mbr_sig != htole16(MBR_SIG)) {
+ if (verbose)
+ warnx("%s: MBR not found at sector %llu", device_name,
+ (long long)lba);
+ free(mbr);
+ return (0);
+ }
+
+ /*
+ * Differentiate between a regular MBR and a PMBR. This is more
+ * convenient in general. A PMBR is one with a single partition
+ * of type 0xee.
+ */
+ pmbr = 0;
+ for (i = 0; i < 4; i++) {
+ if (mbr->mbr_part[i].part_typ == 0)
+ continue;
+ if (mbr->mbr_part[i].part_typ == 0xee)
+ pmbr++;
+ else
+ break;
+ }
+ if (pmbr && i == 4 && lba == 0) {
+ if (pmbr != 1)
+ warnx("%s: Suspicious PMBR at sector %llu",
+ device_name, (long long)lba);
+ else if (verbose > 1)
+ warnx("%s: PMBR at sector %llu", device_name,
+ (long long)lba);
+ p = map_add(lba, 1LL, MAP_TYPE_PMBR, mbr);
+ return ((p == NULL) ? -1 : 0);
+ }
+ if (pmbr)
+ warnx("%s: Suspicious MBR at sector %llu", device_name,
+ (long long)lba);
+ else if (verbose > 1)
+ warnx("%s: MBR at sector %llu", device_name, (long long)lba);
+
+ p = map_add(lba, 1LL, MAP_TYPE_MBR, mbr);
+ if (p == NULL)
+ return (-1);
+ for (i = 0; i < 4; i++) {
+ if (mbr->mbr_part[i].part_typ == 0 ||
+ mbr->mbr_part[i].part_typ == 0xee)
+ continue;
+ start = le16toh(mbr->mbr_part[i].part_start_hi);
+ start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
+ size = le16toh(mbr->mbr_part[i].part_size_hi);
+ size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
+ if (start == 0 && size == 0) {
+ warnx("%s: Malformed MBR at sector %llu", device_name,
+ (long long)lba);
+ continue;
+ }
+ /* start is relative to the offset of the MBR itself. */
+ start += lba;
+ if (verbose > 2)
+ warnx("%s: MBR part: type=%d, start=%llu, size=%llu",
+ device_name, mbr->mbr_part[i].part_typ,
+ (long long)start, (long long)size);
+ if (mbr->mbr_part[i].part_typ != 15) {
+ m = map_add(start, size, MAP_TYPE_MBR_PART, p);
+ if (m == NULL)
+ return (-1);
+ m->map_index = i + 1;
+ } else {
+ if (gpt_mbr(fd, start) == -1)
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+static int
+gpt_gpt(int fd, off_t lba)
+{
+ uuid_t type;
+ off_t size;
+ struct gpt_ent *ent;
+ struct gpt_hdr *hdr;
+ char *p, *s;
+ map_t *m;
+ size_t blocks, tblsz;
+ unsigned int i;
+ uint32_t crc;
+
+ hdr = gpt_read(fd, lba, 1);
+ if (hdr == NULL)
+ return (-1);
+
+ if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
+ goto fail_hdr;
+
+ crc = le32toh(hdr->hdr_crc_self);
+ hdr->hdr_crc_self = 0;
+ if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
+ if (verbose)
+ warnx("%s: Bad CRC in GPT header at sector %llu",
+ device_name, (long long)lba);
+ goto fail_hdr;
+ }
+
+ tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
+ blocks = tblsz / secsz + ((tblsz % secsz) ? 1 : 0);
+
+ /* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
+ p = gpt_read(fd, le64toh(hdr->hdr_lba_table), blocks);
+ if (p == NULL)
+ return (-1);
+
+ if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
+ if (verbose)
+ warnx("%s: Bad CRC in GPT table at sector %llu",
+ device_name,
+ (long long)le64toh(hdr->hdr_lba_table));
+ goto fail_ent;
+ }
+
+ if (verbose > 1)
+ warnx("%s: %s GPT at sector %llu", device_name,
+ (lba == 1) ? "Pri" : "Sec", (long long)lba);
+
+ m = map_add(lba, 1, (lba == 1)
+ ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr);
+ if (m == NULL)
+ return (-1);
+
+ m = map_add(le64toh(hdr->hdr_lba_table), blocks, (lba == 1)
+ ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p);
+ if (m == NULL)
+ return (-1);
+
+ if (lba != 1)
+ return (0);
+
+ for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
+ ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
+ if (uuid_is_nil(&ent->ent_type, NULL))
+ continue;
+
+ size = le64toh(ent->ent_lba_end) - le64toh(ent->ent_lba_start) +
+ 1LL;
+ if (verbose > 2) {
+ le_uuid_dec(&ent->ent_type, &type);
+ uuid_to_string(&type, &s, NULL);
+ warnx(
+ "%s: GPT partition: type=%s, start=%llu, size=%llu", device_name, s,
+ (long long)le64toh(ent->ent_lba_start),
+ (long long)size);
+ free(s);
+ }
+ m = map_add(le64toh(ent->ent_lba_start), size,
+ MAP_TYPE_GPT_PART, ent);
+ if (m == NULL)
+ return (-1);
+ m->map_index = i + 1;
+ }
+ return (0);
+
+ fail_ent:
+ free(p);
+
+ fail_hdr:
+ free(hdr);
+ return (0);
+}
+
+int
+gpt_open(const char *dev)
+{
+ struct stat sb;
+ int fd, mode;
+
+ mode = readonly ? O_RDONLY : O_RDWR|O_EXCL;
+
+ strlcpy(device_path, dev, sizeof(device_path));
+ device_name = device_path;
+
+ if ((fd = open(device_path, mode)) != -1)
+ goto found;
+
+ snprintf(device_path, sizeof(device_path), "%s%s", _PATH_DEV, dev);
+ device_name = device_path + strlen(_PATH_DEV);
+ if ((fd = open(device_path, mode)) != -1)
+ goto found;
+
+ return (-1);
+
+ found:
+ if (fstat(fd, &sb) == -1)
+ goto close;
+
+ if ((sb.st_mode & S_IFMT) != S_IFREG) {
+ if (ioctl(fd, DIOCGSECTORSIZE, &secsz) == -1 ||
+ ioctl(fd, DIOCGMEDIASIZE, &mediasz) == -1)
+ goto close;
+ } else {
+ secsz = 512; /* Fixed size for files. */
+ if (sb.st_size % secsz) {
+ errno = EINVAL;
+ goto close;
+ }
+ mediasz = sb.st_size;
+ }
+
+ /*
+ * We require an absolute minimum of 6 sectors. One for the MBR,
+ * 2 for the GPT header, 2 for the GPT table and one to hold some
+ * user data. Let's catch this extreme border case here so that
+ * we don't have to worry about it later.
+ */
+ if (mediasz / secsz < 6) {
+ errno = ENODEV;
+ goto close;
+ }
+
+ if (verbose)
+ warnx("%s: mediasize=%llu; sectorsize=%u; blocks=%llu",
+ device_name, (long long)mediasz, secsz,
+ (long long)(mediasz / secsz));
+
+ map_init(mediasz / secsz);
+
+ if (gpt_mbr(fd, 0LL) == -1)
+ goto close;
+ if (gpt_gpt(fd, 1LL) == -1)
+ goto close;
+ if (gpt_gpt(fd, mediasz / secsz - 1LL) == -1)
+ goto close;
+
+ return (fd);
+
+ close:
+ close(fd);
+ return (-1);
+}
+
+void
+gpt_close(int fd)
+{
+ /* XXX post processing? */
+ close(fd);
+}
+
+static struct {
+ int (*fptr)(int, char *[]);
+ const char *name;
+} cmdsw[] = {
+ { cmd_add, "add" },
+ { cmd_create, "create" },
+ { cmd_destroy, "destroy" },
+ { NULL, "help" },
+ { cmd_label, "label" },
+ { cmd_migrate, "migrate" },
+ { cmd_recover, "recover" },
+ { cmd_remove, "remove" },
+ { NULL, "rename" },
+ { cmd_show, "show" },
+ { NULL, "verify" },
+ { NULL, NULL }
+};
+
+static void
+usage(void)
+{
+
+ fprintf(stderr,
+ "usage: %s [-rv] [-p nparts] command [options] device ...\n",
+ getprogname());
+ exit(1);
+}
+
+static void
+prefix(const char *cmd)
+{
+ char *pfx;
+ const char *prg;
+
+ prg = getprogname();
+ pfx = malloc(strlen(prg) + strlen(cmd) + 2);
+ /* Don't bother failing. It's not important */
+ if (pfx == NULL)
+ return;
+
+ sprintf(pfx, "%s %s", prg, cmd);
+ setprogname(pfx);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *cmd, *p;
+ int ch, i;
+
+ /* Get the generic options */
+ while ((ch = getopt(argc, argv, "p:rv")) != -1) {
+ switch(ch) {
+ case 'p':
+ if (parts > 0)
+ usage();
+ parts = strtol(optarg, &p, 10);
+ if (*p != 0 || parts < 1)
+ usage();
+ break;
+ case 'r':
+ readonly = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ usage();
+ }
+ }
+ if (!parts)
+ parts = 128;
+
+ if (argc == optind)
+ usage();
+
+ cmd = argv[optind++];
+ for (i = 0; cmdsw[i].name != NULL && strcmp(cmd, cmdsw[i].name); i++);
+
+ if (cmdsw[i].fptr == NULL)
+ errx(1, "unknown command: %s", cmd);
+
+ prefix(cmd);
+ return ((*cmdsw[i].fptr)(argc, argv));
+}
diff --git a/sbin/gpt/gpt.h b/sbin/gpt/gpt.h
new file mode 100644
index 0000000..fc9e2d5
--- /dev/null
+++ b/sbin/gpt/gpt.h
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 2002 Marcel Moolenaar
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _GPT_H_
+#define _GPT_H_
+
+#include <sys/endian.h>
+#include <sys/gpt.h>
+
+#include <uuid.h>
+
+void le_uuid_dec(void const *, uuid_t *);
+void le_uuid_enc(void *, uuid_t const *);
+
+struct mbr_part {
+ uint8_t part_flag; /* bootstrap flags */
+ uint8_t part_shd; /* starting head */
+ uint8_t part_ssect; /* starting sector */
+ uint8_t part_scyl; /* starting cylinder */
+ uint8_t part_typ; /* partition type */
+ uint8_t part_ehd; /* end head */
+ uint8_t part_esect; /* end sector */
+ uint8_t part_ecyl; /* end cylinder */
+ uint16_t part_start_lo; /* absolute starting ... */
+ uint16_t part_start_hi; /* ... sector number */
+ uint16_t part_size_lo; /* partition size ... */
+ uint16_t part_size_hi; /* ... in sectors */
+};
+
+struct mbr {
+ uint16_t mbr_code[223];
+ struct mbr_part mbr_part[4];
+ uint16_t mbr_sig;
+#define MBR_SIG 0xAA55
+};
+
+extern char *device_name;
+extern off_t mediasz;
+extern u_int parts;
+extern u_int secsz;
+extern int readonly, verbose;
+
+uint32_t crc32(const void *, size_t);
+void gpt_close(int);
+int gpt_open(const char *);
+void* gpt_read(int, off_t, size_t);
+int gpt_write(int, map_t *);
+
+uint8_t *utf16_to_utf8(uint16_t *);
+void utf8_to_utf16(const uint8_t *, uint16_t *, size_t);
+
+int cmd_add(int, char *[]);
+int cmd_create(int, char *[]);
+int cmd_destroy(int, char *[]);
+int cmd_label(int, char *[]);
+int cmd_migrate(int, char *[]);
+int cmd_recover(int, char *[]);
+int cmd_remove(int, char *[]);
+int cmd_show(int, char *[]);
+
+#endif /* _GPT_H_ */
diff --git a/sbin/gpt/label.c b/sbin/gpt/label.c
new file mode 100644
index 0000000..59beb35
--- /dev/null
+++ b/sbin/gpt/label.c
@@ -0,0 +1,263 @@
+/*-
+ * Copyright (c) 2005 Marcel Moolenaar
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "map.h"
+#include "gpt.h"
+
+static int all;
+static uuid_t type;
+static off_t block, size;
+static unsigned int entry;
+static uint8_t *name;
+
+static void
+usage_label(void)
+{
+ const char *common = "<-l label | -f file> device ...";
+
+ fprintf(stderr,
+ "usage: %s -a %s\n"
+ " %s [-b lba] [-i index] [-s lba] [-t uuid] %s\n",
+ getprogname(), common, getprogname(), common);
+ exit(1);
+}
+
+static void
+label(int fd)
+{
+ uuid_t uuid;
+ map_t *gpt, *tpg;
+ map_t *tbl, *lbt;
+ map_t *m;
+ struct gpt_hdr *hdr;
+ struct gpt_ent *ent;
+ unsigned int i;
+
+ gpt = map_find(MAP_TYPE_PRI_GPT_HDR);
+ if (gpt == NULL) {
+ warnx("%s: error: no primary GPT header; run create or recover",
+ device_name);
+ return;
+ }
+
+ tpg = map_find(MAP_TYPE_SEC_GPT_HDR);
+ if (tpg == NULL) {
+ warnx("%s: error: no secondary GPT header; run recover",
+ device_name);
+ return;
+ }
+
+ tbl = map_find(MAP_TYPE_PRI_GPT_TBL);
+ lbt = map_find(MAP_TYPE_SEC_GPT_TBL);
+ if (tbl == NULL || lbt == NULL) {
+ warnx("%s: error: run recover -- trust me", device_name);
+ return;
+ }
+
+ /* Relabel all matching entries in the map. */
+ for (m = map_first(); m != NULL; m = m->map_next) {
+ if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
+ continue;
+ if (entry > 0 && entry != m->map_index)
+ continue;
+ if (block > 0 && block != m->map_start)
+ continue;
+ if (size > 0 && size != m->map_size)
+ continue;
+
+ i = m->map_index - 1;
+
+ hdr = gpt->map_data;
+ ent = (void*)((char*)tbl->map_data + i *
+ le32toh(hdr->hdr_entsz));
+ le_uuid_dec(&ent->ent_type, &uuid);
+ if (!uuid_is_nil(&type, NULL) &&
+ !uuid_equal(&type, &uuid, NULL))
+ continue;
+
+ /* Label the primary entry. */
+ utf8_to_utf16(name, ent->ent_name, 36);
+
+ hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
+ le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
+ hdr->hdr_crc_self = 0;
+ hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
+
+ gpt_write(fd, gpt);
+ gpt_write(fd, tbl);
+
+ hdr = tpg->map_data;
+ ent = (void*)((char*)lbt->map_data + i *
+ le32toh(hdr->hdr_entsz));
+
+ /* Label the secundary entry. */
+ utf8_to_utf16(name, ent->ent_name, 36);
+
+ hdr->hdr_crc_table = htole32(crc32(lbt->map_data,
+ le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
+ hdr->hdr_crc_self = 0;
+ hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
+
+ gpt_write(fd, lbt);
+ gpt_write(fd, tpg);
+
+ printf("%sp%u labeled\n", device_name, m->map_index);
+ }
+}
+
+static void
+name_from_file(const char *fn)
+{
+ FILE *f;
+ char *p;
+ size_t maxlen = 1024;
+ size_t len;
+
+ if (strcmp(fn, "-") != 0) {
+ f = fopen(fn, "r");
+ if (f == NULL)
+ err(1, "unable to open file %s", fn);
+ } else
+ f = stdin;
+ name = malloc(maxlen);
+ len = fread(name, 1, maxlen - 1, f);
+ if (ferror(f))
+ err(1, "unable to read label from file %s", fn);
+ if (f != stdin)
+ fclose(f);
+ name[len] = '\0';
+ /* Only keep the first line, excluding the newline character. */
+ p = strchr(name, '\n');
+ if (p != NULL)
+ *p = '\0';
+}
+
+int
+cmd_label(int argc, char *argv[])
+{
+ char *p;
+ int ch, fd;
+ uint32_t status;
+
+ /* Get the label options */
+ while ((ch = getopt(argc, argv, "ab:f:i:l:s:t:")) != -1) {
+ switch(ch) {
+ case 'a':
+ if (all > 0)
+ usage_label();
+ all = 1;
+ break;
+ case 'b':
+ if (block > 0)
+ usage_label();
+ block = strtol(optarg, &p, 10);
+ if (*p != 0 || block < 1)
+ usage_label();
+ break;
+ case 'f':
+ if (name != NULL)
+ usage_label();
+ name_from_file(optarg);
+ break;
+ case 'i':
+ if (entry > 0)
+ usage_label();
+ entry = strtol(optarg, &p, 10);
+ if (*p != 0 || entry < 1)
+ usage_label();
+ break;
+ case 'l':
+ if (name != NULL)
+ usage_label();
+ name = strdup(optarg);
+ break;
+ case 's':
+ if (size > 0)
+ usage_label();
+ size = strtol(optarg, &p, 10);
+ if (*p != 0 || size < 1)
+ usage_label();
+ break;
+ case 't':
+ if (!uuid_is_nil(&type, NULL))
+ usage_label();
+ uuid_from_string(optarg, &type, &status);
+ if (status != uuid_s_ok) {
+ if (strcmp(optarg, "efi") == 0) {
+ uuid_t efi = GPT_ENT_TYPE_EFI;
+ type = efi;
+ } else if (strcmp(optarg, "swap") == 0) {
+ uuid_t sw = GPT_ENT_TYPE_FREEBSD_SWAP;
+ type = sw;
+ } else if (strcmp(optarg, "ufs") == 0) {
+ uuid_t ufs = GPT_ENT_TYPE_FREEBSD_UFS;
+ type = ufs;
+ } else if (strcmp(optarg, "linux") == 0 ||
+ strcmp(optarg, "windows") == 0) {
+ uuid_t m = GPT_ENT_TYPE_MS_BASIC_DATA;
+ type = m;
+ } else
+ usage_label();
+ }
+ break;
+ default:
+ usage_label();
+ }
+ }
+
+ if (!all ^
+ (block > 0 || entry > 0 || size > 0 || !uuid_is_nil(&type, NULL)))
+ usage_label();
+
+ if (name == NULL || argc == optind)
+ usage_label();
+
+ while (optind < argc) {
+ fd = gpt_open(argv[optind++]);
+ if (fd == -1) {
+ warn("unable to open device '%s'", device_name);
+ continue;
+ }
+
+ label(fd);
+
+ gpt_close(fd);
+ }
+
+ return (0);
+}
diff --git a/sbin/gpt/map.c b/sbin/gpt/map.c
new file mode 100644
index 0000000..91b5685
--- /dev/null
+++ b/sbin/gpt/map.c
@@ -0,0 +1,216 @@
+/*-
+ * Copyright (c) 2002 Marcel Moolenaar
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "map.h"
+
+int lbawidth;
+
+static map_t *mediamap;
+
+static map_t *
+mkmap(off_t start, off_t size, int type)
+{
+ map_t *m;
+
+ m = malloc(sizeof(*m));
+ if (m == NULL)
+ return (NULL);
+ m->map_start = start;
+ m->map_size = size;
+ m->map_next = m->map_prev = NULL;
+ m->map_type = type;
+ m->map_index = 0;
+ m->map_data = NULL;
+ return (m);
+}
+
+map_t *
+map_add(off_t start, off_t size, int type, void *data)
+{
+ map_t *m, *n, *p;
+
+ n = mediamap;
+ while (n != NULL && n->map_start + n->map_size <= start)
+ n = n->map_next;
+ if (n == NULL)
+ return (NULL);
+
+ if (n->map_start + n->map_size < start + size) {
+ warnx("error: bogus map");
+ return (0);
+ }
+
+ if (n->map_start == start && n->map_size == size) {
+ if (n->map_type != MAP_TYPE_UNUSED) {
+ if (n->map_type != MAP_TYPE_MBR_PART ||
+ type != MAP_TYPE_GPT_PART) {
+ warnx("warning: partition(%llu,%llu) mirrored",
+ (long long)start, (long long)size);
+ }
+ }
+ n->map_type = type;
+ n->map_data = data;
+ return (n);
+ }
+
+ if (n->map_type != MAP_TYPE_UNUSED) {
+ if (n->map_type != MAP_TYPE_MBR_PART ||
+ type != MAP_TYPE_GPT_PART) {
+ warnx("error: bogus map");
+ return (0);
+ }
+ n->map_type = MAP_TYPE_UNUSED;
+ }
+
+ m = mkmap(start, size, type);
+ if (m == NULL)
+ return (NULL);
+
+ m->map_data = data;
+
+ if (start == n->map_start) {
+ m->map_prev = n->map_prev;
+ m->map_next = n;
+ if (m->map_prev != NULL)
+ m->map_prev->map_next = m;
+ else
+ mediamap = m;
+ n->map_prev = m;
+ n->map_start += size;
+ n->map_size -= size;
+ } else if (start + size == n->map_start + n->map_size) {
+ p = n;
+ m->map_next = p->map_next;
+ m->map_prev = p;
+ if (m->map_next != NULL)
+ m->map_next->map_prev = m;
+ p->map_next = m;
+ p->map_size -= size;
+ } else {
+ p = mkmap(n->map_start, start - n->map_start, n->map_type);
+ n->map_start += p->map_size + m->map_size;
+ n->map_size -= (p->map_size + m->map_size);
+ p->map_prev = n->map_prev;
+ m->map_prev = p;
+ n->map_prev = m;
+ m->map_next = n;
+ p->map_next = m;
+ if (p->map_prev != NULL)
+ p->map_prev->map_next = p;
+ else
+ mediamap = p;
+ }
+
+ return (m);
+}
+
+map_t *
+map_alloc(off_t start, off_t size)
+{
+ off_t delta;
+ map_t *m;
+
+ for (m = mediamap; m != NULL; m = m->map_next) {
+ if (m->map_type != MAP_TYPE_UNUSED || m->map_start < 2)
+ continue;
+ if (start != 0 && m->map_start > start)
+ return (NULL);
+ delta = (start != 0) ? start - m->map_start : 0;
+ if (size == 0 || m->map_size - delta >= size) {
+ if (m->map_size - delta <= 0)
+ continue;
+ if (size == 0)
+ size = m->map_size - delta;
+ return (map_add(m->map_start + delta, size,
+ MAP_TYPE_GPT_PART, NULL));
+ }
+ }
+
+ return (NULL);
+}
+
+map_t *
+map_find(int type)
+{
+ map_t *m;
+
+ m = mediamap;
+ while (m != NULL && m->map_type != type)
+ m = m->map_next;
+ return (m);
+}
+
+map_t *
+map_first(void)
+{
+ return mediamap;
+}
+
+map_t *
+map_last(void)
+{
+ map_t *m;
+
+ m = mediamap;
+ while (m != NULL && m->map_next != NULL)
+ m = m->map_next;
+ return (m);
+}
+
+off_t
+map_free(off_t start, off_t size)
+{
+ map_t *m;
+
+ m = mediamap;
+
+ while (m != NULL && m->map_start + m->map_size <= start)
+ m = m->map_next;
+ if (m == NULL || m->map_type != MAP_TYPE_UNUSED)
+ return (0LL);
+ if (size)
+ return ((m->map_start + m->map_size >= start + size) ? 1 : 0);
+ return (m->map_size - (start - m->map_start));
+}
+
+void
+map_init(off_t size)
+{
+ char buf[32];
+
+ mediamap = mkmap(0LL, size, MAP_TYPE_UNUSED);
+ lbawidth = sprintf(buf, "%llu", (long long)size);
+ if (lbawidth < 5)
+ lbawidth = 5;
+}
diff --git a/sbin/gpt/map.h b/sbin/gpt/map.h
new file mode 100644
index 0000000..4c3f099
--- /dev/null
+++ b/sbin/gpt/map.h
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 2002 Marcel Moolenaar
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MAP_H_
+#define _MAP_H_
+
+typedef struct map {
+ off_t map_start;
+ off_t map_size;
+ struct map *map_next;
+ struct map *map_prev;
+ int map_type;
+#define MAP_TYPE_UNUSED 0
+#define MAP_TYPE_MBR 1
+#define MAP_TYPE_MBR_PART 2
+#define MAP_TYPE_PRI_GPT_HDR 3
+#define MAP_TYPE_SEC_GPT_HDR 4
+#define MAP_TYPE_PRI_GPT_TBL 5
+#define MAP_TYPE_SEC_GPT_TBL 6
+#define MAP_TYPE_GPT_PART 7
+#define MAP_TYPE_PMBR 8
+ unsigned int map_index;
+ void *map_data;
+} map_t;
+
+extern int lbawidth;
+
+map_t *map_add(off_t, off_t, int, void*);
+map_t *map_alloc(off_t, off_t);
+map_t *map_find(int);
+map_t *map_first(void);
+map_t *map_last(void);
+
+off_t map_free(off_t, off_t);
+
+void map_init(off_t);
+
+#endif /* _MAP_H_ */
diff --git a/sbin/gpt/migrate.c b/sbin/gpt/migrate.c
new file mode 100644
index 0000000..8f3b4ec
--- /dev/null
+++ b/sbin/gpt/migrate.c
@@ -0,0 +1,365 @@
+/*-
+ * Copyright (c) 2002 Marcel Moolenaar
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/disklabel.h>
+
+#include <err.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "map.h"
+#include "gpt.h"
+
+/*
+ * Allow compilation on platforms that do not have a BSD label.
+ * The values are valid for amd64, i386 and ia64 disklabels.
+ */
+#ifndef LABELOFFSET
+#define LABELOFFSET 0
+#endif
+#ifndef LABELSECTOR
+#define LABELSECTOR 1
+#endif
+
+static int force;
+static int slice;
+
+static void
+usage_migrate(void)
+{
+
+ fprintf(stderr,
+ "usage: %s [-fs] device ...\n", getprogname());
+ exit(1);
+}
+
+static struct gpt_ent*
+migrate_disklabel(int fd, off_t start, struct gpt_ent *ent)
+{
+ char *buf;
+ struct disklabel *dl;
+ off_t ofs, rawofs;
+ int i;
+
+ buf = gpt_read(fd, start + LABELSECTOR, 1);
+ dl = (void*)(buf + LABELOFFSET);
+
+ if (le32toh(dl->d_magic) != DISKMAGIC ||
+ le32toh(dl->d_magic2) != DISKMAGIC) {
+ warnx("%s: warning: FreeBSD slice without disklabel",
+ device_name);
+ return (ent);
+ }
+
+ rawofs = le32toh(dl->d_partitions[RAW_PART].p_offset) *
+ le32toh(dl->d_secsize);
+ for (i = 0; i < le16toh(dl->d_npartitions); i++) {
+ if (dl->d_partitions[i].p_fstype == FS_UNUSED)
+ continue;
+ ofs = le32toh(dl->d_partitions[i].p_offset) *
+ le32toh(dl->d_secsize);
+ if (ofs < rawofs)
+ rawofs = 0;
+ }
+ rawofs /= secsz;
+
+ for (i = 0; i < le16toh(dl->d_npartitions); i++) {
+ switch (dl->d_partitions[i].p_fstype) {
+ case FS_UNUSED:
+ continue;
+ case FS_SWAP: {
+ uuid_t swap = GPT_ENT_TYPE_FREEBSD_SWAP;
+ le_uuid_enc(&ent->ent_type, &swap);
+ utf8_to_utf16("FreeBSD swap partition",
+ ent->ent_name, 36);
+ break;
+ }
+ case FS_BSDFFS: {
+ uuid_t ufs = GPT_ENT_TYPE_FREEBSD_UFS;
+ le_uuid_enc(&ent->ent_type, &ufs);
+ utf8_to_utf16("FreeBSD UFS partition",
+ ent->ent_name, 36);
+ break;
+ }
+ case FS_VINUM: {
+ uuid_t vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
+ le_uuid_enc(&ent->ent_type, &vinum);
+ utf8_to_utf16("FreeBSD vinum partition",
+ ent->ent_name, 36);
+ break;
+ }
+ default:
+ warnx("%s: warning: unknown FreeBSD partition (%d)",
+ device_name, dl->d_partitions[i].p_fstype);
+ continue;
+ }
+
+ ofs = (le32toh(dl->d_partitions[i].p_offset) *
+ le32toh(dl->d_secsize)) / secsz;
+ ofs = (ofs > 0) ? ofs - rawofs : 0;
+ ent->ent_lba_start = htole64(start + ofs);
+ ent->ent_lba_end = htole64(start + ofs +
+ le32toh(dl->d_partitions[i].p_size) - 1LL);
+ ent++;
+ }
+
+ return (ent);
+}
+
+static void
+migrate(int fd)
+{
+ uuid_t uuid;
+ off_t blocks, last;
+ map_t *gpt, *tpg;
+ map_t *tbl, *lbt;
+ map_t *map;
+ struct gpt_hdr *hdr;
+ struct gpt_ent *ent;
+ struct mbr *mbr;
+ uint32_t start, size;
+ unsigned int i;
+
+ last = mediasz / secsz - 1LL;
+
+ map = map_find(MAP_TYPE_MBR);
+ if (map == NULL || map->map_start != 0) {
+ warnx("%s: error: no partitions to convert", device_name);
+ return;
+ }
+
+ mbr = map->map_data;
+
+ if (map_find(MAP_TYPE_PRI_GPT_HDR) != NULL ||
+ map_find(MAP_TYPE_SEC_GPT_HDR) != NULL) {
+ warnx("%s: error: device already contains a GPT", device_name);
+ return;
+ }
+
+ /* Get the amount of free space after the MBR */
+ blocks = map_free(1LL, 0LL);
+ if (blocks == 0LL) {
+ warnx("%s: error: no room for the GPT header", device_name);
+ return;
+ }
+
+ /* Don't create more than parts entries. */
+ if ((uint64_t)(blocks - 1) * secsz > parts * sizeof(struct gpt_ent)) {
+ blocks = (parts * sizeof(struct gpt_ent)) / secsz;
+ if ((parts * sizeof(struct gpt_ent)) % secsz)
+ blocks++;
+ blocks++; /* Don't forget the header itself */
+ }
+
+ /* Never cross the median of the device. */
+ if ((blocks + 1LL) > ((last + 1LL) >> 1))
+ blocks = ((last + 1LL) >> 1) - 1LL;
+
+ /*
+ * Get the amount of free space at the end of the device and
+ * calculate the size for the GPT structures.
+ */
+ map = map_last();
+ if (map->map_type != MAP_TYPE_UNUSED) {
+ warnx("%s: error: no room for the backup header", device_name);
+ return;
+ }
+
+ if (map->map_size < blocks)
+ blocks = map->map_size;
+ if (blocks == 1LL) {
+ warnx("%s: error: no room for the GPT table", device_name);
+ return;
+ }
+
+ blocks--; /* Number of blocks in the GPT table. */
+ gpt = map_add(1LL, 1LL, MAP_TYPE_PRI_GPT_HDR, calloc(1, secsz));
+ tbl = map_add(2LL, blocks, MAP_TYPE_PRI_GPT_TBL,
+ calloc(blocks, secsz));
+ if (gpt == NULL || tbl == NULL)
+ return;
+
+ lbt = map_add(last - blocks, blocks, MAP_TYPE_SEC_GPT_TBL,
+ tbl->map_data);
+ tpg = map_add(last, 1LL, MAP_TYPE_SEC_GPT_HDR, calloc(1, secsz));
+
+ hdr = gpt->map_data;
+ memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
+ hdr->hdr_revision = htole32(GPT_HDR_REVISION);
+ /*
+ * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
+ * contains padding we must not include in the size.
+ */
+ hdr->hdr_size = htole32(offsetof(struct gpt_hdr, padding));
+ hdr->hdr_lba_self = htole64(gpt->map_start);
+ hdr->hdr_lba_alt = htole64(tpg->map_start);
+ hdr->hdr_lba_start = htole64(tbl->map_start + blocks);
+ hdr->hdr_lba_end = htole64(lbt->map_start - 1LL);
+ uuid_create(&uuid, NULL);
+ le_uuid_enc(&hdr->hdr_uuid, &uuid);
+ hdr->hdr_lba_table = htole64(tbl->map_start);
+ hdr->hdr_entries = htole32((blocks * secsz) / sizeof(struct gpt_ent));
+ if (le32toh(hdr->hdr_entries) > parts)
+ hdr->hdr_entries = htole32(parts);
+ hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
+
+ ent = tbl->map_data;
+ for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
+ uuid_create(&uuid, NULL);
+ le_uuid_enc(&ent[i].ent_uuid, &uuid);
+ }
+
+ /* Mirror partitions. */
+ for (i = 0; i < 4; i++) {
+ start = le16toh(mbr->mbr_part[i].part_start_hi);
+ start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
+ size = le16toh(mbr->mbr_part[i].part_size_hi);
+ size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
+
+ switch (mbr->mbr_part[i].part_typ) {
+ case 0:
+ continue;
+ case 165: { /* FreeBSD */
+ if (slice) {
+ uuid_t freebsd = GPT_ENT_TYPE_FREEBSD;
+ le_uuid_enc(&ent->ent_type, &freebsd);
+ ent->ent_lba_start = htole64((uint64_t)start);
+ ent->ent_lba_end = htole64(start + size - 1LL);
+ utf8_to_utf16("FreeBSD disklabel partition",
+ ent->ent_name, 36);
+ ent++;
+ } else
+ ent = migrate_disklabel(fd, start, ent);
+ break;
+ }
+ case 239: { /* EFI */
+ uuid_t efi_slice = GPT_ENT_TYPE_EFI;
+ le_uuid_enc(&ent->ent_type, &efi_slice);
+ ent->ent_lba_start = htole64((uint64_t)start);
+ ent->ent_lba_end = htole64(start + size - 1LL);
+ utf8_to_utf16("EFI system partition",
+ ent->ent_name, 36);
+ ent++;
+ break;
+ }
+ default:
+ if (!force) {
+ warnx("%s: error: unknown partition type (%d)",
+ device_name, mbr->mbr_part[i].part_typ);
+ return;
+ }
+ }
+ }
+ ent = tbl->map_data;
+
+ hdr->hdr_crc_table = htole32(crc32(ent, le32toh(hdr->hdr_entries) *
+ le32toh(hdr->hdr_entsz)));
+ hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
+
+ gpt_write(fd, gpt);
+ gpt_write(fd, tbl);
+
+ /*
+ * Create backup GPT.
+ */
+ memcpy(tpg->map_data, gpt->map_data, secsz);
+ hdr = tpg->map_data;
+ hdr->hdr_lba_self = htole64(tpg->map_start);
+ hdr->hdr_lba_alt = htole64(gpt->map_start);
+ hdr->hdr_lba_table = htole64(lbt->map_start);
+ hdr->hdr_crc_self = 0; /* Don't ever forget this! */
+ hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
+
+ gpt_write(fd, lbt);
+ gpt_write(fd, tpg);
+
+ map = map_find(MAP_TYPE_MBR);
+ mbr = map->map_data;
+ /*
+ * Turn the MBR into a Protective MBR.
+ */
+ bzero(mbr->mbr_part, sizeof(mbr->mbr_part));
+ mbr->mbr_part[0].part_shd = 0xff;
+ mbr->mbr_part[0].part_ssect = 0xff;
+ mbr->mbr_part[0].part_scyl = 0xff;
+ mbr->mbr_part[0].part_typ = 0xee;
+ mbr->mbr_part[0].part_ehd = 0xff;
+ mbr->mbr_part[0].part_esect = 0xff;
+ mbr->mbr_part[0].part_ecyl = 0xff;
+ mbr->mbr_part[0].part_start_lo = htole16(1);
+ if (last > 0xffffffff) {
+ mbr->mbr_part[0].part_size_lo = htole16(0xffff);
+ mbr->mbr_part[0].part_size_hi = htole16(0xffff);
+ } else {
+ mbr->mbr_part[0].part_size_lo = htole16(last);
+ mbr->mbr_part[0].part_size_hi = htole16(last >> 16);
+ }
+ gpt_write(fd, map);
+}
+
+int
+cmd_migrate(int argc, char *argv[])
+{
+ int ch, fd;
+
+ /* Get the migrate options */
+ while ((ch = getopt(argc, argv, "fs")) != -1) {
+ switch(ch) {
+ case 'f':
+ force = 1;
+ break;
+ case 's':
+ slice = 1;
+ break;
+ default:
+ usage_migrate();
+ }
+ }
+
+ if (argc == optind)
+ usage_migrate();
+
+ while (optind < argc) {
+ fd = gpt_open(argv[optind++]);
+ if (fd == -1) {
+ warn("unable to open device '%s'", device_name);
+ continue;
+ }
+
+ migrate(fd);
+
+ gpt_close(fd);
+ }
+
+ return (0);
+}
diff --git a/sbin/gpt/recover.c b/sbin/gpt/recover.c
new file mode 100644
index 0000000..dd13a64
--- /dev/null
+++ b/sbin/gpt/recover.c
@@ -0,0 +1,178 @@
+/*-
+ * Copyright (c) 2002 Marcel Moolenaar
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "map.h"
+#include "gpt.h"
+
+static int recoverable;
+
+static void
+usage_recover(void)
+{
+
+ fprintf(stderr,
+ "usage: %s device ...\n", getprogname());
+ exit(1);
+}
+
+static void
+recover(int fd)
+{
+ off_t last;
+ map_t *gpt, *tpg;
+ map_t *tbl, *lbt;
+ struct gpt_hdr *hdr;
+
+ if (map_find(MAP_TYPE_MBR) != NULL) {
+ warnx("%s: error: device contains a MBR", device_name);
+ return;
+ }
+
+ gpt = map_find(MAP_TYPE_PRI_GPT_HDR);
+ tpg = map_find(MAP_TYPE_SEC_GPT_HDR);
+ tbl = map_find(MAP_TYPE_PRI_GPT_TBL);
+ lbt = map_find(MAP_TYPE_SEC_GPT_TBL);
+
+ if (gpt == NULL && tpg == NULL) {
+ warnx("%s: no primary or secondary GPT headers, can't recover",
+ device_name);
+ return;
+ }
+ if (tbl == NULL && lbt == NULL) {
+ warnx("%s: no primary or secondary GPT tables, can't recover",
+ device_name);
+ return;
+ }
+
+ last = mediasz / secsz - 1LL;
+
+ if (tbl != NULL && lbt == NULL) {
+ lbt = map_add(last - tbl->map_size, tbl->map_size,
+ MAP_TYPE_SEC_GPT_TBL, tbl->map_data);
+ if (lbt == NULL) {
+ warnx("%s: adding secondary GPT table failed",
+ device_name);
+ return;
+ }
+ gpt_write(fd, lbt);
+ warnx("%s: recovered secondary GPT table from primary",
+ device_name);
+ } else if (tbl == NULL && lbt != NULL) {
+ tbl = map_add(2LL, lbt->map_size, MAP_TYPE_PRI_GPT_TBL,
+ lbt->map_data);
+ if (tbl == NULL) {
+ warnx("%s: adding primary GPT table failed",
+ device_name);
+ return;
+ }
+ gpt_write(fd, tbl);
+ warnx("%s: recovered primary GPT table from secondary",
+ device_name);
+ }
+
+ if (gpt != NULL && tpg == NULL) {
+ tpg = map_add(last, 1LL, MAP_TYPE_SEC_GPT_HDR,
+ calloc(1, secsz));
+ if (tpg == NULL) {
+ warnx("%s: adding secondary GPT header failed",
+ device_name);
+ return;
+ }
+ memcpy(tpg->map_data, gpt->map_data, secsz);
+ hdr = tpg->map_data;
+ hdr->hdr_lba_self = htole64(tpg->map_start);
+ hdr->hdr_lba_alt = htole64(gpt->map_start);
+ hdr->hdr_lba_table = htole64(lbt->map_start);
+ hdr->hdr_crc_self = 0;
+ hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
+ gpt_write(fd, tpg);
+ warnx("%s: recovered secondary GPT header from primary",
+ device_name);
+ } else if (gpt == NULL && tpg != NULL) {
+ gpt = map_add(1LL, 1LL, MAP_TYPE_PRI_GPT_HDR,
+ calloc(1, secsz));
+ if (gpt == NULL) {
+ warnx("%s: adding primary GPT header failed",
+ device_name);
+ return;
+ }
+ memcpy(gpt->map_data, tpg->map_data, secsz);
+ hdr = gpt->map_data;
+ hdr->hdr_lba_self = htole64(gpt->map_start);
+ hdr->hdr_lba_alt = htole64(tpg->map_start);
+ hdr->hdr_lba_table = htole64(tbl->map_start);
+ hdr->hdr_crc_self = 0;
+ hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
+ gpt_write(fd, gpt);
+ warnx("%s: recovered primary GPT header from secondary",
+ device_name);
+ }
+}
+
+int
+cmd_recover(int argc, char *argv[])
+{
+ int ch, fd;
+
+ while ((ch = getopt(argc, argv, "r")) != -1) {
+ switch(ch) {
+ case 'r':
+ recoverable = 1;
+ break;
+ default:
+ usage_recover();
+ }
+ }
+
+ if (argc == optind)
+ usage_recover();
+
+ while (optind < argc) {
+ fd = gpt_open(argv[optind++]);
+ if (fd == -1) {
+ warn("unable to open device '%s'", device_name);
+ continue;
+ }
+
+ recover(fd);
+
+ gpt_close(fd);
+ }
+
+ return (0);
+}
diff --git a/sbin/gpt/remove.c b/sbin/gpt/remove.c
new file mode 100644
index 0000000..500dd4d
--- /dev/null
+++ b/sbin/gpt/remove.c
@@ -0,0 +1,224 @@
+/*-
+ * Copyright (c) 2004 Marcel Moolenaar
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "map.h"
+#include "gpt.h"
+
+static int all;
+static uuid_t type;
+static off_t block, size;
+static unsigned int entry;
+
+static void
+usage_remove(void)
+{
+
+ fprintf(stderr,
+ "usage: %s -a device ...\n"
+ " %s [-b lba] [-i index] [-s lba] [-t uuid] device ...\n",
+ getprogname(), getprogname());
+ exit(1);
+}
+
+static void
+rem(int fd)
+{
+ uuid_t uuid;
+ map_t *gpt, *tpg;
+ map_t *tbl, *lbt;
+ map_t *m;
+ struct gpt_hdr *hdr;
+ struct gpt_ent *ent;
+ unsigned int i;
+
+ gpt = map_find(MAP_TYPE_PRI_GPT_HDR);
+ if (gpt == NULL) {
+ warnx("%s: error: no primary GPT header; run create or recover",
+ device_name);
+ return;
+ }
+
+ tpg = map_find(MAP_TYPE_SEC_GPT_HDR);
+ if (tpg == NULL) {
+ warnx("%s: error: no secondary GPT header; run recover",
+ device_name);
+ return;
+ }
+
+ tbl = map_find(MAP_TYPE_PRI_GPT_TBL);
+ lbt = map_find(MAP_TYPE_SEC_GPT_TBL);
+ if (tbl == NULL || lbt == NULL) {
+ warnx("%s: error: run recover -- trust me", device_name);
+ return;
+ }
+
+ /* Remove all matching entries in the map. */
+ for (m = map_first(); m != NULL; m = m->map_next) {
+ if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
+ continue;
+ if (entry > 0 && entry != m->map_index)
+ continue;
+ if (block > 0 && block != m->map_start)
+ continue;
+ if (size > 0 && size != m->map_size)
+ continue;
+
+ i = m->map_index - 1;
+
+ hdr = gpt->map_data;
+ ent = (void*)((char*)tbl->map_data + i *
+ le32toh(hdr->hdr_entsz));
+ le_uuid_dec(&ent->ent_type, &uuid);
+ if (!uuid_is_nil(&type, NULL) &&
+ !uuid_equal(&type, &uuid, NULL))
+ continue;
+
+ /* Remove the primary entry by clearing the partition type. */
+ uuid_create_nil(&ent->ent_type, NULL);
+
+ hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
+ le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
+ hdr->hdr_crc_self = 0;
+ hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
+
+ gpt_write(fd, gpt);
+ gpt_write(fd, tbl);
+
+ hdr = tpg->map_data;
+ ent = (void*)((char*)lbt->map_data + i *
+ le32toh(hdr->hdr_entsz));
+
+ /* Remove the secundary entry. */
+ uuid_create_nil(&ent->ent_type, NULL);
+
+ hdr->hdr_crc_table = htole32(crc32(lbt->map_data,
+ le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
+ hdr->hdr_crc_self = 0;
+ hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
+
+ gpt_write(fd, lbt);
+ gpt_write(fd, tpg);
+
+ printf("%sp%u removed\n", device_name, m->map_index);
+ }
+}
+
+int
+cmd_remove(int argc, char *argv[])
+{
+ char *p;
+ int ch, fd;
+ uint32_t status;
+
+ /* Get the remove options */
+ while ((ch = getopt(argc, argv, "ab:i:s:t:")) != -1) {
+ switch(ch) {
+ case 'a':
+ if (all > 0)
+ usage_remove();
+ all = 1;
+ break;
+ case 'b':
+ if (block > 0)
+ usage_remove();
+ block = strtol(optarg, &p, 10);
+ if (*p != 0 || block < 1)
+ usage_remove();
+ break;
+ case 'i':
+ if (entry > 0)
+ usage_remove();
+ entry = strtol(optarg, &p, 10);
+ if (*p != 0 || entry < 1)
+ usage_remove();
+ break;
+ case 's':
+ if (size > 0)
+ usage_remove();
+ size = strtol(optarg, &p, 10);
+ if (*p != 0 || size < 1)
+ usage_remove();
+ break;
+ case 't':
+ if (!uuid_is_nil(&type, NULL))
+ usage_remove();
+ uuid_from_string(optarg, &type, &status);
+ if (status != uuid_s_ok) {
+ if (strcmp(optarg, "efi") == 0) {
+ uuid_t efi = GPT_ENT_TYPE_EFI;
+ type = efi;
+ } else if (strcmp(optarg, "swap") == 0) {
+ uuid_t sw = GPT_ENT_TYPE_FREEBSD_SWAP;
+ type = sw;
+ } else if (strcmp(optarg, "ufs") == 0) {
+ uuid_t ufs = GPT_ENT_TYPE_FREEBSD_UFS;
+ type = ufs;
+ } else if (strcmp(optarg, "linux") == 0 ||
+ strcmp(optarg, "windows") == 0) {
+ uuid_t m = GPT_ENT_TYPE_MS_BASIC_DATA;
+ type = m;
+ } else
+ usage_remove();
+ }
+ break;
+ default:
+ usage_remove();
+ }
+ }
+
+ if (!all ^
+ (block > 0 || entry > 0 || size > 0 || !uuid_is_nil(&type, NULL)))
+ usage_remove();
+
+ if (argc == optind)
+ usage_remove();
+
+ while (optind < argc) {
+ fd = gpt_open(argv[optind++]);
+ if (fd == -1) {
+ warn("unable to open device '%s'", device_name);
+ continue;
+ }
+
+ rem(fd);
+
+ gpt_close(fd);
+ }
+
+ return (0);
+}
diff --git a/sbin/gpt/show.c b/sbin/gpt/show.c
new file mode 100644
index 0000000..4df3be1
--- /dev/null
+++ b/sbin/gpt/show.c
@@ -0,0 +1,209 @@
+/*-
+ * Copyright (c) 2002 Marcel Moolenaar
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "map.h"
+#include "gpt.h"
+
+static int show_label = 0;
+static int show_uuid = 0;
+
+static void
+usage_show(void)
+{
+
+ fprintf(stderr,
+ "usage: %s [-lu] device ...\n", getprogname());
+ exit(1);
+}
+
+static const char *
+friendly(uuid_t *t)
+{
+ static uuid_t efi_slice = GPT_ENT_TYPE_EFI;
+ static uuid_t mslinux = GPT_ENT_TYPE_MS_BASIC_DATA;
+ static uuid_t freebsd = GPT_ENT_TYPE_FREEBSD;
+ static uuid_t linuxswap = GPT_ENT_TYPE_LINUX_SWAP;
+ static uuid_t msr = GPT_ENT_TYPE_MS_RESERVED;
+ static uuid_t swap = GPT_ENT_TYPE_FREEBSD_SWAP;
+ static uuid_t ufs = GPT_ENT_TYPE_FREEBSD_UFS;
+ static uuid_t vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
+ static char buf[80];
+ char *s;
+
+ if (show_uuid)
+ goto unfriendly;
+
+ if (uuid_equal(t, &efi_slice, NULL))
+ return ("EFI System");
+ if (uuid_equal(t, &swap, NULL))
+ return ("FreeBSD swap");
+ if (uuid_equal(t, &ufs, NULL))
+ return ("FreeBSD UFS/UFS2");
+ if (uuid_equal(t, &vinum, NULL))
+ return ("FreeBSD vinum");
+
+ if (uuid_equal(t, &freebsd, NULL))
+ return ("FreeBSD legacy");
+ if (uuid_equal(t, &mslinux, NULL))
+ return ("Linux/Windows");
+ if (uuid_equal(t, &linuxswap, NULL))
+ return ("Linux swap");
+ if (uuid_equal(t, &msr, NULL))
+ return ("Windows reserved");
+
+unfriendly:
+ uuid_to_string(t, &s, NULL);
+ strlcpy(buf, s, sizeof buf);
+ free(s);
+ return (buf);
+}
+
+static void
+show(int fd __unused)
+{
+ uuid_t type;
+ off_t start;
+ map_t *m, *p;
+ struct mbr *mbr;
+ struct gpt_ent *ent;
+ unsigned int i;
+
+ printf(" %*s", lbawidth, "start");
+ printf(" %*s", lbawidth, "size");
+ printf(" index contents\n");
+
+ m = map_first();
+ while (m != NULL) {
+ printf(" %*llu", lbawidth, (long long)m->map_start);
+ printf(" %*llu", lbawidth, (long long)m->map_size);
+ putchar(' ');
+ putchar(' ');
+ if (m->map_index > 0)
+ printf("%5d", m->map_index);
+ else
+ printf(" ");
+ putchar(' ');
+ putchar(' ');
+ switch (m->map_type) {
+ case MAP_TYPE_MBR:
+ if (m->map_start != 0)
+ printf("Extended ");
+ printf("MBR");
+ break;
+ case MAP_TYPE_PRI_GPT_HDR:
+ printf("Pri GPT header");
+ break;
+ case MAP_TYPE_SEC_GPT_HDR:
+ printf("Sec GPT header");
+ break;
+ case MAP_TYPE_PRI_GPT_TBL:
+ printf("Pri GPT table");
+ break;
+ case MAP_TYPE_SEC_GPT_TBL:
+ printf("Sec GPT table");
+ break;
+ case MAP_TYPE_MBR_PART:
+ p = m->map_data;
+ if (p->map_start != 0)
+ printf("Extended ");
+ printf("MBR part ");
+ mbr = p->map_data;
+ for (i = 0; i < 4; i++) {
+ start = le16toh(mbr->mbr_part[i].part_start_hi);
+ start = (start << 16) +
+ le16toh(mbr->mbr_part[i].part_start_lo);
+ if (m->map_start == p->map_start + start)
+ break;
+ }
+ printf("%d", mbr->mbr_part[i].part_typ);
+ break;
+ case MAP_TYPE_GPT_PART:
+ printf("GPT part ");
+ ent = m->map_data;
+ if (show_label) {
+ printf("- \"%s\"",
+ utf16_to_utf8(ent->ent_name));
+ } else {
+ le_uuid_dec(&ent->ent_type, &type);
+ printf("- %s", friendly(&type));
+ }
+ break;
+ case MAP_TYPE_PMBR:
+ printf("PMBR");
+ break;
+ }
+ putchar('\n');
+ m = m->map_next;
+ }
+}
+
+int
+cmd_show(int argc, char *argv[])
+{
+ int ch, fd;
+
+ while ((ch = getopt(argc, argv, "lu")) != -1) {
+ switch(ch) {
+ case 'l':
+ show_label = 1;
+ break;
+ case 'u':
+ show_uuid = 1;
+ break;
+ default:
+ usage_show();
+ }
+ }
+
+ if (argc == optind)
+ usage_show();
+
+ while (optind < argc) {
+ fd = gpt_open(argv[optind++]);
+ if (fd == -1) {
+ warn("unable to open device '%s'", device_name);
+ continue;
+ }
+
+ show(fd);
+
+ gpt_close(fd);
+ }
+
+ return (0);
+}
diff --git a/sbin/growfs/Makefile b/sbin/growfs/Makefile
new file mode 100644
index 0000000..a5e9937
--- /dev/null
+++ b/sbin/growfs/Makefile
@@ -0,0 +1,19 @@
+# @(#)Makefile 8.8 (Berkeley) 6/21/2000
+#
+# $TSHeader: src/sbin/growfs/Makefile,v 1.4 2000/12/05 19:45:24 tomsoft Exp $
+# $FreeBSD$
+#
+
+#GFSDBG=
+
+PROG= growfs
+SRCS= growfs.c
+MAN= growfs.8
+
+WARNS?= 6
+
+.if defined(GFSDBG)
+SRCS+= debug.c
+.endif
+
+.include <bsd.prog.mk>
diff --git a/sbin/growfs/debug.c b/sbin/growfs/debug.c
new file mode 100644
index 0000000..d97fae3
--- /dev/null
+++ b/sbin/growfs/debug.c
@@ -0,0 +1,874 @@
+/*
+ * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz
+ * Copyright (c) 1980, 1989, 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt.
+ *
+ * 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 the University of
+ * California, Berkeley and its contributors, as well as Christoph
+ * Herrmann and Thomas-Henning von Kamptz.
+ * 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 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.
+ *
+ * $TSHeader: src/sbin/growfs/debug.c,v 1.3 2000/12/12 19:31:00 tomsoft Exp $
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/* ********************************************************** INCLUDES ***** */
+#include <sys/param.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include "debug.h"
+
+#ifdef FS_DEBUG
+
+/* *********************************************************** GLOBALS ***** */
+static FILE *dbg_log=NULL;
+static unsigned int indent=0;
+
+/*
+ * prototypes not done here, as they come with debug.h
+ */
+
+/* ********************************************************** dbg_open ***** */
+/*
+ * Open the filehandle where all debug output has to go.
+ */
+void
+dbg_open(const char *fn)
+{
+
+ if (strcmp(fn, "-") == 0)
+ dbg_log=fopen("/dev/stdout", "a");
+ else
+ dbg_log=fopen(fn, "a");
+
+ return;
+}
+
+/* ********************************************************* dbg_close ***** */
+/*
+ * Close the filehandle where all debug output went to.
+ */
+void
+dbg_close(void)
+{
+
+ if(dbg_log) {
+ fclose(dbg_log);
+ dbg_log=NULL;
+ }
+
+ return;
+}
+
+/* ****************************************************** dbg_dump_hex ***** */
+/*
+ * Dump out a full file system block in hex.
+ */
+void
+dbg_dump_hex(struct fs *sb, const char *comment, unsigned char *mem)
+{
+ int i, j, k;
+
+ if(!dbg_log) {
+ return;
+ }
+ fprintf(dbg_log, "===== START HEXDUMP =====\n");
+ fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)mem, comment);
+ indent++;
+ for (i=0; i<sb->fs_bsize; i+=24) {
+ for (j=0; j<3; j++) {
+ for (k=0; k<8; k++) {
+ fprintf(dbg_log, "%02x ", *mem++);
+ }
+ fprintf(dbg_log, " ");
+ }
+ fprintf(dbg_log, "\n");
+ }
+ indent--;
+ fprintf(dbg_log, "===== END HEXDUMP =====\n");
+
+ return;
+}
+
+/* ******************************************************* dbg_dump_fs ***** */
+/*
+ * Dump the superblock.
+ */
+void
+dbg_dump_fs(struct fs *sb, const char *comment)
+{
+#ifdef FSMAXSNAP
+ int j;
+#endif /* FSMAXSNAP */
+
+ if(!dbg_log) {
+ return;
+ }
+
+ fprintf(dbg_log, "===== START SUPERBLOCK =====\n");
+ fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)sb, comment);
+ indent++;
+
+ fprintf(dbg_log, "sblkno int32_t 0x%08x\n",
+ sb->fs_sblkno);
+ fprintf(dbg_log, "cblkno int32_t 0x%08x\n",
+ sb->fs_cblkno);
+ fprintf(dbg_log, "iblkno int32_t 0x%08x\n",
+ sb->fs_iblkno);
+ fprintf(dbg_log, "dblkno int32_t 0x%08x\n",
+ sb->fs_dblkno);
+
+ fprintf(dbg_log, "old_cgoffset int32_t 0x%08x\n",
+ sb->fs_old_cgoffset);
+ fprintf(dbg_log, "old_cgmask int32_t 0x%08x\n",
+ sb->fs_old_cgmask);
+ fprintf(dbg_log, "old_time int32_t %10u\n",
+ (unsigned int)sb->fs_old_time);
+ fprintf(dbg_log, "old_size int32_t 0x%08x\n",
+ sb->fs_old_size);
+ fprintf(dbg_log, "old_dsize int32_t 0x%08x\n",
+ sb->fs_old_dsize);
+ fprintf(dbg_log, "ncg int32_t 0x%08x\n",
+ sb->fs_ncg);
+ fprintf(dbg_log, "bsize int32_t 0x%08x\n",
+ sb->fs_bsize);
+ fprintf(dbg_log, "fsize int32_t 0x%08x\n",
+ sb->fs_fsize);
+ fprintf(dbg_log, "frag int32_t 0x%08x\n",
+ sb->fs_frag);
+
+ fprintf(dbg_log, "minfree int32_t 0x%08x\n",
+ sb->fs_minfree);
+ fprintf(dbg_log, "old_rotdelay int32_t 0x%08x\n",
+ sb->fs_old_rotdelay);
+ fprintf(dbg_log, "old_rps int32_t 0x%08x\n",
+ sb->fs_old_rps);
+
+ fprintf(dbg_log, "bmask int32_t 0x%08x\n",
+ sb->fs_bmask);
+ fprintf(dbg_log, "fmask int32_t 0x%08x\n",
+ sb->fs_fmask);
+ fprintf(dbg_log, "bshift int32_t 0x%08x\n",
+ sb->fs_bshift);
+ fprintf(dbg_log, "fshift int32_t 0x%08x\n",
+ sb->fs_fshift);
+
+ fprintf(dbg_log, "maxcontig int32_t 0x%08x\n",
+ sb->fs_maxcontig);
+ fprintf(dbg_log, "maxbpg int32_t 0x%08x\n",
+ sb->fs_maxbpg);
+
+ fprintf(dbg_log, "fragshift int32_t 0x%08x\n",
+ sb->fs_fragshift);
+ fprintf(dbg_log, "fsbtodb int32_t 0x%08x\n",
+ sb->fs_fsbtodb);
+ fprintf(dbg_log, "sbsize int32_t 0x%08x\n",
+ sb->fs_sbsize);
+ fprintf(dbg_log, "spare1 int32_t[2] 0x%08x 0x%08x\n",
+ sb->fs_spare1[0], sb->fs_spare1[1]);
+ fprintf(dbg_log, "nindir int32_t 0x%08x\n",
+ sb->fs_nindir);
+ fprintf(dbg_log, "inopb int32_t 0x%08x\n",
+ sb->fs_inopb);
+ fprintf(dbg_log, "old_nspf int32_t 0x%08x\n",
+ sb->fs_old_nspf);
+
+ fprintf(dbg_log, "optim int32_t 0x%08x\n",
+ sb->fs_optim);
+
+ fprintf(dbg_log, "old_npsect int32_t 0x%08x\n",
+ sb->fs_old_npsect);
+ fprintf(dbg_log, "old_interleave int32_t 0x%08x\n",
+ sb->fs_old_interleave);
+ fprintf(dbg_log, "old_trackskew int32_t 0x%08x\n",
+ sb->fs_old_trackskew);
+
+ fprintf(dbg_log, "id int32_t[2] 0x%08x 0x%08x\n",
+ sb->fs_id[0], sb->fs_id[1]);
+
+ fprintf(dbg_log, "old_csaddr int32_t 0x%08x\n",
+ sb->fs_old_csaddr);
+ fprintf(dbg_log, "cssize int32_t 0x%08x\n",
+ sb->fs_cssize);
+ fprintf(dbg_log, "cgsize int32_t 0x%08x\n",
+ sb->fs_cgsize);
+
+ fprintf(dbg_log, "spare2 int32_t 0x%08x\n",
+ sb->fs_spare2);
+ fprintf(dbg_log, "old_nsect int32_t 0x%08x\n",
+ sb->fs_old_nsect);
+ fprintf(dbg_log, "old_spc int32_t 0x%08x\n",
+ sb->fs_old_spc);
+
+ fprintf(dbg_log, "old_ncyl int32_t 0x%08x\n",
+ sb->fs_old_ncyl);
+
+ fprintf(dbg_log, "old_cpg int32_t 0x%08x\n",
+ sb->fs_old_cpg);
+ fprintf(dbg_log, "ipg int32_t 0x%08x\n",
+ sb->fs_ipg);
+ fprintf(dbg_log, "fpg int32_t 0x%08x\n",
+ sb->fs_fpg);
+
+ dbg_dump_csum("internal old_cstotal", &sb->fs_old_cstotal);
+
+ fprintf(dbg_log, "fmod int8_t 0x%02x\n",
+ sb->fs_fmod);
+ fprintf(dbg_log, "clean int8_t 0x%02x\n",
+ sb->fs_clean);
+ fprintf(dbg_log, "ronly int8_t 0x%02x\n",
+ sb->fs_ronly);
+ fprintf(dbg_log, "old_flags int8_t 0x%02x\n",
+ sb->fs_old_flags);
+ fprintf(dbg_log, "fsmnt u_char[MAXMNTLEN] \"%s\"\n",
+ sb->fs_fsmnt);
+ fprintf(dbg_log, "volname u_char[MAXVOLLEN] \"%s\"\n",
+ sb->fs_volname);
+ fprintf(dbg_log, "swuid u_int64_t 0x%08x%08x\n",
+ ((unsigned int *)&(sb->fs_swuid))[1],
+ ((unsigned int *)&(sb->fs_swuid))[0]);
+
+ fprintf(dbg_log, "pad int32_t 0x%08x\n",
+ sb->fs_pad);
+
+ fprintf(dbg_log, "cgrotor int32_t 0x%08x\n",
+ sb->fs_cgrotor);
+/*
+ * struct csum[MAXCSBUFS] - is only maintained in memory
+ */
+/* fprintf(dbg_log, " int32_t\n", sb->*fs_maxcluster);*/
+ fprintf(dbg_log, "old_cpc int32_t 0x%08x\n",
+ sb->fs_old_cpc);
+/*
+ * int16_t fs_opostbl[16][8] - is dumped when used in dbg_dump_sptbl
+ */
+ fprintf(dbg_log, "maxbsize int32_t 0x%08x\n",
+ sb->fs_maxbsize);
+ fprintf(dbg_log, "sblockloc int64_t 0x%08x%08x\n",
+ ((unsigned int *)&(sb->fs_sblockloc))[1],
+ ((unsigned int *)&(sb->fs_sblockloc))[0]);
+
+ dbg_dump_csum_total("internal cstotal", &sb->fs_cstotal);
+
+ fprintf(dbg_log, "time ufs_time_t %10u\n",
+ (unsigned int)sb->fs_time);
+
+ fprintf(dbg_log, "size int64_t 0x%08x%08x\n",
+ ((unsigned int *)&(sb->fs_size))[1],
+ ((unsigned int *)&(sb->fs_size))[0]);
+ fprintf(dbg_log, "dsize int64_t 0x%08x%08x\n",
+ ((unsigned int *)&(sb->fs_dsize))[1],
+ ((unsigned int *)&(sb->fs_dsize))[0]);
+ fprintf(dbg_log, "csaddr ufs2_daddr_t 0x%08x%08x\n",
+ ((unsigned int *)&(sb->fs_csaddr))[1],
+ ((unsigned int *)&(sb->fs_csaddr))[0]);
+ fprintf(dbg_log, "pendingblocks int64_t 0x%08x%08x\n",
+ ((unsigned int *)&(sb->fs_pendingblocks))[1],
+ ((unsigned int *)&(sb->fs_pendingblocks))[0]);
+ fprintf(dbg_log, "pendinginodes int32_t 0x%08x\n",
+ sb->fs_pendinginodes);
+
+#ifdef FSMAXSNAP
+ for(j=0; j<FSMAXSNAP; j++) {
+ fprintf(dbg_log, "snapinum int32_t[%2d] 0x%08x\n",
+ j, sb->fs_snapinum[j]);
+ if(!sb->fs_snapinum[j]) { /* list is dense */
+ break;
+ }
+ }
+#endif /* FSMAXSNAP */
+ fprintf(dbg_log, "avgfilesize int32_t 0x%08x\n",
+ sb->fs_avgfilesize);
+ fprintf(dbg_log, "avgfpdir int32_t 0x%08x\n",
+ sb->fs_avgfpdir);
+ fprintf(dbg_log, "save_cgsize int32_t 0x%08x\n",
+ sb->fs_save_cgsize);
+ fprintf(dbg_log, "flags int32_t 0x%08x\n",
+ sb->fs_flags);
+ fprintf(dbg_log, "contigsumsize int32_t 0x%08x\n",
+ sb->fs_contigsumsize);
+ fprintf(dbg_log, "maxsymlinklen int32_t 0x%08x\n",
+ sb->fs_maxsymlinklen);
+ fprintf(dbg_log, "old_inodefmt int32_t 0x%08x\n",
+ sb->fs_old_inodefmt);
+ fprintf(dbg_log, "maxfilesize u_int64_t 0x%08x%08x\n",
+ ((unsigned int *)&(sb->fs_maxfilesize))[1],
+ ((unsigned int *)&(sb->fs_maxfilesize))[0]);
+ fprintf(dbg_log, "qbmask int64_t 0x%08x%08x\n",
+ ((unsigned int *)&(sb->fs_qbmask))[1],
+ ((unsigned int *)&(sb->fs_qbmask))[0]);
+ fprintf(dbg_log, "qfmask int64_t 0x%08x%08x\n",
+ ((unsigned int *)&(sb->fs_qfmask))[1],
+ ((unsigned int *)&(sb->fs_qfmask))[0]);
+ fprintf(dbg_log, "state int32_t 0x%08x\n",
+ sb->fs_state);
+ fprintf(dbg_log, "old_postblformat int32_t 0x%08x\n",
+ sb->fs_old_postblformat);
+ fprintf(dbg_log, "old_nrpos int32_t 0x%08x\n",
+ sb->fs_old_nrpos);
+ fprintf(dbg_log, "spare5 int32_t[2] 0x%08x 0x%08x\n",
+ sb->fs_spare5[0], sb->fs_spare5[1]);
+ fprintf(dbg_log, "magic int32_t 0x%08x\n",
+ sb->fs_magic);
+
+ indent--;
+ fprintf(dbg_log, "===== END SUPERBLOCK =====\n");
+
+ return;
+}
+
+/* ******************************************************* dbg_dump_cg ***** */
+/*
+ * Dump a cylinder group.
+ */
+void
+dbg_dump_cg(const char *comment, struct cg *cgr)
+{
+ int j;
+
+ if(!dbg_log) {
+ return;
+ }
+
+ fprintf(dbg_log, "===== START CYLINDER GROUP =====\n");
+ fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment);
+ indent++;
+
+ fprintf(dbg_log, "magic int32_t 0x%08x\n", cgr->cg_magic);
+ fprintf(dbg_log, "old_time int32_t 0x%08x\n", cgr->cg_old_time);
+ fprintf(dbg_log, "cgx int32_t 0x%08x\n", cgr->cg_cgx);
+ fprintf(dbg_log, "old_ncyl int16_t 0x%04x\n", cgr->cg_old_ncyl);
+ fprintf(dbg_log, "old_niblk int16_t 0x%04x\n", cgr->cg_old_niblk);
+ fprintf(dbg_log, "ndblk int32_t 0x%08x\n", cgr->cg_ndblk);
+ dbg_dump_csum("internal cs", &cgr->cg_cs);
+ fprintf(dbg_log, "rotor int32_t 0x%08x\n", cgr->cg_rotor);
+ fprintf(dbg_log, "frotor int32_t 0x%08x\n", cgr->cg_frotor);
+ fprintf(dbg_log, "irotor int32_t 0x%08x\n", cgr->cg_irotor);
+ for(j=0; j<MAXFRAG; j++) {
+ fprintf(dbg_log, "frsum int32_t[%d] 0x%08x\n", j,
+ cgr->cg_frsum[j]);
+ }
+ fprintf(dbg_log, "old_btotoff int32_t 0x%08x\n", cgr->cg_old_btotoff);
+ fprintf(dbg_log, "old_boff int32_t 0x%08x\n", cgr->cg_old_boff);
+ fprintf(dbg_log, "iusedoff int32_t 0x%08x\n", cgr->cg_iusedoff);
+ fprintf(dbg_log, "freeoff int32_t 0x%08x\n", cgr->cg_freeoff);
+ fprintf(dbg_log, "nextfreeoff int32_t 0x%08x\n",
+ cgr->cg_nextfreeoff);
+ fprintf(dbg_log, "clustersumoff int32_t 0x%08x\n",
+ cgr->cg_clustersumoff);
+ fprintf(dbg_log, "clusteroff int32_t 0x%08x\n",
+ cgr->cg_clusteroff);
+ fprintf(dbg_log, "nclusterblks int32_t 0x%08x\n",
+ cgr->cg_nclusterblks);
+ fprintf(dbg_log, "niblk int32_t 0x%08x\n", cgr->cg_niblk);
+ fprintf(dbg_log, "initediblk int32_t 0x%08x\n", cgr->cg_initediblk);
+ fprintf(dbg_log, "time ufs_time_t %10u\n",
+ (unsigned int)cgr->cg_initediblk);
+
+ indent--;
+ fprintf(dbg_log, "===== END CYLINDER GROUP =====\n");
+
+ return;
+}
+
+/* ***************************************************** dbg_dump_csum ***** */
+/*
+ * Dump a cylinder summary.
+ */
+void
+dbg_dump_csum(const char *comment, struct csum *cs)
+{
+
+ if(!dbg_log) {
+ return;
+ }
+
+ fprintf(dbg_log, "===== START CYLINDER SUMMARY =====\n");
+ fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cs, comment);
+ indent++;
+
+ fprintf(dbg_log, "ndir int32_t 0x%08x\n", cs->cs_ndir);
+ fprintf(dbg_log, "nbfree int32_t 0x%08x\n", cs->cs_nbfree);
+ fprintf(dbg_log, "nifree int32_t 0x%08x\n", cs->cs_nifree);
+ fprintf(dbg_log, "nffree int32_t 0x%08x\n", cs->cs_nffree);
+
+ indent--;
+ fprintf(dbg_log, "===== END CYLINDER SUMMARY =====\n");
+
+ return;
+}
+
+/* ************************************************ dbg_dump_csum_total ***** */
+/*
+ * Dump a cylinder summary.
+ */
+void
+dbg_dump_csum_total(const char *comment, struct csum_total *cs)
+{
+
+ if(!dbg_log) {
+ return;
+ }
+
+ fprintf(dbg_log, "===== START CYLINDER SUMMARY TOTAL =====\n");
+ fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cs, comment);
+ indent++;
+
+ fprintf(dbg_log, "ndir int64_t 0x%08x%08x\n",
+ ((unsigned int *)&(cs->cs_ndir))[1],
+ ((unsigned int *)&(cs->cs_ndir))[0]);
+ fprintf(dbg_log, "nbfree int64_t 0x%08x%08x\n",
+ ((unsigned int *)&(cs->cs_nbfree))[1],
+ ((unsigned int *)&(cs->cs_nbfree))[0]);
+ fprintf(dbg_log, "nifree int64_t 0x%08x%08x\n",
+ ((unsigned int *)&(cs->cs_nifree))[1],
+ ((unsigned int *)&(cs->cs_nifree))[0]);
+ fprintf(dbg_log, "nffree int64_t 0x%08x%08x\n",
+ ((unsigned int *)&(cs->cs_nffree))[1],
+ ((unsigned int *)&(cs->cs_nffree))[0]);
+ fprintf(dbg_log, "numclusters int64_t 0x%08x%08x\n",
+ ((unsigned int *)&(cs->cs_numclusters))[1],
+ ((unsigned int *)&(cs->cs_numclusters))[0]);
+
+ indent--;
+ fprintf(dbg_log, "===== END CYLINDER SUMMARY TOTAL =====\n");
+
+ return;
+}
+/* **************************************************** dbg_dump_inmap ***** */
+/*
+ * Dump the inode allocation map in one cylinder group.
+ */
+void
+dbg_dump_inmap(struct fs *sb, const char *comment, struct cg *cgr)
+{
+ int j,k,l,e;
+ unsigned char *cp;
+
+ if(!dbg_log) {
+ return;
+ }
+
+ fprintf(dbg_log, "===== START INODE ALLOCATION MAP =====\n");
+ fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment);
+ indent++;
+
+ cp=(unsigned char *)cg_inosused(cgr);
+ e=sb->fs_ipg/8;
+ for(j=0; j<e; j+=32) {
+ fprintf(dbg_log, "%08x: ", j);
+ for(k=0; k<32; k+=8) {
+ if(j+k+8<e) {
+ fprintf(dbg_log,
+ "%02x%02x%02x%02x%02x%02x%02x%02x ",
+ cp[0], cp[1], cp[2], cp[3],
+ cp[4], cp[5], cp[6], cp[7]);
+ } else {
+ for(l=0; (l<8)&&(j+k+l<e); l++) {
+ fprintf(dbg_log, "%02x", cp[l]);
+ }
+ }
+ cp+=8;
+ }
+ fprintf(dbg_log, "\n");
+ }
+
+ indent--;
+ fprintf(dbg_log, "===== END INODE ALLOCATION MAP =====\n");
+
+ return;
+}
+
+
+/* **************************************************** dbg_dump_frmap ***** */
+/*
+ * Dump the fragment allocation map in one cylinder group.
+ */
+void
+dbg_dump_frmap(struct fs *sb, const char *comment, struct cg *cgr)
+{
+ int j,k,l,e;
+ unsigned char *cp;
+
+ if(!dbg_log) {
+ return;
+ }
+
+ fprintf(dbg_log, "===== START FRAGMENT ALLOCATION MAP =====\n");
+ fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment);
+ indent++;
+
+ cp=(unsigned char *)cg_blksfree(cgr);
+ if (sb->fs_old_nspf)
+ e=howmany((sb->fs_old_cpg * sb->fs_old_spc / sb->fs_old_nspf), CHAR_BIT);
+ else
+ e = 0;
+ for(j=0; j<e; j+=32) {
+ fprintf(dbg_log, "%08x: ", j);
+ for(k=0; k<32; k+=8) {
+ if(j+k+8<e) {
+ fprintf(dbg_log,
+ "%02x%02x%02x%02x%02x%02x%02x%02x ",
+ cp[0], cp[1], cp[2], cp[3],
+ cp[4], cp[5], cp[6], cp[7]);
+ } else {
+ for(l=0; (l<8)&&(j+k+l<e); l++) {
+ fprintf(dbg_log, "%02x", cp[l]);
+ }
+ }
+ cp+=8;
+ }
+ fprintf(dbg_log, "\n");
+ }
+
+ indent--;
+ fprintf(dbg_log, "===== END FRAGMENT ALLOCATION MAP =====\n");
+
+ return;
+}
+
+/* **************************************************** dbg_dump_clmap ***** */
+/*
+ * Dump the cluster allocation map in one cylinder group.
+ */
+void
+dbg_dump_clmap(struct fs *sb, const char *comment, struct cg *cgr)
+{
+ int j,k,l,e;
+ unsigned char *cp;
+
+ if(!dbg_log) {
+ return;
+ }
+
+ fprintf(dbg_log, "===== START CLUSTER ALLOCATION MAP =====\n");
+ fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment);
+ indent++;
+
+ cp=(unsigned char *)cg_clustersfree(cgr);
+ if (sb->fs_old_nspf)
+ e=howmany(sb->fs_old_cpg * sb->fs_old_spc / (sb->fs_old_nspf << sb->fs_fragshift), CHAR_BIT);
+ else
+ e = 0;
+ for(j=0; j<e; j+=32) {
+ fprintf(dbg_log, "%08x: ", j);
+ for(k=0; k<32; k+=8) {
+ if(j+k+8<e) {
+ fprintf(dbg_log,
+ "%02x%02x%02x%02x%02x%02x%02x%02x ",
+ cp[0], cp[1], cp[2], cp[3],
+ cp[4], cp[5], cp[6], cp[7]);
+ } else {
+ for(l=0; (l<8)&&(j+k+l<e); l++) {
+ fprintf(dbg_log, "%02x", cp[l]);
+ }
+ }
+ cp+=8;
+ }
+ fprintf(dbg_log, "\n");
+ }
+
+ indent--;
+ fprintf(dbg_log, "===== END CLUSTER ALLOCATION MAP =====\n");
+
+ return;
+}
+
+/* **************************************************** dbg_dump_clsum ***** */
+/*
+ * Dump the cluster availability summary of one cylinder group.
+ */
+void
+dbg_dump_clsum(struct fs *sb, const char *comment, struct cg *cgr)
+{
+ int j;
+ int *ip;
+
+ if(!dbg_log) {
+ return;
+ }
+
+ fprintf(dbg_log, "===== START CLUSTER SUMMARY =====\n");
+ fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment);
+ indent++;
+
+ ip=(int *)cg_clustersum(cgr);
+ for(j=0; j<=sb->fs_contigsumsize; j++) {
+ fprintf(dbg_log, "%02d: %8d\n", j, *ip++);
+ }
+
+ indent--;
+ fprintf(dbg_log, "===== END CLUSTER SUMMARY =====\n");
+
+ return;
+}
+
+#ifdef NOT_CURRENTLY
+/*
+ * This code dates from before the UFS2 integration, and doesn't compile
+ * post-UFS2 due to the use of cg_blks(). I'm not sure how best to update
+ * this for UFS2, where the rotational bits of UFS no longer apply, so
+ * will leave it disabled for now; it should probably be re-enabled
+ * specifically for UFS1.
+ */
+/* **************************************************** dbg_dump_sptbl ***** */
+/*
+ * Dump the block summary, and the rotational layout table.
+ */
+void
+dbg_dump_sptbl(struct fs *sb, const char *comment, struct cg *cgr)
+{
+ int j,k;
+ int *ip;
+
+ if(!dbg_log) {
+ return;
+ }
+
+ fprintf(dbg_log,
+ "===== START BLOCK SUMMARY AND POSITION TABLE =====\n");
+ fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment);
+ indent++;
+
+ ip=(int *)cg_blktot(cgr);
+ for(j=0; j<sb->fs_old_cpg; j++) {
+ fprintf(dbg_log, "%2d: %5d = ", j, *ip++);
+ for(k=0; k<sb->fs_old_nrpos; k++) {
+ fprintf(dbg_log, "%4d", cg_blks(sb, cgr, j)[k]);
+ if(k<sb->fs_old_nrpos-1) {
+ fprintf(dbg_log, " + ");
+ }
+ }
+ fprintf(dbg_log, "\n");
+ }
+
+ indent--;
+ fprintf(dbg_log, "===== END BLOCK SUMMARY AND POSITION TABLE =====\n");
+
+ return;
+}
+#endif
+
+/* ************************************************** dbg_dump_ufs1_ino ***** */
+/*
+ * Dump a UFS1 inode structure.
+ */
+void
+dbg_dump_ufs1_ino(struct fs *sb, const char *comment, struct ufs1_dinode *ino)
+{
+ int ictr;
+ int remaining_blocks;
+
+ if(!dbg_log) {
+ return;
+ }
+
+ fprintf(dbg_log, "===== START UFS1 INODE DUMP =====\n");
+ fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)ino, comment);
+ indent++;
+
+ fprintf(dbg_log, "mode u_int16_t 0%o\n", ino->di_mode);
+ fprintf(dbg_log, "nlink int16_t 0x%04x\n", ino->di_nlink);
+ fprintf(dbg_log, "size u_int64_t 0x%08x%08x\n",
+ ((unsigned int *)&(ino->di_size))[1],
+ ((unsigned int *)&(ino->di_size))[0]);
+ fprintf(dbg_log, "atime int32_t 0x%08x\n", ino->di_atime);
+ fprintf(dbg_log, "atimensec int32_t 0x%08x\n",
+ ino->di_atimensec);
+ fprintf(dbg_log, "mtime int32_t 0x%08x\n",
+ ino->di_mtime);
+ fprintf(dbg_log, "mtimensec int32_t 0x%08x\n",
+ ino->di_mtimensec);
+ fprintf(dbg_log, "ctime int32_t 0x%08x\n", ino->di_ctime);
+ fprintf(dbg_log, "ctimensec int32_t 0x%08x\n",
+ ino->di_ctimensec);
+
+ remaining_blocks=howmany(ino->di_size, sb->fs_bsize); /* XXX ts - +1? */
+ for(ictr=0; ictr < MIN(NDADDR, remaining_blocks); ictr++) {
+ fprintf(dbg_log, "db ufs_daddr_t[%x] 0x%08x\n", ictr,
+ ino->di_db[ictr]);
+ }
+ remaining_blocks-=NDADDR;
+ if(remaining_blocks>0) {
+ fprintf(dbg_log, "ib ufs_daddr_t[0] 0x%08x\n",
+ ino->di_ib[0]);
+ }
+ remaining_blocks-=howmany(sb->fs_bsize, sizeof(ufs1_daddr_t));
+ if(remaining_blocks>0) {
+ fprintf(dbg_log, "ib ufs_daddr_t[1] 0x%08x\n",
+ ino->di_ib[1]);
+ }
+#define SQUARE(a) ((a)*(a))
+ remaining_blocks-=SQUARE(howmany(sb->fs_bsize, sizeof(ufs1_daddr_t)));
+#undef SQUARE
+ if(remaining_blocks>0) {
+ fprintf(dbg_log, "ib ufs_daddr_t[2] 0x%08x\n",
+ ino->di_ib[2]);
+ }
+
+ fprintf(dbg_log, "flags u_int32_t 0x%08x\n", ino->di_flags);
+ fprintf(dbg_log, "blocks int32_t 0x%08x\n", ino->di_blocks);
+ fprintf(dbg_log, "gen int32_t 0x%08x\n", ino->di_gen);
+ fprintf(dbg_log, "uid u_int32_t 0x%08x\n", ino->di_uid);
+ fprintf(dbg_log, "gid u_int32_t 0x%08x\n", ino->di_gid);
+
+ indent--;
+ fprintf(dbg_log, "===== END UFS1 INODE DUMP =====\n");
+
+ return;
+}
+
+/* ************************************************** dbg_dump_ufs2_ino ***** */
+/*
+ * Dump a UFS2 inode structure.
+ */
+void
+dbg_dump_ufs2_ino(struct fs *sb, const char *comment, struct ufs2_dinode *ino)
+{
+ int ictr;
+ int remaining_blocks;
+
+ if(!dbg_log) {
+ return;
+ }
+
+ fprintf(dbg_log, "===== START UFS2 INODE DUMP =====\n");
+ fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)ino, comment);
+ indent++;
+
+ fprintf(dbg_log, "mode u_int16_t 0%o\n", ino->di_mode);
+ fprintf(dbg_log, "nlink int16_t 0x%04x\n", ino->di_nlink);
+ fprintf(dbg_log, "uid u_int32_t 0x%08x\n", ino->di_uid);
+ fprintf(dbg_log, "gid u_int32_t 0x%08x\n", ino->di_gid);
+ fprintf(dbg_log, "blksize u_int32_t 0x%08x\n", ino->di_blksize);
+ fprintf(dbg_log, "size u_int64_t 0x%08x%08x\n",
+ ((unsigned int *)&(ino->di_size))[1],
+ ((unsigned int *)&(ino->di_size))[0]);
+ fprintf(dbg_log, "blocks u_int64_t 0x%08x%08x\n",
+ ((unsigned int *)&(ino->di_blocks))[1],
+ ((unsigned int *)&(ino->di_blocks))[0]);
+ fprintf(dbg_log, "atime ufs_time_t %10jd\n", ino->di_atime);
+ fprintf(dbg_log, "mtime ufs_time_t %10jd\n", ino->di_mtime);
+ fprintf(dbg_log, "ctime ufs_time_t %10jd\n", ino->di_ctime);
+ fprintf(dbg_log, "birthtime ufs_time_t %10jd\n", ino->di_birthtime);
+ fprintf(dbg_log, "mtimensec int32_t 0x%08x\n", ino->di_mtimensec);
+ fprintf(dbg_log, "atimensec int32_t 0x%08x\n", ino->di_atimensec);
+ fprintf(dbg_log, "ctimensec int32_t 0x%08x\n", ino->di_ctimensec);
+ fprintf(dbg_log, "birthnsec int32_t 0x%08x\n", ino->di_birthnsec);
+ fprintf(dbg_log, "gen int32_t 0x%08x\n", ino->di_gen);
+ fprintf(dbg_log, "kernflags u_int32_t 0x%08x\n", ino->di_kernflags);
+ fprintf(dbg_log, "flags u_int32_t 0x%08x\n", ino->di_flags);
+ fprintf(dbg_log, "extsize int32_t 0x%08x\n", ino->di_extsize);
+
+ /* XXX: What do we do with di_extb[NXADDR]? */
+
+ remaining_blocks=howmany(ino->di_size, sb->fs_bsize); /* XXX ts - +1? */
+ for(ictr=0; ictr < MIN(NDADDR, remaining_blocks); ictr++) {
+ fprintf(dbg_log, "db ufs2_daddr_t[%x] 0x%16jx\n", ictr,
+ ino->di_db[ictr]);
+ }
+ remaining_blocks-=NDADDR;
+ if(remaining_blocks>0) {
+ fprintf(dbg_log, "ib ufs2_daddr_t[0] 0x%16jx\n",
+ ino->di_ib[0]);
+ }
+ remaining_blocks-=howmany(sb->fs_bsize, sizeof(ufs2_daddr_t));
+ if(remaining_blocks>0) {
+ fprintf(dbg_log, "ib ufs2_daddr_t[1] 0x%16jx\n",
+ ino->di_ib[1]);
+ }
+#define SQUARE(a) ((a)*(a))
+ remaining_blocks-=SQUARE(howmany(sb->fs_bsize, sizeof(ufs2_daddr_t)));
+#undef SQUARE
+ if(remaining_blocks>0) {
+ fprintf(dbg_log, "ib ufs2_daddr_t[2] 0x%16jx\n",
+ ino->di_ib[2]);
+ }
+
+ indent--;
+ fprintf(dbg_log, "===== END UFS2 INODE DUMP =====\n");
+
+ return;
+}
+
+/* ***************************************************** dbg_dump_iblk ***** */
+/*
+ * Dump an indirect block. The iteration to dump a full file has to be
+ * written around.
+ */
+void
+dbg_dump_iblk(struct fs *sb, const char *comment, char *block, size_t length)
+{
+ unsigned int *mem, i, j, size;
+
+ if(!dbg_log) {
+ return;
+ }
+
+ fprintf(dbg_log, "===== START INDIRECT BLOCK DUMP =====\n");
+ fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)block,
+ comment);
+ indent++;
+
+ if (sb->fs_magic == FS_UFS1_MAGIC)
+ size = sizeof(ufs1_daddr_t);
+ else
+ size = sizeof(ufs2_daddr_t);
+
+ mem=(unsigned int *)block;
+ for (i=0; (size_t)i<MIN(howmany(sb->fs_bsize, size),
+ length); i+=8) {
+ fprintf(dbg_log, "%04x: ", i);
+ for (j=0; j<8; j++) {
+ if((size_t)(i+j)<length) {
+ fprintf(dbg_log, "%08X ", *mem++);
+ }
+ }
+ fprintf(dbg_log, "\n");
+ }
+
+ indent--;
+ fprintf(dbg_log, "===== END INDIRECT BLOCK DUMP =====\n");
+
+ return;
+}
+
+#endif /* FS_DEBUG */
+
diff --git a/sbin/growfs/debug.h b/sbin/growfs/debug.h
new file mode 100644
index 0000000..3cad9d9
--- /dev/null
+++ b/sbin/growfs/debug.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz
+ * Copyright (c) 1980, 1989, 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt.
+ *
+ * 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 the University of
+ * California, Berkeley and its contributors, as well as Christoph
+ * Herrmann and Thomas-Henning von Kamptz.
+ * 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 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.
+ *
+ * $TSHeader: src/sbin/growfs/debug.h,v 1.2 2000/11/16 18:43:50 tom Exp $
+ * $FreeBSD$
+ *
+ */
+
+#ifdef FS_DEBUG
+
+/* ********************************************************** INCLUDES ***** */
+#include <sys/param.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+void dbg_open(const char *);
+void dbg_close(void);
+void dbg_dump_hex(struct fs *, const char *, unsigned char *);
+void dbg_dump_fs(struct fs *, const char *);
+void dbg_dump_cg(const char *, struct cg *);
+void dbg_dump_csum(const char *, struct csum *);
+void dbg_dump_csum_total(const char *, struct csum_total *);
+void dbg_dump_ufs1_ino(struct fs *, const char *, struct ufs1_dinode *);
+void dbg_dump_ufs2_ino(struct fs *, const char *, struct ufs2_dinode *);
+void dbg_dump_iblk(struct fs *, const char *, char *, size_t);
+void dbg_dump_inmap(struct fs *, const char *, struct cg *);
+void dbg_dump_frmap(struct fs *, const char *, struct cg *);
+void dbg_dump_clmap(struct fs *, const char *, struct cg *);
+void dbg_dump_clsum(struct fs *, const char *, struct cg *);
+void dbg_dump_sptbl(struct fs *, const char *, struct cg *);
+
+#define DBG_OPEN(P) dbg_open((P))
+#define DBG_CLOSE dbg_close()
+#define DBG_DUMP_HEX(F,C,M) dbg_dump_hex((F),(C),(M))
+#define DBG_DUMP_FS(F,C) dbg_dump_fs((F),(C))
+#define DBG_DUMP_CG(F,C,M) dbg_dump_cg((C),(M))
+#define DBG_DUMP_CSUM(F,C,M) dbg_dump_csum((C),(M))
+#define DBG_DUMP_INO(F,C,M) (F)->fs_magic == FS_UFS1_MAGIC \
+ ? dbg_dump_ufs1_ino((F),(C),(struct ufs1_dinode *)(M)) \
+ : dbg_dump_ufs2_ino((F),(C),(struct ufs2_dinode *)(M))
+#define DBG_DUMP_IBLK(F,C,M,L) dbg_dump_iblk((F),(C),(M),(L))
+#define DBG_DUMP_INMAP(F,C,M) dbg_dump_inmap((F),(C),(M))
+#define DBG_DUMP_FRMAP(F,C,M) dbg_dump_frmap((F),(C),(M))
+#define DBG_DUMP_CLMAP(F,C,M) dbg_dump_clmap((F),(C),(M))
+#define DBG_DUMP_CLSUM(F,C,M) dbg_dump_clsum((F),(C),(M))
+#ifdef NOT_CURRENTLY
+#define DBG_DUMP_SPTBL(F,C,M) dbg_dump_sptbl((F),(C),(M))
+#endif
+
+#define DL_TRC 0x01
+#define DL_INFO 0x02
+extern int _dbg_lvl_;
+
+#define DBG_FUNC(N) char __FKT__[] = {N};
+#define DBG_ENTER if(_dbg_lvl_ & DL_TRC) { \
+ fprintf(stderr, "~>%s: %s\n", __FILE__, __FKT__ ); \
+ }
+#define DBG_LEAVE if(_dbg_lvl_ & DL_TRC) { \
+ fprintf(stderr, "~<%s[%d]: %s\n", __FILE__, __LINE__, __FKT__ ); \
+ }
+#define DBG_TRC if(_dbg_lvl_ & DL_TRC) { \
+ fprintf(stderr, "~=%s[%d]: %s\n", __FILE__, __LINE__, __FKT__ ); \
+ }
+#define DBG_PRINT0(A) if(_dbg_lvl_ & DL_INFO) { \
+ fprintf(stderr, "~ %s", (A)); \
+ }
+#define DBG_PRINT1(A,B) if(_dbg_lvl_ & DL_INFO) { \
+ fprintf(stderr, "~ "); \
+ fprintf(stderr, (A), (B)); \
+ }
+#define DBG_PRINT2(A,B,C) if(_dbg_lvl_ & DL_INFO) { \
+ fprintf(stderr, "~ "); \
+ fprintf(stderr, (A), (B), (C)); \
+ }
+#define DBG_PRINT3(A,B,C,D) if(_dbg_lvl_ & DL_INFO) { \
+ fprintf(stderr, "~ "); \
+ fprintf(stderr, (A), (B), (C), (D)); \
+ }
+#define DBG_PRINT4(A,B,C,D,E) if(_dbg_lvl_ & DL_INFO) { \
+ fprintf(stderr, "~ "); \
+ fprintf(stderr, (A), (B), (C), (D), (E)); \
+ }
+#else /* not FS_DEBUG */
+
+#define DBG_OPEN(P)
+#define DBG_CLOSE
+#define DBG_DUMP_HEX(F,C,M)
+#define DBG_DUMP_FS(F,C)
+#define DBG_DUMP_CG(F,C,M)
+#define DBG_DUMP_CSUM(F,C,M)
+#define DBG_DUMP_INO(F,C,M)
+#define DBG_DUMP_IBLK(F,C,M,L)
+#define DBG_DUMP_INMAP(F,C,M)
+#define DBG_DUMP_FRMAP(F,C,M)
+#define DBG_DUMP_CLMAP(F,C,M)
+#define DBG_DUMP_CLSUM(F,C,M)
+#define DBG_DUMP_SPTBL(F,C,M)
+#define DBG_FUNC(N)
+#define DBG_ENTER
+#define DBG_TRC
+#define DBG_LEAVE
+#define DBG_PRINT0(A)
+#define DBG_PRINT1(A,B)
+#define DBG_PRINT2(A,B,C)
+#define DBG_PRINT3(A,B,C,D)
+#define DBG_PRINT4(A,B,C,D,E)
+
+#endif /* FS_DEBUG */
diff --git a/sbin/growfs/growfs.8 b/sbin/growfs/growfs.8
new file mode 100644
index 0000000..387e511
--- /dev/null
+++ b/sbin/growfs/growfs.8
@@ -0,0 +1,196 @@
+.\" Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz
+.\" Copyright (c) 1980, 1989, 1993 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt.
+.\"
+.\" 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 the University of
+.\" California, Berkeley and its contributors, as well as Christoph
+.\" Herrmann and Thomas-Henning von Kamptz.
+.\" 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 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.
+.\"
+.\" $TSHeader: src/sbin/growfs/growfs.8,v 1.3 2000/12/12 19:31:00 tomsoft Exp $
+.\" $FreeBSD$
+.\"
+.Dd September 8, 2000
+.Dt GROWFS 8
+.Os
+.Sh NAME
+.Nm growfs
+.Nd grow size of an existing ufs file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl Ny
+.Op Fl s Ar size
+.Ar special
+.Sh DESCRIPTION
+The
+.Nm
+utility extends the
+.Xr newfs 8
+program.
+Before starting
+.Nm
+the disk must be labeled to a bigger size using
+.Xr bsdlabel 8 .
+If you wish to grow a file system beyond the boundary of
+the slice it resides in, you must re-size the slice using
+.Xr fdisk 8
+before running
+.Nm .
+If you are using volumes you must enlarge them by using
+.Xr vinum 8 .
+The
+.Nm
+utility extends the size of the file system on the specified special file.
+Currently
+.Nm
+can only enlarge unmounted file systems.
+Do not try enlarging a mounted file system, your system may panic and you will
+not be able to use the file system any longer.
+Most of the
+.Xr newfs 8
+options cannot be changed by
+.Nm .
+In fact, you can only increase the size of the file system.
+Use
+.Xr tunefs 8
+for other changes.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl N
+.Dq Test mode .
+Causes the new file system parameters to be printed out without actually
+enlarging the file system.
+.It Fl y
+.Dq Expert mode .
+Usually
+.Nm
+will ask you if you took a backup of your data before and will do some tests
+whether
+.Ar special
+is currently mounted or whether there are any active snapshots on the file
+system specified.
+This will be suppressed.
+So use this option with great care!
+.It Fl s Ar size
+Determines the
+.Ar size
+of the file system after enlarging in sectors.
+This value defaults to the size of the raw partition specified in
+.Ar special
+(in other words,
+.Nm
+will enlarge the file system to the size of the entire partition).
+.El
+.Sh EXAMPLES
+.Dl growfs -s 4194304 /dev/vinum/testvol
+.Pp
+will enlarge
+.Pa /dev/vinum/testvol
+up to 2GB if there is enough space in
+.Pa /dev/vinum/testvol .
+.Sh SEE ALSO
+.Xr bsdlabel 8 ,
+.Xr dumpfs 8 ,
+.Xr fdisk 8 ,
+.Xr ffsinfo 8 ,
+.Xr fsck 8 ,
+.Xr newfs 8 ,
+.Xr tunefs 8 ,
+.Xr vinum 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 4.4 .
+.Sh AUTHORS
+.An Christoph Herrmann Aq chm@FreeBSD.org
+.An Thomas-Henning von Kamptz Aq tomsoft@FreeBSD.org
+.An The GROWFS team Aq growfs@Tomsoft.COM
+.Sh BUGS
+The
+.Nm
+utility works starting with
+.Fx
+3.x.
+There may be cases on
+.Fx
+3.x only, when
+.Nm
+does not recognize properly whether or not the file system is mounted and
+exits with an error message.
+Then please use
+.Nm
+.Fl y
+if you are sure that the file system is not mounted.
+It is also recommended to always use
+.Xr fsck 8
+after enlarging (just to be on the safe side).
+.Pp
+For enlarging beyond certain limits, it is essential to have some free blocks
+available in the first cylinder group.
+If that space is not available in the first cylinder group, a critical data
+structure has to be relocated into one of the new available cylinder groups.
+On
+.Fx
+3.x this will cause problems with
+.Xr fsck 8
+afterwards.
+So
+.Xr fsck 8
+needs to be patched if you want to use
+.Nm
+for
+.Fx
+3.x.
+This patch is already integrated in
+.Fx
+starting with
+.Fx 4.4 .
+To avoid an unexpected relocation of that structure it is possible to use
+.Nm ffsinfo
+.Fl g Ar 0
+.Fl l Ar 4
+on the first cylinder group to verify that
+.Em nbfree
+in the CYLINDER SUMMARY (internal cs) of the CYLINDER GROUP
+.Em cgr0
+has enough blocks.
+As a rule of thumb for default file system parameters one block is needed for
+every 2 GB of total file system size.
+.Pp
+Normally
+.Nm
+writes this critical structure to disk and reads it again later for doing more
+updates.
+This read operation will provide unexpected data when using
+.Fl N .
+Therefore, this part cannot really be simulated and will be skipped in test
+mode.
diff --git a/sbin/growfs/growfs.c b/sbin/growfs/growfs.c
new file mode 100644
index 0000000..fa5b680
--- /dev/null
+++ b/sbin/growfs/growfs.c
@@ -0,0 +1,2520 @@
+/*
+ * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz
+ * Copyright (c) 1980, 1989, 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt.
+ *
+ * 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 the University of
+ * California, Berkeley and its contributors, as well as Christoph
+ * Herrmann and Thomas-Henning von Kamptz.
+ * 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 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.
+ *
+ * $TSHeader: src/sbin/growfs/growfs.c,v 1.5 2000/12/12 19:31:00 tomsoft Exp $
+ *
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz\n\
+Copyright (c) 1980, 1989, 1993 The Regents of the University of California.\n\
+All rights reserved.\n";
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* ********************************************************** INCLUDES ***** */
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/disk.h>
+
+#include <stdio.h>
+#include <paths.h>
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include "debug.h"
+
+/* *************************************************** GLOBALS & TYPES ***** */
+#ifdef FS_DEBUG
+int _dbg_lvl_ = (DL_INFO); /* DL_TRC */
+#endif /* FS_DEBUG */
+
+static union {
+ struct fs fs;
+ char pad[SBLOCKSIZE];
+} fsun1, fsun2;
+#define sblock fsun1.fs /* the new superblock */
+#define osblock fsun2.fs /* the old superblock */
+
+/*
+ * Possible superblock locations ordered from most to least likely.
+ */
+static int sblock_try[] = SBLOCKSEARCH;
+static ufs2_daddr_t sblockloc;
+
+static union {
+ struct cg cg;
+ char pad[MAXBSIZE];
+} cgun1, cgun2;
+#define acg cgun1.cg /* a cylinder cgroup (new) */
+#define aocg cgun2.cg /* an old cylinder group */
+
+static char ablk[MAXBSIZE]; /* a block */
+
+static struct csum *fscs; /* cylinder summary */
+
+union dinode {
+ struct ufs1_dinode dp1;
+ struct ufs2_dinode dp2;
+};
+#define DIP(dp, field) \
+ ((sblock.fs_magic == FS_UFS1_MAGIC) ? \
+ (uint32_t)(dp)->dp1.field : (dp)->dp2.field)
+#define DIP_SET(dp, field, val) do { \
+ if (sblock.fs_magic == FS_UFS1_MAGIC) \
+ (dp)->dp1.field = (val); \
+ else \
+ (dp)->dp2.field = (val); \
+ } while (0)
+static ufs2_daddr_t inoblk; /* inode block address */
+static char inobuf[MAXBSIZE]; /* inode block */
+ino_t maxino; /* last valid inode */
+static int unlabeled; /* unlabeled partition, e.g. vinum volume etc. */
+
+/*
+ * An array of elements of type struct gfs_bpp describes all blocks to
+ * be relocated in order to free the space needed for the cylinder group
+ * summary for all cylinder groups located in the first cylinder group.
+ */
+struct gfs_bpp {
+ ufs2_daddr_t old; /* old block number */
+ ufs2_daddr_t new; /* new block number */
+#define GFS_FL_FIRST 1
+#define GFS_FL_LAST 2
+ unsigned int flags; /* special handling required */
+ int found; /* how many references were updated */
+};
+
+/* ******************************************************** PROTOTYPES ***** */
+static void growfs(int, int, unsigned int);
+static void rdfs(ufs2_daddr_t, size_t, void *, int);
+static void wtfs(ufs2_daddr_t, size_t, void *, int, unsigned int);
+static ufs2_daddr_t alloc(void);
+static int charsperline(void);
+static void usage(void);
+static int isblock(struct fs *, unsigned char *, int);
+static void clrblock(struct fs *, unsigned char *, int);
+static void setblock(struct fs *, unsigned char *, int);
+static void initcg(int, time_t, int, unsigned int);
+static void updjcg(int, time_t, int, int, unsigned int);
+static void updcsloc(time_t, int, int, unsigned int);
+static struct disklabel *get_disklabel(int);
+static void return_disklabel(int, struct disklabel *, unsigned int);
+static union dinode *ginode(ino_t, int, int);
+static void frag_adjust(ufs2_daddr_t, int);
+static int cond_bl_upd(ufs2_daddr_t *, struct gfs_bpp *, int, int,
+ unsigned int);
+static void updclst(int);
+static void updrefs(int, ino_t, struct gfs_bpp *, int, int, unsigned int);
+static void indirchk(ufs_lbn_t, ufs_lbn_t, ufs2_daddr_t, ufs_lbn_t,
+ struct gfs_bpp *, int, int, unsigned int);
+static void get_dev_size(int, int *);
+
+/* ************************************************************ growfs ***** */
+/*
+ * Here we actually start growing the file system. We basically read the
+ * cylinder summary from the first cylinder group as we want to update
+ * this on the fly during our various operations. First we handle the
+ * changes in the former last cylinder group. Afterwards we create all new
+ * cylinder groups. Now we handle the cylinder group containing the
+ * cylinder summary which might result in a relocation of the whole
+ * structure. In the end we write back the updated cylinder summary, the
+ * new superblock, and slightly patched versions of the super block
+ * copies.
+ */
+static void
+growfs(int fsi, int fso, unsigned int Nflag)
+{
+ DBG_FUNC("growfs")
+ int i;
+ int cylno, j;
+ time_t utime;
+ int width;
+ char tmpbuf[100];
+#ifdef FSIRAND
+ static int randinit=0;
+
+ DBG_ENTER;
+
+ if (!randinit) {
+ randinit = 1;
+ srandomdev();
+ }
+#else /* not FSIRAND */
+
+ DBG_ENTER;
+
+#endif /* FSIRAND */
+ time(&utime);
+
+ /*
+ * Get the cylinder summary into the memory.
+ */
+ fscs = (struct csum *)calloc((size_t)1, (size_t)sblock.fs_cssize);
+ if(fscs == NULL) {
+ errx(1, "calloc failed");
+ }
+ for (i = 0; i < osblock.fs_cssize; i += osblock.fs_bsize) {
+ rdfs(fsbtodb(&osblock, osblock.fs_csaddr +
+ numfrags(&osblock, i)), (size_t)MIN(osblock.fs_cssize - i,
+ osblock.fs_bsize), (void *)(((char *)fscs)+i), fsi);
+ }
+
+#ifdef FS_DEBUG
+{
+ struct csum *dbg_csp;
+ int dbg_csc;
+ char dbg_line[80];
+
+ dbg_csp=fscs;
+ for(dbg_csc=0; dbg_csc<osblock.fs_ncg; dbg_csc++) {
+ snprintf(dbg_line, sizeof(dbg_line),
+ "%d. old csum in old location", dbg_csc);
+ DBG_DUMP_CSUM(&osblock,
+ dbg_line,
+ dbg_csp++);
+ }
+}
+#endif /* FS_DEBUG */
+ DBG_PRINT0("fscs read\n");
+
+ /*
+ * Do all needed changes in the former last cylinder group.
+ */
+ updjcg(osblock.fs_ncg-1, utime, fsi, fso, Nflag);
+
+ /*
+ * Dump out summary information about file system.
+ */
+# define B2MBFACTOR (1 / (1024.0 * 1024.0))
+ printf("growfs: %.1fMB (%jd sectors) block size %d, fragment size %d\n",
+ (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR,
+ (intmax_t)fsbtodb(&sblock, sblock.fs_size), sblock.fs_bsize,
+ sblock.fs_fsize);
+ printf("\tusing %d cylinder groups of %.2fMB, %d blks, %d inodes.\n",
+ sblock.fs_ncg, (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR,
+ sblock.fs_fpg / sblock.fs_frag, sblock.fs_ipg);
+ if (sblock.fs_flags & FS_DOSOFTDEP)
+ printf("\twith soft updates\n");
+# undef B2MBFACTOR
+
+ /*
+ * Now build the cylinders group blocks and
+ * then print out indices of cylinder groups.
+ */
+ printf("super-block backups (for fsck -b #) at:\n");
+ i = 0;
+ width = charsperline();
+
+ /*
+ * Iterate for only the new cylinder groups.
+ */
+ for (cylno = osblock.fs_ncg; cylno < sblock.fs_ncg; cylno++) {
+ initcg(cylno, utime, fso, Nflag);
+ j = sprintf(tmpbuf, " %d%s",
+ (int)fsbtodb(&sblock, cgsblock(&sblock, cylno)),
+ cylno < (sblock.fs_ncg-1) ? "," : "" );
+ if (i + j >= width) {
+ printf("\n");
+ i = 0;
+ }
+ i += j;
+ printf("%s", tmpbuf);
+ fflush(stdout);
+ }
+ printf("\n");
+
+ /*
+ * Do all needed changes in the first cylinder group.
+ * allocate blocks in new location
+ */
+ updcsloc(utime, fsi, fso, Nflag);
+
+ /*
+ * Now write the cylinder summary back to disk.
+ */
+ for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) {
+ wtfs(fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)),
+ (size_t)MIN(sblock.fs_cssize - i, sblock.fs_bsize),
+ (void *)(((char *)fscs) + i), fso, Nflag);
+ }
+ DBG_PRINT0("fscs written\n");
+
+#ifdef FS_DEBUG
+{
+ struct csum *dbg_csp;
+ int dbg_csc;
+ char dbg_line[80];
+
+ dbg_csp=fscs;
+ for(dbg_csc=0; dbg_csc<sblock.fs_ncg; dbg_csc++) {
+ snprintf(dbg_line, sizeof(dbg_line),
+ "%d. new csum in new location", dbg_csc);
+ DBG_DUMP_CSUM(&sblock,
+ dbg_line,
+ dbg_csp++);
+ }
+}
+#endif /* FS_DEBUG */
+
+ /*
+ * Now write the new superblock back to disk.
+ */
+ sblock.fs_time = utime;
+ wtfs(sblockloc, (size_t)SBLOCKSIZE, (void *)&sblock, fso, Nflag);
+ DBG_PRINT0("sblock written\n");
+ DBG_DUMP_FS(&sblock,
+ "new initial sblock");
+
+ /*
+ * Clean up the dynamic fields in our superblock copies.
+ */
+ sblock.fs_fmod = 0;
+ sblock.fs_clean = 1;
+ sblock.fs_ronly = 0;
+ sblock.fs_cgrotor = 0;
+ sblock.fs_state = 0;
+ memset((void *)&sblock.fs_fsmnt, 0, sizeof(sblock.fs_fsmnt));
+ sblock.fs_flags &= FS_DOSOFTDEP;
+
+ /*
+ * XXX
+ * The following fields are currently distributed from the superblock
+ * to the copies:
+ * fs_minfree
+ * fs_rotdelay
+ * fs_maxcontig
+ * fs_maxbpg
+ * fs_minfree,
+ * fs_optim
+ * fs_flags regarding SOFTPDATES
+ *
+ * We probably should rather change the summary for the cylinder group
+ * statistics here to the value of what would be in there, if the file
+ * system were created initially with the new size. Therefor we still
+ * need to find an easy way of calculating that.
+ * Possibly we can try to read the first superblock copy and apply the
+ * "diffed" stats between the old and new superblock by still copying
+ * certain parameters onto that.
+ */
+
+ /*
+ * Write out the duplicate super blocks.
+ */
+ for (cylno = 0; cylno < sblock.fs_ncg; cylno++) {
+ wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)),
+ (size_t)SBLOCKSIZE, (void *)&sblock, fso, Nflag);
+ }
+ DBG_PRINT0("sblock copies written\n");
+ DBG_DUMP_FS(&sblock,
+ "new other sblocks");
+
+ DBG_LEAVE;
+ return;
+}
+
+/* ************************************************************ initcg ***** */
+/*
+ * This creates a new cylinder group structure, for more details please see
+ * the source of newfs(8), as this function is taken over almost unchanged.
+ * As this is never called for the first cylinder group, the special
+ * provisions for that case are removed here.
+ */
+static void
+initcg(int cylno, time_t utime, int fso, unsigned int Nflag)
+{
+ DBG_FUNC("initcg")
+ static void *iobuf;
+ long d, dlower, dupper, blkno, start;
+ ufs2_daddr_t i, cbase, dmax;
+ struct ufs1_dinode *dp1;
+ struct ufs2_dinode *dp2;
+ struct csum *cs;
+
+ if (iobuf == NULL && (iobuf = malloc(sblock.fs_bsize)) == NULL) {
+ errx(37, "panic: cannot allocate I/O buffer");
+ }
+ /*
+ * Determine block bounds for cylinder group.
+ * Allow space for super block summary information in first
+ * cylinder group.
+ */
+ cbase = cgbase(&sblock, cylno);
+ dmax = cbase + sblock.fs_fpg;
+ if (dmax > sblock.fs_size)
+ dmax = sblock.fs_size;
+ dlower = cgsblock(&sblock, cylno) - cbase;
+ dupper = cgdmin(&sblock, cylno) - cbase;
+ if (cylno == 0) /* XXX fscs may be relocated */
+ dupper += howmany(sblock.fs_cssize, sblock.fs_fsize);
+ cs = &fscs[cylno];
+ memset(&acg, 0, sblock.fs_cgsize);
+ acg.cg_time = utime;
+ acg.cg_magic = CG_MAGIC;
+ acg.cg_cgx = cylno;
+ acg.cg_niblk = sblock.fs_ipg;
+ acg.cg_initediblk = sblock.fs_ipg;
+ acg.cg_ndblk = dmax - cbase;
+ if (sblock.fs_contigsumsize > 0)
+ acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag;
+ start = &acg.cg_space[0] - (u_char *)(&acg.cg_firstfield);
+ if (sblock.fs_magic == FS_UFS2_MAGIC) {
+ acg.cg_iusedoff = start;
+ } else {
+ acg.cg_old_ncyl = sblock.fs_old_cpg;
+ acg.cg_old_time = acg.cg_time;
+ acg.cg_time = 0;
+ acg.cg_old_niblk = acg.cg_niblk;
+ acg.cg_niblk = 0;
+ acg.cg_initediblk = 0;
+ acg.cg_old_btotoff = start;
+ acg.cg_old_boff = acg.cg_old_btotoff +
+ sblock.fs_old_cpg * sizeof(int32_t);
+ acg.cg_iusedoff = acg.cg_old_boff +
+ sblock.fs_old_cpg * sizeof(u_int16_t);
+ }
+ acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT);
+ acg.cg_nextfreeoff = acg.cg_freeoff + howmany(sblock.fs_fpg, CHAR_BIT);
+ if (sblock.fs_contigsumsize > 0) {
+ acg.cg_clustersumoff =
+ roundup(acg.cg_nextfreeoff, sizeof(u_int32_t));
+ acg.cg_clustersumoff -= sizeof(u_int32_t);
+ acg.cg_clusteroff = acg.cg_clustersumoff +
+ (sblock.fs_contigsumsize + 1) * sizeof(u_int32_t);
+ acg.cg_nextfreeoff = acg.cg_clusteroff +
+ howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT);
+ }
+ if (acg.cg_nextfreeoff > sblock.fs_cgsize) {
+ /*
+ * This should never happen as we would have had that panic
+ * already on file system creation
+ */
+ errx(37, "panic: cylinder group too big");
+ }
+ acg.cg_cs.cs_nifree += sblock.fs_ipg;
+ if (cylno == 0)
+ for (i = 0; i < ROOTINO; i++) {
+ setbit(cg_inosused(&acg), i);
+ acg.cg_cs.cs_nifree--;
+ }
+ /*
+ * XXX Newfs writes out two blocks of initialized inodes
+ * unconditionally. Should we check here to make sure that they
+ * were actually written?
+ */
+ if (sblock.fs_magic == FS_UFS1_MAGIC) {
+ bzero(iobuf, sblock.fs_bsize);
+ for (i = 2 * sblock.fs_frag; i < sblock.fs_ipg / INOPF(&sblock);
+ i += sblock.fs_frag) {
+ dp1 = (struct ufs1_dinode *)iobuf;
+ dp2 = (struct ufs2_dinode *)iobuf;
+#ifdef FSIRAND
+ for (j = 0; j < INOPB(&sblock); j++)
+ if (sblock.fs_magic == FS_UFS1_MAGIC) {
+ dp1->di_gen = random();
+ dp1++;
+ } else {
+ dp2->di_gen = random();
+ dp2++;
+ }
+#endif
+ wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno) + i),
+ sblock.fs_bsize, iobuf, fso, Nflag);
+ }
+ }
+ if (cylno > 0) {
+ /*
+ * In cylno 0, beginning space is reserved
+ * for boot and super blocks.
+ */
+ for (d = 0; d < dlower; d += sblock.fs_frag) {
+ blkno = d / sblock.fs_frag;
+ setblock(&sblock, cg_blksfree(&acg), blkno);
+ if (sblock.fs_contigsumsize > 0)
+ setbit(cg_clustersfree(&acg), blkno);
+ acg.cg_cs.cs_nbfree++;
+ }
+ sblock.fs_dsize += dlower;
+ }
+ sblock.fs_dsize += acg.cg_ndblk - dupper;
+ if ((i = dupper % sblock.fs_frag)) {
+ acg.cg_frsum[sblock.fs_frag - i]++;
+ for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) {
+ setbit(cg_blksfree(&acg), dupper);
+ acg.cg_cs.cs_nffree++;
+ }
+ }
+ for (d = dupper; d + sblock.fs_frag <= acg.cg_ndblk;
+ d += sblock.fs_frag) {
+ blkno = d / sblock.fs_frag;
+ setblock(&sblock, cg_blksfree(&acg), blkno);
+ if (sblock.fs_contigsumsize > 0)
+ setbit(cg_clustersfree(&acg), blkno);
+ acg.cg_cs.cs_nbfree++;
+ }
+ if (d < acg.cg_ndblk) {
+ acg.cg_frsum[acg.cg_ndblk - d]++;
+ for (; d < acg.cg_ndblk; d++) {
+ setbit(cg_blksfree(&acg), d);
+ acg.cg_cs.cs_nffree++;
+ }
+ }
+ if (sblock.fs_contigsumsize > 0) {
+ int32_t *sump = cg_clustersum(&acg);
+ u_char *mapp = cg_clustersfree(&acg);
+ int map = *mapp++;
+ int bit = 1;
+ int run = 0;
+
+ for (i = 0; i < acg.cg_nclusterblks; i++) {
+ if ((map & bit) != 0)
+ run++;
+ else if (run != 0) {
+ if (run > sblock.fs_contigsumsize)
+ run = sblock.fs_contigsumsize;
+ sump[run]++;
+ run = 0;
+ }
+ if ((i & (CHAR_BIT - 1)) != CHAR_BIT - 1)
+ bit <<= 1;
+ else {
+ map = *mapp++;
+ bit = 1;
+ }
+ }
+ if (run != 0) {
+ if (run > sblock.fs_contigsumsize)
+ run = sblock.fs_contigsumsize;
+ sump[run]++;
+ }
+ }
+ sblock.fs_cstotal.cs_ndir += acg.cg_cs.cs_ndir;
+ sblock.fs_cstotal.cs_nffree += acg.cg_cs.cs_nffree;
+ sblock.fs_cstotal.cs_nbfree += acg.cg_cs.cs_nbfree;
+ sblock.fs_cstotal.cs_nifree += acg.cg_cs.cs_nifree;
+ *cs = acg.cg_cs;
+ wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)),
+ sblock.fs_bsize, (char *)&acg, fso, Nflag);
+ DBG_DUMP_CG(&sblock,
+ "new cg",
+ &acg);
+
+ DBG_LEAVE;
+ return;
+}
+
+/* ******************************************************* frag_adjust ***** */
+/*
+ * Here we add or subtract (sign +1/-1) the available fragments in a given
+ * block to or from the fragment statistics. By subtracting before and adding
+ * after an operation on the free frag map we can easy update the fragment
+ * statistic, which seems to be otherwise a rather complex operation.
+ */
+static void
+frag_adjust(ufs2_daddr_t frag, int sign)
+{
+ DBG_FUNC("frag_adjust")
+ int fragsize;
+ int f;
+
+ DBG_ENTER;
+
+ fragsize=0;
+ /*
+ * Here frag only needs to point to any fragment in the block we want
+ * to examine.
+ */
+ for(f=rounddown(frag, sblock.fs_frag);
+ f<roundup(frag+1, sblock.fs_frag);
+ f++) {
+ /*
+ * Count contiguous free fragments.
+ */
+ if(isset(cg_blksfree(&acg), f)) {
+ fragsize++;
+ } else {
+ if(fragsize && fragsize<sblock.fs_frag) {
+ /*
+ * We found something in between.
+ */
+ acg.cg_frsum[fragsize]+=sign;
+ DBG_PRINT2("frag_adjust [%d]+=%d\n",
+ fragsize,
+ sign);
+ }
+ fragsize=0;
+ }
+ }
+ if(fragsize && fragsize<sblock.fs_frag) {
+ /*
+ * We found something.
+ */
+ acg.cg_frsum[fragsize]+=sign;
+ DBG_PRINT2("frag_adjust [%d]+=%d\n",
+ fragsize,
+ sign);
+ }
+ DBG_PRINT2("frag_adjust [[%d]]+=%d\n",
+ fragsize,
+ sign);
+
+ DBG_LEAVE;
+ return;
+}
+
+/* ******************************************************* cond_bl_upd ***** */
+/*
+ * Here we conditionally update a pointer to a fragment. We check for all
+ * relocated blocks if any of its fragments is referenced by the current
+ * field, and update the pointer to the respective fragment in our new
+ * block. If we find a reference we write back the block immediately,
+ * as there is no easy way for our general block reading engine to figure
+ * out if a write back operation is needed.
+ */
+static int
+cond_bl_upd(ufs2_daddr_t *block, struct gfs_bpp *field, int fsi, int fso,
+ unsigned int Nflag)
+{
+ DBG_FUNC("cond_bl_upd")
+ struct gfs_bpp *f;
+ ufs2_daddr_t src, dst;
+ int fragnum;
+ void *ibuf;
+
+ DBG_ENTER;
+
+ f = field;
+ for (f = field; f->old != 0; f++) {
+ src = *block;
+ if (fragstoblks(&sblock, src) != f->old)
+ continue;
+ /*
+ * The fragment is part of the block, so update.
+ */
+ dst = blkstofrags(&sblock, f->new);
+ fragnum = fragnum(&sblock, src);
+ *block = dst + fragnum;
+ f->found++;
+ DBG_PRINT3("scg (%jd->%jd)[%d] reference updated\n",
+ (intmax_t)f->old,
+ (intmax_t)f->new,
+ fragnum);
+
+ /*
+ * Copy the block back immediately.
+ *
+ * XXX If src is is from an indirect block we have
+ * to implement copy on write here in case of
+ * active snapshots.
+ */
+ ibuf = malloc(sblock.fs_bsize);
+ if (!ibuf)
+ errx(1, "malloc failed");
+ src -= fragnum;
+ rdfs(fsbtodb(&sblock, src), (size_t)sblock.fs_bsize, ibuf, fsi);
+ wtfs(dst, (size_t)sblock.fs_bsize, ibuf, fso, Nflag);
+ free(ibuf);
+ /*
+ * The same block can't be found again in this loop.
+ */
+ return (1);
+ }
+
+ DBG_LEAVE;
+ return (0);
+}
+
+/* ************************************************************ updjcg ***** */
+/*
+ * Here we do all needed work for the former last cylinder group. It has to be
+ * changed in any case, even if the file system ended exactly on the end of
+ * this group, as there is some slightly inconsistent handling of the number
+ * of cylinders in the cylinder group. We start again by reading the cylinder
+ * group from disk. If the last block was not fully available, we first handle
+ * the missing fragments, then we handle all new full blocks in that file
+ * system and finally we handle the new last fragmented block in the file
+ * system. We again have to handle the fragment statistics rotational layout
+ * tables and cluster summary during all those operations.
+ */
+static void
+updjcg(int cylno, time_t utime, int fsi, int fso, unsigned int Nflag)
+{
+ DBG_FUNC("updjcg")
+ ufs2_daddr_t cbase, dmax, dupper;
+ struct csum *cs;
+ int i,k;
+ int j=0;
+
+ DBG_ENTER;
+
+ /*
+ * Read the former last (joining) cylinder group from disk, and make
+ * a copy.
+ */
+ rdfs(fsbtodb(&osblock, cgtod(&osblock, cylno)),
+ (size_t)osblock.fs_cgsize, (void *)&aocg, fsi);
+ DBG_PRINT0("jcg read\n");
+ DBG_DUMP_CG(&sblock,
+ "old joining cg",
+ &aocg);
+
+ memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2));
+
+ /*
+ * If the cylinder group had already its new final size almost
+ * nothing is to be done ... except:
+ * For some reason the value of cg_ncyl in the last cylinder group has
+ * to be zero instead of fs_cpg. As this is now no longer the last
+ * cylinder group we have to change that value now to fs_cpg.
+ */
+
+ if(cgbase(&osblock, cylno+1) == osblock.fs_size) {
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ acg.cg_old_ncyl=sblock.fs_old_cpg;
+
+ wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)),
+ (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag);
+ DBG_PRINT0("jcg written\n");
+ DBG_DUMP_CG(&sblock,
+ "new joining cg",
+ &acg);
+
+ DBG_LEAVE;
+ return;
+ }
+
+ /*
+ * Set up some variables needed later.
+ */
+ cbase = cgbase(&sblock, cylno);
+ dmax = cbase + sblock.fs_fpg;
+ if (dmax > sblock.fs_size)
+ dmax = sblock.fs_size;
+ dupper = cgdmin(&sblock, cylno) - cbase;
+ if (cylno == 0) { /* XXX fscs may be relocated */
+ dupper += howmany(sblock.fs_cssize, sblock.fs_fsize);
+ }
+
+ /*
+ * Set pointer to the cylinder summary for our cylinder group.
+ */
+ cs = fscs + cylno;
+
+ /*
+ * Touch the cylinder group, update all fields in the cylinder group as
+ * needed, update the free space in the superblock.
+ */
+ acg.cg_time = utime;
+ if (cylno == sblock.fs_ncg - 1) {
+ /*
+ * This is still the last cylinder group.
+ */
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ acg.cg_old_ncyl =
+ sblock.fs_old_ncyl % sblock.fs_old_cpg;
+ } else {
+ acg.cg_old_ncyl = sblock.fs_old_cpg;
+ }
+ DBG_PRINT2("jcg dbg: %d %u",
+ cylno,
+ sblock.fs_ncg);
+#ifdef FS_DEBUG
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ DBG_PRINT2("%d %u",
+ acg.cg_old_ncyl,
+ sblock.fs_old_cpg);
+#endif
+ DBG_PRINT0("\n");
+ acg.cg_ndblk = dmax - cbase;
+ sblock.fs_dsize += acg.cg_ndblk-aocg.cg_ndblk;
+ if (sblock.fs_contigsumsize > 0) {
+ acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag;
+ }
+
+ /*
+ * Now we have to update the free fragment bitmap for our new free
+ * space. There again we have to handle the fragmentation and also
+ * the rotational layout tables and the cluster summary. This is
+ * also done per fragment for the first new block if the old file
+ * system end was not on a block boundary, per fragment for the new
+ * last block if the new file system end is not on a block boundary,
+ * and per block for all space in between.
+ *
+ * Handle the first new block here if it was partially available
+ * before.
+ */
+ if(osblock.fs_size % sblock.fs_frag) {
+ if(roundup(osblock.fs_size, sblock.fs_frag)<=sblock.fs_size) {
+ /*
+ * The new space is enough to fill at least this
+ * block
+ */
+ j=0;
+ for(i=roundup(osblock.fs_size-cbase, sblock.fs_frag)-1;
+ i>=osblock.fs_size-cbase;
+ i--) {
+ setbit(cg_blksfree(&acg), i);
+ acg.cg_cs.cs_nffree++;
+ j++;
+ }
+
+ /*
+ * Check if the fragment just created could join an
+ * already existing fragment at the former end of the
+ * file system.
+ */
+ if(isblock(&sblock, cg_blksfree(&acg),
+ ((osblock.fs_size - cgbase(&sblock, cylno))/
+ sblock.fs_frag))) {
+ /*
+ * The block is now completely available.
+ */
+ DBG_PRINT0("block was\n");
+ acg.cg_frsum[osblock.fs_size%sblock.fs_frag]--;
+ acg.cg_cs.cs_nbfree++;
+ acg.cg_cs.cs_nffree-=sblock.fs_frag;
+ k=rounddown(osblock.fs_size-cbase,
+ sblock.fs_frag);
+ updclst((osblock.fs_size-cbase)/sblock.fs_frag);
+ } else {
+ /*
+ * Lets rejoin a possible partially growed
+ * fragment.
+ */
+ k=0;
+ while(isset(cg_blksfree(&acg), i) &&
+ (i>=rounddown(osblock.fs_size-cbase,
+ sblock.fs_frag))) {
+ i--;
+ k++;
+ }
+ if(k) {
+ acg.cg_frsum[k]--;
+ }
+ acg.cg_frsum[k+j]++;
+ }
+ } else {
+ /*
+ * We only grow by some fragments within this last
+ * block.
+ */
+ for(i=sblock.fs_size-cbase-1;
+ i>=osblock.fs_size-cbase;
+ i--) {
+ setbit(cg_blksfree(&acg), i);
+ acg.cg_cs.cs_nffree++;
+ j++;
+ }
+ /*
+ * Lets rejoin a possible partially growed fragment.
+ */
+ k=0;
+ while(isset(cg_blksfree(&acg), i) &&
+ (i>=rounddown(osblock.fs_size-cbase,
+ sblock.fs_frag))) {
+ i--;
+ k++;
+ }
+ if(k) {
+ acg.cg_frsum[k]--;
+ }
+ acg.cg_frsum[k+j]++;
+ }
+ }
+
+ /*
+ * Handle all new complete blocks here.
+ */
+ for(i=roundup(osblock.fs_size-cbase, sblock.fs_frag);
+ i+sblock.fs_frag<=dmax-cbase; /* XXX <= or only < ? */
+ i+=sblock.fs_frag) {
+ j = i / sblock.fs_frag;
+ setblock(&sblock, cg_blksfree(&acg), j);
+ updclst(j);
+ acg.cg_cs.cs_nbfree++;
+ }
+
+ /*
+ * Handle the last new block if there are stll some new fragments left.
+ * Here we don't have to bother about the cluster summary or the even
+ * the rotational layout table.
+ */
+ if (i < (dmax - cbase)) {
+ acg.cg_frsum[dmax - cbase - i]++;
+ for (; i < dmax - cbase; i++) {
+ setbit(cg_blksfree(&acg), i);
+ acg.cg_cs.cs_nffree++;
+ }
+ }
+
+ sblock.fs_cstotal.cs_nffree +=
+ (acg.cg_cs.cs_nffree - aocg.cg_cs.cs_nffree);
+ sblock.fs_cstotal.cs_nbfree +=
+ (acg.cg_cs.cs_nbfree - aocg.cg_cs.cs_nbfree);
+ /*
+ * The following statistics are not changed here:
+ * sblock.fs_cstotal.cs_ndir
+ * sblock.fs_cstotal.cs_nifree
+ * As the statistics for this cylinder group are ready, copy it to
+ * the summary information array.
+ */
+ *cs = acg.cg_cs;
+
+ /*
+ * Write the updated "joining" cylinder group back to disk.
+ */
+ wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), (size_t)sblock.fs_cgsize,
+ (void *)&acg, fso, Nflag);
+ DBG_PRINT0("jcg written\n");
+ DBG_DUMP_CG(&sblock,
+ "new joining cg",
+ &acg);
+
+ DBG_LEAVE;
+ return;
+}
+
+/* ********************************************************** updcsloc ***** */
+/*
+ * Here we update the location of the cylinder summary. We have two possible
+ * ways of growing the cylinder summary.
+ * (1) We can try to grow the summary in the current location, and relocate
+ * possibly used blocks within the current cylinder group.
+ * (2) Alternatively we can relocate the whole cylinder summary to the first
+ * new completely empty cylinder group. Once the cylinder summary is no
+ * longer in the beginning of the first cylinder group you should never
+ * use a version of fsck which is not aware of the possibility to have
+ * this structure in a non standard place.
+ * Option (1) is considered to be less intrusive to the structure of the file-
+ * system. So we try to stick to that whenever possible. If there is not enough
+ * space in the cylinder group containing the cylinder summary we have to use
+ * method (2). In case of active snapshots in the file system we probably can
+ * completely avoid implementing copy on write if we stick to method (2) only.
+ */
+static void
+updcsloc(time_t utime, int fsi, int fso, unsigned int Nflag)
+{
+ DBG_FUNC("updcsloc")
+ struct csum *cs;
+ int ocscg, ncscg;
+ int blocks;
+ ufs2_daddr_t cbase, dupper, odupper, d, f, g;
+ int ind;
+ int cylno, inc;
+ struct gfs_bpp *bp;
+ int i, l;
+ int lcs=0;
+ int block;
+
+ DBG_ENTER;
+
+ if(howmany(sblock.fs_cssize, sblock.fs_fsize) ==
+ howmany(osblock.fs_cssize, osblock.fs_fsize)) {
+ /*
+ * No new fragment needed.
+ */
+ DBG_LEAVE;
+ return;
+ }
+ ocscg=dtog(&osblock, osblock.fs_csaddr);
+ cs=fscs+ocscg;
+ blocks = 1+howmany(sblock.fs_cssize, sblock.fs_bsize)-
+ howmany(osblock.fs_cssize, osblock.fs_bsize);
+
+ /*
+ * Read original cylinder group from disk, and make a copy.
+ * XXX If Nflag is set in some very rare cases we now miss
+ * some changes done in updjcg by reading the unmodified
+ * block from disk.
+ */
+ rdfs(fsbtodb(&osblock, cgtod(&osblock, ocscg)),
+ (size_t)osblock.fs_cgsize, (void *)&aocg, fsi);
+ DBG_PRINT0("oscg read\n");
+ DBG_DUMP_CG(&sblock,
+ "old summary cg",
+ &aocg);
+
+ memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2));
+
+ /*
+ * Touch the cylinder group, set up local variables needed later
+ * and update the superblock.
+ */
+ acg.cg_time = utime;
+
+ /*
+ * XXX In the case of having active snapshots we may need much more
+ * blocks for the copy on write. We need each block twice, and
+ * also up to 8*3 blocks for indirect blocks for all possible
+ * references.
+ */
+ if(/*((int)sblock.fs_time&0x3)>0||*/ cs->cs_nbfree < blocks) {
+ /*
+ * There is not enough space in the old cylinder group to
+ * relocate all blocks as needed, so we relocate the whole
+ * cylinder group summary to a new group. We try to use the
+ * first complete new cylinder group just created. Within the
+ * cylinder group we align the area immediately after the
+ * cylinder group information location in order to be as
+ * close as possible to the original implementation of ffs.
+ *
+ * First we have to make sure we'll find enough space in the
+ * new cylinder group. If not, then we currently give up.
+ * We start with freeing everything which was used by the
+ * fragments of the old cylinder summary in the current group.
+ * Now we write back the group meta data, read in the needed
+ * meta data from the new cylinder group, and start allocating
+ * within that group. Here we can assume, the group to be
+ * completely empty. Which makes the handling of fragments and
+ * clusters a lot easier.
+ */
+ DBG_TRC;
+ if(sblock.fs_ncg-osblock.fs_ncg < 2) {
+ errx(2, "panic: not enough space");
+ }
+
+ /*
+ * Point "d" to the first fragment not used by the cylinder
+ * summary.
+ */
+ d=osblock.fs_csaddr+(osblock.fs_cssize/osblock.fs_fsize);
+
+ /*
+ * Set up last cluster size ("lcs") already here. Calculate
+ * the size for the trailing cluster just behind where "d"
+ * points to.
+ */
+ if(sblock.fs_contigsumsize > 0) {
+ for(block=howmany(d%sblock.fs_fpg, sblock.fs_frag),
+ lcs=0; lcs<sblock.fs_contigsumsize;
+ block++, lcs++) {
+ if(isclr(cg_clustersfree(&acg), block)){
+ break;
+ }
+ }
+ }
+
+ /*
+ * Point "d" to the last frag used by the cylinder summary.
+ */
+ d--;
+
+ DBG_PRINT1("d=%jd\n",
+ (intmax_t)d);
+ if((d+1)%sblock.fs_frag) {
+ /*
+ * The end of the cylinder summary is not a complete
+ * block.
+ */
+ DBG_TRC;
+ frag_adjust(d%sblock.fs_fpg, -1);
+ for(; (d+1)%sblock.fs_frag; d--) {
+ DBG_PRINT1("d=%jd\n",
+ (intmax_t)d);
+ setbit(cg_blksfree(&acg), d%sblock.fs_fpg);
+ acg.cg_cs.cs_nffree++;
+ sblock.fs_cstotal.cs_nffree++;
+ }
+ /*
+ * Point "d" to the last fragment of the last
+ * (incomplete) block of the cylinder summary.
+ */
+ d++;
+ frag_adjust(d%sblock.fs_fpg, 1);
+
+ if(isblock(&sblock, cg_blksfree(&acg),
+ (d%sblock.fs_fpg)/sblock.fs_frag)) {
+ DBG_PRINT1("d=%jd\n", (intmax_t)d);
+ acg.cg_cs.cs_nffree-=sblock.fs_frag;
+ acg.cg_cs.cs_nbfree++;
+ sblock.fs_cstotal.cs_nffree-=sblock.fs_frag;
+ sblock.fs_cstotal.cs_nbfree++;
+ if(sblock.fs_contigsumsize > 0) {
+ setbit(cg_clustersfree(&acg),
+ (d%sblock.fs_fpg)/sblock.fs_frag);
+ if(lcs < sblock.fs_contigsumsize) {
+ if(lcs) {
+ cg_clustersum(&acg)
+ [lcs]--;
+ }
+ lcs++;
+ cg_clustersum(&acg)[lcs]++;
+ }
+ }
+ }
+ /*
+ * Point "d" to the first fragment of the block before
+ * the last incomplete block.
+ */
+ d--;
+ }
+
+ DBG_PRINT1("d=%jd\n", (intmax_t)d);
+ for(d=rounddown(d, sblock.fs_frag); d >= osblock.fs_csaddr;
+ d-=sblock.fs_frag) {
+ DBG_TRC;
+ DBG_PRINT1("d=%jd\n", (intmax_t)d);
+ setblock(&sblock, cg_blksfree(&acg),
+ (d%sblock.fs_fpg)/sblock.fs_frag);
+ acg.cg_cs.cs_nbfree++;
+ sblock.fs_cstotal.cs_nbfree++;
+ if(sblock.fs_contigsumsize > 0) {
+ setbit(cg_clustersfree(&acg),
+ (d%sblock.fs_fpg)/sblock.fs_frag);
+ /*
+ * The last cluster size is already set up.
+ */
+ if(lcs < sblock.fs_contigsumsize) {
+ if(lcs) {
+ cg_clustersum(&acg)[lcs]--;
+ }
+ lcs++;
+ cg_clustersum(&acg)[lcs]++;
+ }
+ }
+ }
+ *cs = acg.cg_cs;
+
+ /*
+ * Now write the former cylinder group containing the cylinder
+ * summary back to disk.
+ */
+ wtfs(fsbtodb(&sblock, cgtod(&sblock, ocscg)),
+ (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag);
+ DBG_PRINT0("oscg written\n");
+ DBG_DUMP_CG(&sblock,
+ "old summary cg",
+ &acg);
+
+ /*
+ * Find the beginning of the new cylinder group containing the
+ * cylinder summary.
+ */
+ sblock.fs_csaddr=cgdmin(&sblock, osblock.fs_ncg);
+ ncscg=dtog(&sblock, sblock.fs_csaddr);
+ cs=fscs+ncscg;
+
+
+ /*
+ * If Nflag is specified, we would now read random data instead
+ * of an empty cg structure from disk. So we can't simulate that
+ * part for now.
+ */
+ if(Nflag) {
+ DBG_PRINT0("nscg update skipped\n");
+ DBG_LEAVE;
+ return;
+ }
+
+ /*
+ * Read the future cylinder group containing the cylinder
+ * summary from disk, and make a copy.
+ */
+ rdfs(fsbtodb(&sblock, cgtod(&sblock, ncscg)),
+ (size_t)sblock.fs_cgsize, (void *)&aocg, fsi);
+ DBG_PRINT0("nscg read\n");
+ DBG_DUMP_CG(&sblock,
+ "new summary cg",
+ &aocg);
+
+ memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2));
+
+ /*
+ * Allocate all complete blocks used by the new cylinder
+ * summary.
+ */
+ for(d=sblock.fs_csaddr; d+sblock.fs_frag <=
+ sblock.fs_csaddr+(sblock.fs_cssize/sblock.fs_fsize);
+ d+=sblock.fs_frag) {
+ clrblock(&sblock, cg_blksfree(&acg),
+ (d%sblock.fs_fpg)/sblock.fs_frag);
+ acg.cg_cs.cs_nbfree--;
+ sblock.fs_cstotal.cs_nbfree--;
+ if(sblock.fs_contigsumsize > 0) {
+ clrbit(cg_clustersfree(&acg),
+ (d%sblock.fs_fpg)/sblock.fs_frag);
+ }
+ }
+
+ /*
+ * Allocate all fragments used by the cylinder summary in the
+ * last block.
+ */
+ if(d<sblock.fs_csaddr+(sblock.fs_cssize/sblock.fs_fsize)) {
+ for(; d-sblock.fs_csaddr<
+ sblock.fs_cssize/sblock.fs_fsize;
+ d++) {
+ clrbit(cg_blksfree(&acg), d%sblock.fs_fpg);
+ acg.cg_cs.cs_nffree--;
+ sblock.fs_cstotal.cs_nffree--;
+ }
+ acg.cg_cs.cs_nbfree--;
+ acg.cg_cs.cs_nffree+=sblock.fs_frag;
+ sblock.fs_cstotal.cs_nbfree--;
+ sblock.fs_cstotal.cs_nffree+=sblock.fs_frag;
+ if(sblock.fs_contigsumsize > 0) {
+ clrbit(cg_clustersfree(&acg),
+ (d%sblock.fs_fpg)/sblock.fs_frag);
+ }
+
+ frag_adjust(d%sblock.fs_fpg, +1);
+ }
+ /*
+ * XXX Handle the cluster statistics here in the case this
+ * cylinder group is now almost full, and the remaining
+ * space is less then the maximum cluster size. This is
+ * probably not needed, as you would hardly find a file
+ * system which has only MAXCSBUFS+FS_MAXCONTIG of free
+ * space right behind the cylinder group information in
+ * any new cylinder group.
+ */
+
+ /*
+ * Update our statistics in the cylinder summary.
+ */
+ *cs = acg.cg_cs;
+
+ /*
+ * Write the new cylinder group containing the cylinder summary
+ * back to disk.
+ */
+ wtfs(fsbtodb(&sblock, cgtod(&sblock, ncscg)),
+ (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag);
+ DBG_PRINT0("nscg written\n");
+ DBG_DUMP_CG(&sblock,
+ "new summary cg",
+ &acg);
+
+ DBG_LEAVE;
+ return;
+ }
+ /*
+ * We have got enough of space in the current cylinder group, so we
+ * can relocate just a few blocks, and let the summary information
+ * grow in place where it is right now.
+ */
+ DBG_TRC;
+
+ cbase = cgbase(&osblock, ocscg); /* old and new are equal */
+ dupper = sblock.fs_csaddr - cbase +
+ howmany(sblock.fs_cssize, sblock.fs_fsize);
+ odupper = osblock.fs_csaddr - cbase +
+ howmany(osblock.fs_cssize, osblock.fs_fsize);
+
+ sblock.fs_dsize -= dupper-odupper;
+
+ /*
+ * Allocate the space for the array of blocks to be relocated.
+ */
+ bp=(struct gfs_bpp *)malloc(((dupper-odupper)/sblock.fs_frag+2)*
+ sizeof(struct gfs_bpp));
+ if(bp == NULL) {
+ errx(1, "malloc failed");
+ }
+ memset((char *)bp, 0, ((dupper-odupper)/sblock.fs_frag+2)*
+ sizeof(struct gfs_bpp));
+
+ /*
+ * Lock all new frags needed for the cylinder group summary. This is
+ * done per fragment in the first and last block of the new required
+ * area, and per block for all other blocks.
+ *
+ * Handle the first new block here (but only if some fragments where
+ * already used for the cylinder summary).
+ */
+ ind=0;
+ frag_adjust(odupper, -1);
+ for(d=odupper; ((d<dupper)&&(d%sblock.fs_frag)); d++) {
+ DBG_PRINT1("scg first frag check loop d=%jd\n",
+ (intmax_t)d);
+ if(isclr(cg_blksfree(&acg), d)) {
+ if (!ind) {
+ bp[ind].old=d/sblock.fs_frag;
+ bp[ind].flags|=GFS_FL_FIRST;
+ if(roundup(d, sblock.fs_frag) >= dupper) {
+ bp[ind].flags|=GFS_FL_LAST;
+ }
+ ind++;
+ }
+ } else {
+ clrbit(cg_blksfree(&acg), d);
+ acg.cg_cs.cs_nffree--;
+ sblock.fs_cstotal.cs_nffree--;
+ }
+ /*
+ * No cluster handling is needed here, as there was at least
+ * one fragment in use by the cylinder summary in the old
+ * file system.
+ * No block-free counter handling here as this block was not
+ * a free block.
+ */
+ }
+ frag_adjust(odupper, 1);
+
+ /*
+ * Handle all needed complete blocks here.
+ */
+ for(; d+sblock.fs_frag<=dupper; d+=sblock.fs_frag) {
+ DBG_PRINT1("scg block check loop d=%jd\n",
+ (intmax_t)d);
+ if(!isblock(&sblock, cg_blksfree(&acg), d/sblock.fs_frag)) {
+ for(f=d; f<d+sblock.fs_frag; f++) {
+ if(isset(cg_blksfree(&aocg), f)) {
+ acg.cg_cs.cs_nffree--;
+ sblock.fs_cstotal.cs_nffree--;
+ }
+ }
+ clrblock(&sblock, cg_blksfree(&acg), d/sblock.fs_frag);
+ bp[ind].old=d/sblock.fs_frag;
+ ind++;
+ } else {
+ clrblock(&sblock, cg_blksfree(&acg), d/sblock.fs_frag);
+ acg.cg_cs.cs_nbfree--;
+ sblock.fs_cstotal.cs_nbfree--;
+ if(sblock.fs_contigsumsize > 0) {
+ clrbit(cg_clustersfree(&acg), d/sblock.fs_frag);
+ for(lcs=0, l=(d/sblock.fs_frag)+1;
+ lcs<sblock.fs_contigsumsize;
+ l++, lcs++ ) {
+ if(isclr(cg_clustersfree(&acg),l)){
+ break;
+ }
+ }
+ if(lcs < sblock.fs_contigsumsize) {
+ cg_clustersum(&acg)[lcs+1]--;
+ if(lcs) {
+ cg_clustersum(&acg)[lcs]++;
+ }
+ }
+ }
+ }
+ /*
+ * No fragment counter handling is needed here, as this finally
+ * doesn't change after the relocation.
+ */
+ }
+
+ /*
+ * Handle all fragments needed in the last new affected block.
+ */
+ if(d<dupper) {
+ frag_adjust(dupper-1, -1);
+
+ if(isblock(&sblock, cg_blksfree(&acg), d/sblock.fs_frag)) {
+ acg.cg_cs.cs_nbfree--;
+ sblock.fs_cstotal.cs_nbfree--;
+ acg.cg_cs.cs_nffree+=sblock.fs_frag;
+ sblock.fs_cstotal.cs_nffree+=sblock.fs_frag;
+ if(sblock.fs_contigsumsize > 0) {
+ clrbit(cg_clustersfree(&acg), d/sblock.fs_frag);
+ for(lcs=0, l=(d/sblock.fs_frag)+1;
+ lcs<sblock.fs_contigsumsize;
+ l++, lcs++ ) {
+ if(isclr(cg_clustersfree(&acg),l)){
+ break;
+ }
+ }
+ if(lcs < sblock.fs_contigsumsize) {
+ cg_clustersum(&acg)[lcs+1]--;
+ if(lcs) {
+ cg_clustersum(&acg)[lcs]++;
+ }
+ }
+ }
+ }
+
+ for(; d<dupper; d++) {
+ DBG_PRINT1("scg second frag check loop d=%jd\n",
+ (intmax_t)d);
+ if(isclr(cg_blksfree(&acg), d)) {
+ bp[ind].old=d/sblock.fs_frag;
+ bp[ind].flags|=GFS_FL_LAST;
+ } else {
+ clrbit(cg_blksfree(&acg), d);
+ acg.cg_cs.cs_nffree--;
+ sblock.fs_cstotal.cs_nffree--;
+ }
+ }
+ if(bp[ind].flags & GFS_FL_LAST) { /* we have to advance here */
+ ind++;
+ }
+ frag_adjust(dupper-1, 1);
+ }
+
+ /*
+ * If we found a block to relocate just do so.
+ */
+ if(ind) {
+ for(i=0; i<ind; i++) {
+ if(!bp[i].old) { /* no more blocks listed */
+ /*
+ * XXX A relative blocknumber should not be
+ * zero, which is not explicitly
+ * guaranteed by our code.
+ */
+ break;
+ }
+ /*
+ * Allocate a complete block in the same (current)
+ * cylinder group.
+ */
+ bp[i].new=alloc()/sblock.fs_frag;
+
+ /*
+ * There is no frag_adjust() needed for the new block
+ * as it will have no fragments yet :-).
+ */
+ for(f=bp[i].old*sblock.fs_frag,
+ g=bp[i].new*sblock.fs_frag;
+ f<(bp[i].old+1)*sblock.fs_frag;
+ f++, g++) {
+ if(isset(cg_blksfree(&aocg), f)) {
+ setbit(cg_blksfree(&acg), g);
+ acg.cg_cs.cs_nffree++;
+ sblock.fs_cstotal.cs_nffree++;
+ }
+ }
+
+ /*
+ * Special handling is required if this was the first
+ * block. We have to consider the fragments which were
+ * used by the cylinder summary in the original block
+ * which re to be free in the copy of our block. We
+ * have to be careful if this first block happens to
+ * be also the last block to be relocated.
+ */
+ if(bp[i].flags & GFS_FL_FIRST) {
+ for(f=bp[i].old*sblock.fs_frag,
+ g=bp[i].new*sblock.fs_frag;
+ f<odupper;
+ f++, g++) {
+ setbit(cg_blksfree(&acg), g);
+ acg.cg_cs.cs_nffree++;
+ sblock.fs_cstotal.cs_nffree++;
+ }
+ if(!(bp[i].flags & GFS_FL_LAST)) {
+ frag_adjust(bp[i].new*sblock.fs_frag,1);
+ }
+ }
+
+ /*
+ * Special handling is required if this is the last
+ * block to be relocated.
+ */
+ if(bp[i].flags & GFS_FL_LAST) {
+ frag_adjust(bp[i].new*sblock.fs_frag, 1);
+ frag_adjust(bp[i].old*sblock.fs_frag, -1);
+ for(f=dupper;
+ f<roundup(dupper, sblock.fs_frag);
+ f++) {
+ if(isclr(cg_blksfree(&acg), f)) {
+ setbit(cg_blksfree(&acg), f);
+ acg.cg_cs.cs_nffree++;
+ sblock.fs_cstotal.cs_nffree++;
+ }
+ }
+ frag_adjust(bp[i].old*sblock.fs_frag, 1);
+ }
+
+ /*
+ * !!! Attach the cylindergroup offset here.
+ */
+ bp[i].old+=cbase/sblock.fs_frag;
+ bp[i].new+=cbase/sblock.fs_frag;
+
+ /*
+ * Copy the content of the block.
+ */
+ /*
+ * XXX Here we will have to implement a copy on write
+ * in the case we have any active snapshots.
+ */
+ rdfs(fsbtodb(&sblock, bp[i].old*sblock.fs_frag),
+ (size_t)sblock.fs_bsize, (void *)&ablk, fsi);
+ wtfs(fsbtodb(&sblock, bp[i].new*sblock.fs_frag),
+ (size_t)sblock.fs_bsize, (void *)&ablk, fso, Nflag);
+ DBG_DUMP_HEX(&sblock,
+ "copied full block",
+ (unsigned char *)&ablk);
+
+ DBG_PRINT2("scg (%jd->%jd) block relocated\n",
+ (intmax_t)bp[i].old,
+ (intmax_t)bp[i].new);
+ }
+
+ /*
+ * Now we have to update all references to any fragment which
+ * belongs to any block relocated. We iterate now over all
+ * cylinder groups, within those over all non zero length
+ * inodes.
+ */
+ for(cylno=0; cylno<osblock.fs_ncg; cylno++) {
+ DBG_PRINT1("scg doing cg (%d)\n",
+ cylno);
+ for(inc=osblock.fs_ipg-1 ; inc>0 ; inc--) {
+ updrefs(cylno, (ino_t)inc, bp, fsi, fso, Nflag);
+ }
+ }
+
+ /*
+ * All inodes are checked, now make sure the number of
+ * references found make sense.
+ */
+ for(i=0; i<ind; i++) {
+ if(!bp[i].found || (bp[i].found>sblock.fs_frag)) {
+ warnx("error: %jd refs found for block %jd.",
+ (intmax_t)bp[i].found, (intmax_t)bp[i].old);
+ }
+
+ }
+ }
+ /*
+ * The following statistics are not changed here:
+ * sblock.fs_cstotal.cs_ndir
+ * sblock.fs_cstotal.cs_nifree
+ * The following statistics were already updated on the fly:
+ * sblock.fs_cstotal.cs_nffree
+ * sblock.fs_cstotal.cs_nbfree
+ * As the statistics for this cylinder group are ready, copy it to
+ * the summary information array.
+ */
+
+ *cs = acg.cg_cs;
+
+ /*
+ * Write summary cylinder group back to disk.
+ */
+ wtfs(fsbtodb(&sblock, cgtod(&sblock, ocscg)), (size_t)sblock.fs_cgsize,
+ (void *)&acg, fso, Nflag);
+ DBG_PRINT0("scg written\n");
+ DBG_DUMP_CG(&sblock,
+ "new summary cg",
+ &acg);
+
+ DBG_LEAVE;
+ return;
+}
+
+/* ************************************************************** rdfs ***** */
+/*
+ * Here we read some block(s) from disk.
+ */
+static void
+rdfs(ufs2_daddr_t bno, size_t size, void *bf, int fsi)
+{
+ DBG_FUNC("rdfs")
+ ssize_t n;
+
+ DBG_ENTER;
+
+ if (bno < 0) {
+ err(32, "rdfs: attempting to read negative block number");
+ }
+ if (lseek(fsi, (off_t)bno * DEV_BSIZE, 0) < 0) {
+ err(33, "rdfs: seek error: %jd", (intmax_t)bno);
+ }
+ n = read(fsi, bf, size);
+ if (n != (ssize_t)size) {
+ err(34, "rdfs: read error: %jd", (intmax_t)bno);
+ }
+
+ DBG_LEAVE;
+ return;
+}
+
+/* ************************************************************** wtfs ***** */
+/*
+ * Here we write some block(s) to disk.
+ */
+static void
+wtfs(ufs2_daddr_t bno, size_t size, void *bf, int fso, unsigned int Nflag)
+{
+ DBG_FUNC("wtfs")
+ ssize_t n;
+
+ DBG_ENTER;
+
+ if (Nflag) {
+ DBG_LEAVE;
+ return;
+ }
+ if (lseek(fso, (off_t)bno * DEV_BSIZE, SEEK_SET) < 0) {
+ err(35, "wtfs: seek error: %ld", (long)bno);
+ }
+ n = write(fso, bf, size);
+ if (n != (ssize_t)size) {
+ err(36, "wtfs: write error: %ld", (long)bno);
+ }
+
+ DBG_LEAVE;
+ return;
+}
+
+/* ************************************************************* alloc ***** */
+/*
+ * Here we allocate a free block in the current cylinder group. It is assumed,
+ * that acg contains the current cylinder group. As we may take a block from
+ * somewhere in the file system we have to handle cluster summary here.
+ */
+static ufs2_daddr_t
+alloc(void)
+{
+ DBG_FUNC("alloc")
+ ufs2_daddr_t d, blkno;
+ int lcs1, lcs2;
+ int l;
+ int csmin, csmax;
+ int dlower, dupper, dmax;
+
+ DBG_ENTER;
+
+ if (acg.cg_magic != CG_MAGIC) {
+ warnx("acg: bad magic number");
+ DBG_LEAVE;
+ return (0);
+ }
+ if (acg.cg_cs.cs_nbfree == 0) {
+ warnx("error: cylinder group ran out of space");
+ DBG_LEAVE;
+ return (0);
+ }
+ /*
+ * We start seeking for free blocks only from the space available after
+ * the end of the new grown cylinder summary. Otherwise we allocate a
+ * block here which we have to relocate a couple of seconds later again
+ * again, and we are not prepared to to this anyway.
+ */
+ blkno=-1;
+ dlower=cgsblock(&sblock, acg.cg_cgx)-cgbase(&sblock, acg.cg_cgx);
+ dupper=cgdmin(&sblock, acg.cg_cgx)-cgbase(&sblock, acg.cg_cgx);
+ dmax=cgbase(&sblock, acg.cg_cgx)+sblock.fs_fpg;
+ if (dmax > sblock.fs_size) {
+ dmax = sblock.fs_size;
+ }
+ dmax-=cgbase(&sblock, acg.cg_cgx); /* retransform into cg */
+ csmin=sblock.fs_csaddr-cgbase(&sblock, acg.cg_cgx);
+ csmax=csmin+howmany(sblock.fs_cssize, sblock.fs_fsize);
+ DBG_PRINT3("seek range: dl=%d, du=%d, dm=%d\n",
+ dlower,
+ dupper,
+ dmax);
+ DBG_PRINT2("range cont: csmin=%d, csmax=%d\n",
+ csmin,
+ csmax);
+
+ for(d=0; (d<dlower && blkno==-1); d+=sblock.fs_frag) {
+ if(d>=csmin && d<=csmax) {
+ continue;
+ }
+ if(isblock(&sblock, cg_blksfree(&acg), fragstoblks(&sblock,
+ d))) {
+ blkno = fragstoblks(&sblock, d);/* Yeah found a block */
+ break;
+ }
+ }
+ for(d=dupper; (d<dmax && blkno==-1); d+=sblock.fs_frag) {
+ if(d>=csmin && d<=csmax) {
+ continue;
+ }
+ if(isblock(&sblock, cg_blksfree(&acg), fragstoblks(&sblock,
+ d))) {
+ blkno = fragstoblks(&sblock, d);/* Yeah found a block */
+ break;
+ }
+ }
+ if(blkno==-1) {
+ warnx("internal error: couldn't find promised block in cg");
+ DBG_LEAVE;
+ return (0);
+ }
+
+ /*
+ * This is needed if the block was found already in the first loop.
+ */
+ d=blkstofrags(&sblock, blkno);
+
+ clrblock(&sblock, cg_blksfree(&acg), blkno);
+ if (sblock.fs_contigsumsize > 0) {
+ /*
+ * Handle the cluster allocation bitmap.
+ */
+ clrbit(cg_clustersfree(&acg), blkno);
+ /*
+ * We possibly have split a cluster here, so we have to do
+ * recalculate the sizes of the remaining cluster halves now,
+ * and use them for updating the cluster summary information.
+ *
+ * Lets start with the blocks before our allocated block ...
+ */
+ for(lcs1=0, l=blkno-1; lcs1<sblock.fs_contigsumsize;
+ l--, lcs1++ ) {
+ if(isclr(cg_clustersfree(&acg),l)){
+ break;
+ }
+ }
+ /*
+ * ... and continue with the blocks right after our allocated
+ * block.
+ */
+ for(lcs2=0, l=blkno+1; lcs2<sblock.fs_contigsumsize;
+ l++, lcs2++ ) {
+ if(isclr(cg_clustersfree(&acg),l)){
+ break;
+ }
+ }
+
+ /*
+ * Now update all counters.
+ */
+ cg_clustersum(&acg)[MIN(lcs1+lcs2+1,sblock.fs_contigsumsize)]--;
+ if(lcs1) {
+ cg_clustersum(&acg)[lcs1]++;
+ }
+ if(lcs2) {
+ cg_clustersum(&acg)[lcs2]++;
+ }
+ }
+ /*
+ * Update all statistics based on blocks.
+ */
+ acg.cg_cs.cs_nbfree--;
+ sblock.fs_cstotal.cs_nbfree--;
+
+ DBG_LEAVE;
+ return (d);
+}
+
+/* *********************************************************** isblock ***** */
+/*
+ * Here we check if all frags of a block are free. For more details again
+ * please see the source of newfs(8), as this function is taken over almost
+ * unchanged.
+ */
+static int
+isblock(struct fs *fs, unsigned char *cp, int h)
+{
+ DBG_FUNC("isblock")
+ unsigned char mask;
+
+ DBG_ENTER;
+
+ switch (fs->fs_frag) {
+ case 8:
+ DBG_LEAVE;
+ return (cp[h] == 0xff);
+ case 4:
+ mask = 0x0f << ((h & 0x1) << 2);
+ DBG_LEAVE;
+ return ((cp[h >> 1] & mask) == mask);
+ case 2:
+ mask = 0x03 << ((h & 0x3) << 1);
+ DBG_LEAVE;
+ return ((cp[h >> 2] & mask) == mask);
+ case 1:
+ mask = 0x01 << (h & 0x7);
+ DBG_LEAVE;
+ return ((cp[h >> 3] & mask) == mask);
+ default:
+ fprintf(stderr, "isblock bad fs_frag %d\n", fs->fs_frag);
+ DBG_LEAVE;
+ return (0);
+ }
+}
+
+/* ********************************************************** clrblock ***** */
+/*
+ * Here we allocate a complete block in the block map. For more details again
+ * please see the source of newfs(8), as this function is taken over almost
+ * unchanged.
+ */
+static void
+clrblock(struct fs *fs, unsigned char *cp, int h)
+{
+ DBG_FUNC("clrblock")
+
+ DBG_ENTER;
+
+ switch ((fs)->fs_frag) {
+ case 8:
+ cp[h] = 0;
+ break;
+ case 4:
+ cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2));
+ break;
+ case 2:
+ cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1));
+ break;
+ case 1:
+ cp[h >> 3] &= ~(0x01 << (h & 0x7));
+ break;
+ default:
+ warnx("clrblock bad fs_frag %d", fs->fs_frag);
+ break;
+ }
+
+ DBG_LEAVE;
+ return;
+}
+
+/* ********************************************************** setblock ***** */
+/*
+ * Here we free a complete block in the free block map. For more details again
+ * please see the source of newfs(8), as this function is taken over almost
+ * unchanged.
+ */
+static void
+setblock(struct fs *fs, unsigned char *cp, int h)
+{
+ DBG_FUNC("setblock")
+
+ DBG_ENTER;
+
+ switch (fs->fs_frag) {
+ case 8:
+ cp[h] = 0xff;
+ break;
+ case 4:
+ cp[h >> 1] |= (0x0f << ((h & 0x1) << 2));
+ break;
+ case 2:
+ cp[h >> 2] |= (0x03 << ((h & 0x3) << 1));
+ break;
+ case 1:
+ cp[h >> 3] |= (0x01 << (h & 0x7));
+ break;
+ default:
+ warnx("setblock bad fs_frag %d", fs->fs_frag);
+ break;
+ }
+
+ DBG_LEAVE;
+ return;
+}
+
+/* ************************************************************ ginode ***** */
+/*
+ * This function provides access to an individual inode. We find out in which
+ * block the requested inode is located, read it from disk if needed, and
+ * return the pointer into that block. We maintain a cache of one block to
+ * not read the same block again and again if we iterate linearly over all
+ * inodes.
+ */
+static union dinode *
+ginode(ino_t inumber, int fsi, int cg)
+{
+ DBG_FUNC("ginode")
+ static ino_t startinum = 0; /* first inode in cached block */
+
+ DBG_ENTER;
+
+ /*
+ * The inumber passed in is relative to the cg, so use it here to see
+ * if the inode has been allocated yet.
+ */
+ if (isclr(cg_inosused(&aocg), inumber)) {
+ DBG_LEAVE;
+ return NULL;
+ }
+ /*
+ * Now make the inumber relative to the entire inode space so it can
+ * be sanity checked.
+ */
+ inumber += (cg * sblock.fs_ipg);
+ if (inumber < ROOTINO) {
+ DBG_LEAVE;
+ return NULL;
+ }
+ if (inumber > maxino)
+ errx(8, "bad inode number %d to ginode", inumber);
+ if (startinum == 0 ||
+ inumber < startinum || inumber >= startinum + INOPB(&sblock)) {
+ inoblk = fsbtodb(&sblock, ino_to_fsba(&sblock, inumber));
+ rdfs(inoblk, (size_t)sblock.fs_bsize, inobuf, fsi);
+ startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock);
+ }
+ DBG_LEAVE;
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ return (union dinode *)((uintptr_t)inobuf +
+ (inumber % INOPB(&sblock)) * sizeof(struct ufs1_dinode));
+ return (union dinode *)((uintptr_t)inobuf +
+ (inumber % INOPB(&sblock)) * sizeof(struct ufs2_dinode));
+}
+
+/* ****************************************************** charsperline ***** */
+/*
+ * Figure out how many lines our current terminal has. For more details again
+ * please see the source of newfs(8), as this function is taken over almost
+ * unchanged.
+ */
+static int
+charsperline(void)
+{
+ DBG_FUNC("charsperline")
+ int columns;
+ char *cp;
+ struct winsize ws;
+
+ DBG_ENTER;
+
+ columns = 0;
+ if (ioctl(0, TIOCGWINSZ, &ws) != -1) {
+ columns = ws.ws_col;
+ }
+ if (columns == 0 && (cp = getenv("COLUMNS"))) {
+ columns = atoi(cp);
+ }
+ if (columns == 0) {
+ columns = 80; /* last resort */
+ }
+
+ DBG_LEAVE;
+ return columns;
+}
+
+/* ****************************************************** get_dev_size ***** */
+/*
+ * Get the size of the partition if we can't figure it out from the disklabel,
+ * e.g. from vinum volumes.
+ */
+static void
+get_dev_size(int fd, int *size)
+{
+ int sectorsize;
+ off_t mediasize;
+
+ if (ioctl(fd, DIOCGSECTORSIZE, &sectorsize) == -1)
+ err(1,"DIOCGSECTORSIZE");
+ if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) == -1)
+ err(1,"DIOCGMEDIASIZE");
+
+ if (sectorsize <= 0)
+ errx(1, "bogus sectorsize: %d", sectorsize);
+
+ *size = mediasize / sectorsize;
+}
+
+/* ************************************************************** main ***** */
+/*
+ * growfs(8) is a utility which allows to increase the size of an existing
+ * ufs file system. Currently this can only be done on unmounted file system.
+ * It recognizes some command line options to specify the new desired size,
+ * and it does some basic checkings. The old file system size is determined
+ * and after some more checks like we can really access the new last block
+ * on the disk etc. we calculate the new parameters for the superblock. After
+ * having done this we just call growfs() which will do the work. Before
+ * we finish the only thing left is to update the disklabel.
+ * We still have to provide support for snapshots. Therefore we first have to
+ * understand what data structures are always replicated in the snapshot on
+ * creation, for all other blocks we touch during our procedure, we have to
+ * keep the old blocks unchanged somewhere available for the snapshots. If we
+ * are lucky, then we only have to handle our blocks to be relocated in that
+ * way.
+ * Also we have to consider in what order we actually update the critical
+ * data structures of the file system to make sure, that in case of a disaster
+ * fsck(8) is still able to restore any lost data.
+ * The foreseen last step then will be to provide for growing even mounted
+ * file systems. There we have to extend the mount() system call to provide
+ * userland access to the file system locking facility.
+ */
+int
+main(int argc, char **argv)
+{
+ DBG_FUNC("main")
+ char *device, *special, *cp;
+ int ch;
+ unsigned int size=0;
+ size_t len;
+ unsigned int Nflag=0;
+ int ExpertFlag=0;
+ struct stat st;
+ struct disklabel *lp;
+ struct partition *pp;
+ int i,fsi,fso;
+ u_int32_t p_size;
+ char reply[5];
+#ifdef FSMAXSNAP
+ int j;
+#endif /* FSMAXSNAP */
+
+ DBG_ENTER;
+
+ while((ch=getopt(argc, argv, "Ns:vy")) != -1) {
+ switch(ch) {
+ case 'N':
+ Nflag=1;
+ break;
+ case 's':
+ size=(size_t)atol(optarg);
+ if(size<1) {
+ usage();
+ }
+ break;
+ case 'v': /* for compatibility to newfs */
+ break;
+ case 'y':
+ ExpertFlag=1;
+ break;
+ case '?':
+ /* FALLTHROUGH */
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if(argc != 1) {
+ usage();
+ }
+ device=*argv;
+
+ /*
+ * Now try to guess the (raw)device name.
+ */
+ if (0 == strrchr(device, '/')) {
+ /*
+ * No path prefix was given, so try in that order:
+ * /dev/r%s
+ * /dev/%s
+ * /dev/vinum/r%s
+ * /dev/vinum/%s.
+ *
+ * FreeBSD now doesn't distinguish between raw and block
+ * devices any longer, but it should still work this way.
+ */
+ len=strlen(device)+strlen(_PATH_DEV)+2+strlen("vinum/");
+ special=(char *)malloc(len);
+ if(special == NULL) {
+ errx(1, "malloc failed");
+ }
+ snprintf(special, len, "%sr%s", _PATH_DEV, device);
+ if (stat(special, &st) == -1) {
+ snprintf(special, len, "%s%s", _PATH_DEV, device);
+ if (stat(special, &st) == -1) {
+ snprintf(special, len, "%svinum/r%s",
+ _PATH_DEV, device);
+ if (stat(special, &st) == -1) {
+ /* For now this is the 'last resort' */
+ snprintf(special, len, "%svinum/%s",
+ _PATH_DEV, device);
+ }
+ }
+ }
+ device = special;
+ }
+
+ /*
+ * Try to access our devices for writing ...
+ */
+ if (Nflag) {
+ fso = -1;
+ } else {
+ fso = open(device, O_WRONLY);
+ if (fso < 0) {
+ err(1, "%s", device);
+ }
+ }
+
+ /*
+ * ... and reading.
+ */
+ fsi = open(device, O_RDONLY);
+ if (fsi < 0) {
+ err(1, "%s", device);
+ }
+
+ /*
+ * Try to read a label and guess the slice if not specified. This
+ * code should guess the right thing and avoid to bother the user
+ * with the task of specifying the option -v on vinum volumes.
+ */
+ cp=device+strlen(device)-1;
+ lp = get_disklabel(fsi);
+ pp = NULL;
+ if (lp != NULL) {
+ if (isdigit(*cp)) {
+ pp = &lp->d_partitions[2];
+ } else if (*cp>='a' && *cp<='h') {
+ pp = &lp->d_partitions[*cp - 'a'];
+ } else {
+ errx(1, "unknown device");
+ }
+ p_size = pp->p_size;
+ } else {
+ get_dev_size(fsi, &p_size);
+ }
+
+ /*
+ * Check if that partition is suitable for growing a file system.
+ */
+ if (p_size < 1) {
+ errx(1, "partition is unavailable");
+ }
+
+ /*
+ * Read the current superblock, and take a backup.
+ */
+ for (i = 0; sblock_try[i] != -1; i++) {
+ sblockloc = sblock_try[i] / DEV_BSIZE;
+ rdfs(sblockloc, (size_t)SBLOCKSIZE, (void *)&(osblock), fsi);
+ if ((osblock.fs_magic == FS_UFS1_MAGIC ||
+ (osblock.fs_magic == FS_UFS2_MAGIC &&
+ osblock.fs_sblockloc == sblock_try[i])) &&
+ osblock.fs_bsize <= MAXBSIZE &&
+ osblock.fs_bsize >= (int32_t) sizeof(struct fs))
+ break;
+ }
+ if (sblock_try[i] == -1) {
+ errx(1, "superblock not recognized");
+ }
+ memcpy((void *)&fsun1, (void *)&fsun2, sizeof(fsun2));
+ maxino = sblock.fs_ncg * sblock.fs_ipg;
+
+ DBG_OPEN("/tmp/growfs.debug"); /* already here we need a superblock */
+ DBG_DUMP_FS(&sblock,
+ "old sblock");
+
+ /*
+ * Determine size to grow to. Default to the full size specified in
+ * the disk label.
+ */
+ sblock.fs_size = dbtofsb(&osblock, p_size);
+ if (size != 0) {
+ if (size > p_size){
+ errx(1, "there is not enough space (%d < %d)",
+ p_size, size);
+ }
+ sblock.fs_size = dbtofsb(&osblock, size);
+ }
+
+ /*
+ * Are we really growing ?
+ */
+ if(osblock.fs_size >= sblock.fs_size) {
+ errx(1, "we are not growing (%jd->%jd)",
+ (intmax_t)osblock.fs_size, (intmax_t)sblock.fs_size);
+ }
+
+
+#ifdef FSMAXSNAP
+ /*
+ * Check if we find an active snapshot.
+ */
+ if(ExpertFlag == 0) {
+ for(j=0; j<FSMAXSNAP; j++) {
+ if(sblock.fs_snapinum[j]) {
+ errx(1, "active snapshot found in file system\n"
+ " please remove all snapshots before "
+ "using growfs");
+ }
+ if(!sblock.fs_snapinum[j]) { /* list is dense */
+ break;
+ }
+ }
+ }
+#endif
+
+ if (ExpertFlag == 0 && Nflag == 0) {
+ printf("We strongly recommend you to make a backup "
+ "before growing the Filesystem\n\n"
+ " Did you backup your data (Yes/No) ? ");
+ fgets(reply, (int)sizeof(reply), stdin);
+ if (strcmp(reply, "Yes\n")){
+ printf("\n Nothing done \n");
+ exit (0);
+ }
+ }
+
+ printf("new file systemsize is: %jd frags\n", (intmax_t)sblock.fs_size);
+
+ /*
+ * Try to access our new last block in the file system. Even if we
+ * later on realize we have to abort our operation, on that block
+ * there should be no data, so we can't destroy something yet.
+ */
+ wtfs((ufs2_daddr_t)p_size-1, (size_t)DEV_BSIZE, (void *)&sblock,
+ fso, Nflag);
+
+ /*
+ * Now calculate new superblock values and check for reasonable
+ * bound for new file system size:
+ * fs_size: is derived from label or user input
+ * fs_dsize: should get updated in the routines creating or
+ * updating the cylinder groups on the fly
+ * fs_cstotal: should get updated in the routines creating or
+ * updating the cylinder groups
+ */
+
+ /*
+ * Update the number of cylinders and cylinder groups in the file system.
+ */
+ if (sblock.fs_magic == FS_UFS1_MAGIC) {
+ sblock.fs_old_ncyl =
+ sblock.fs_size * sblock.fs_old_nspf / sblock.fs_old_spc;
+ if (sblock.fs_size * sblock.fs_old_nspf >
+ sblock.fs_old_ncyl * sblock.fs_old_spc)
+ sblock.fs_old_ncyl++;
+ }
+ sblock.fs_ncg = howmany(sblock.fs_size, sblock.fs_fpg);
+ maxino = sblock.fs_ncg * sblock.fs_ipg;
+
+ if (sblock.fs_size % sblock.fs_fpg != 0 &&
+ sblock.fs_size % sblock.fs_fpg < cgdmin(&sblock, sblock.fs_ncg)) {
+ /*
+ * The space in the new last cylinder group is too small,
+ * so revert back.
+ */
+ sblock.fs_ncg--;
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ sblock.fs_old_ncyl = sblock.fs_ncg * sblock.fs_old_cpg;
+ printf("Warning: %jd sector(s) cannot be allocated.\n",
+ (intmax_t)fsbtodb(&sblock, sblock.fs_size % sblock.fs_fpg));
+ sblock.fs_size = sblock.fs_ncg * sblock.fs_fpg;
+ }
+
+ /*
+ * Update the space for the cylinder group summary information in the
+ * respective cylinder group data area.
+ */
+ sblock.fs_cssize =
+ fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum));
+
+ if(osblock.fs_size >= sblock.fs_size) {
+ errx(1, "not enough new space");
+ }
+
+ DBG_PRINT0("sblock calculated\n");
+
+ /*
+ * Ok, everything prepared, so now let's do the tricks.
+ */
+ growfs(fsi, fso, Nflag);
+
+ /*
+ * Update the disk label.
+ */
+ if (!unlabeled) {
+ pp->p_fsize = sblock.fs_fsize;
+ pp->p_frag = sblock.fs_frag;
+ pp->p_cpg = sblock.fs_fpg;
+
+ return_disklabel(fso, lp, Nflag);
+ DBG_PRINT0("label rewritten\n");
+ }
+
+ close(fsi);
+ if(fso>-1) close(fso);
+
+ DBG_CLOSE;
+
+ DBG_LEAVE;
+ return 0;
+}
+
+/* ************************************************** return_disklabel ***** */
+/*
+ * Write the updated disklabel back to disk.
+ */
+static void
+return_disklabel(int fd, struct disklabel *lp, unsigned int Nflag)
+{
+ DBG_FUNC("return_disklabel")
+ u_short sum;
+ u_short *ptr;
+
+ DBG_ENTER;
+
+ if(!lp) {
+ DBG_LEAVE;
+ return;
+ }
+ if(!Nflag) {
+ lp->d_checksum=0;
+ sum = 0;
+ ptr=(u_short *)lp;
+
+ /*
+ * recalculate checksum
+ */
+ while(ptr < (u_short *)&lp->d_partitions[lp->d_npartitions]) {
+ sum ^= *ptr++;
+ }
+ lp->d_checksum=sum;
+
+ if (ioctl(fd, DIOCWDINFO, (char *)lp) < 0) {
+ errx(1, "DIOCWDINFO failed");
+ }
+ }
+ free(lp);
+
+ DBG_LEAVE;
+ return ;
+}
+
+/* ***************************************************** get_disklabel ***** */
+/*
+ * Read the disklabel from disk.
+ */
+static struct disklabel *
+get_disklabel(int fd)
+{
+ DBG_FUNC("get_disklabel")
+ static struct disklabel *lab;
+
+ DBG_ENTER;
+
+ lab=(struct disklabel *)malloc(sizeof(struct disklabel));
+ if (!lab)
+ errx(1, "malloc failed");
+
+ if (!ioctl(fd, DIOCGDINFO, (char *)lab))
+ return (lab);
+
+ unlabeled++;
+
+ DBG_LEAVE;
+ return (NULL);
+}
+
+
+/* ************************************************************* usage ***** */
+/*
+ * Dump a line of usage.
+ */
+static void
+usage(void)
+{
+ DBG_FUNC("usage")
+
+ DBG_ENTER;
+
+ fprintf(stderr, "usage: growfs [-Ny] [-s size] special\n");
+
+ DBG_LEAVE;
+ exit(1);
+}
+
+/* *********************************************************** updclst ***** */
+/*
+ * This updates most parameters and the bitmap related to cluster. We have to
+ * assume that sblock, osblock, acg are set up.
+ */
+static void
+updclst(int block)
+{
+ DBG_FUNC("updclst")
+ static int lcs=0;
+
+ DBG_ENTER;
+
+ if(sblock.fs_contigsumsize < 1) { /* no clustering */
+ return;
+ }
+ /*
+ * update cluster allocation map
+ */
+ setbit(cg_clustersfree(&acg), block);
+
+ /*
+ * update cluster summary table
+ */
+ if(!lcs) {
+ /*
+ * calculate size for the trailing cluster
+ */
+ for(block--; lcs<sblock.fs_contigsumsize; block--, lcs++ ) {
+ if(isclr(cg_clustersfree(&acg), block)){
+ break;
+ }
+ }
+ }
+ if(lcs < sblock.fs_contigsumsize) {
+ if(lcs) {
+ cg_clustersum(&acg)[lcs]--;
+ }
+ lcs++;
+ cg_clustersum(&acg)[lcs]++;
+ }
+
+ DBG_LEAVE;
+ return;
+}
+
+/* *********************************************************** updrefs ***** */
+/*
+ * This updates all references to relocated blocks for the given inode. The
+ * inode is given as number within the cylinder group, and the number of the
+ * cylinder group.
+ */
+static void
+updrefs(int cg, ino_t in, struct gfs_bpp *bp, int fsi, int fso, unsigned int
+ Nflag)
+{
+ DBG_FUNC("updrefs")
+ ufs_lbn_t len, lbn, numblks;
+ ufs2_daddr_t iptr, blksperindir;
+ union dinode *ino;
+ int i, mode, inodeupdated;
+
+ DBG_ENTER;
+
+ ino = ginode(in, fsi, cg);
+ if (ino == NULL) {
+ DBG_LEAVE;
+ return;
+ }
+ mode = DIP(ino, di_mode) & IFMT;
+ if (mode != IFDIR && mode != IFREG && mode != IFLNK) {
+ DBG_LEAVE;
+ return; /* only check DIR, FILE, LINK */
+ }
+ if (mode == IFLNK &&
+ DIP(ino, di_size) < (u_int64_t) sblock.fs_maxsymlinklen) {
+ DBG_LEAVE;
+ return; /* skip short symlinks */
+ }
+ numblks = howmany(DIP(ino, di_size), sblock.fs_bsize);
+ if (numblks == 0) {
+ DBG_LEAVE;
+ return; /* skip empty file */
+ }
+ if (DIP(ino, di_blocks) == 0) {
+ DBG_LEAVE;
+ return; /* skip empty swiss cheesy file or old fastlink */
+ }
+ DBG_PRINT2("scg checking inode (%d in %d)\n",
+ in,
+ cg);
+
+ /*
+ * Check all the blocks.
+ */
+ inodeupdated = 0;
+ len = numblks < NDADDR ? numblks : NDADDR;
+ for (i = 0; i < len; i++) {
+ iptr = DIP(ino, di_db[i]);
+ if (iptr == 0)
+ continue;
+ if (cond_bl_upd(&iptr, bp, fsi, fso, Nflag)) {
+ DIP_SET(ino, di_db[i], iptr);
+ inodeupdated++;
+ }
+ }
+ DBG_PRINT0("~~scg direct blocks checked\n");
+
+ blksperindir = 1;
+ len = numblks - NDADDR;
+ lbn = NDADDR;
+ for (i = 0; len > 0 && i < NIADDR; i++) {
+ iptr = DIP(ino, di_ib[i]);
+ if (iptr == 0)
+ continue;
+ if (cond_bl_upd(&iptr, bp, fsi, fso, Nflag)) {
+ DIP_SET(ino, di_ib[i], iptr);
+ inodeupdated++;
+ }
+ indirchk(blksperindir, lbn, iptr, numblks, bp, fsi, fso, Nflag);
+ blksperindir *= NINDIR(&sblock);
+ lbn += blksperindir;
+ len -= blksperindir;
+ DBG_PRINT1("scg indirect_%d blocks checked\n", i + 1);
+ }
+ if (inodeupdated)
+ wtfs(inoblk, sblock.fs_bsize, inobuf, fso, Nflag);
+
+ DBG_LEAVE;
+ return;
+}
+
+/*
+ * Recursively check all the indirect blocks.
+ */
+static void
+indirchk(ufs_lbn_t blksperindir, ufs_lbn_t lbn, ufs2_daddr_t blkno,
+ ufs_lbn_t lastlbn, struct gfs_bpp *bp, int fsi, int fso, unsigned int Nflag)
+{
+ DBG_FUNC("indirchk")
+ void *ibuf;
+ int i, last;
+ ufs2_daddr_t iptr;
+
+ DBG_ENTER;
+
+ /* read in the indirect block. */
+ ibuf = malloc(sblock.fs_bsize);
+ if (!ibuf)
+ errx(1, "malloc failed");
+ rdfs(fsbtodb(&sblock, blkno), (size_t)sblock.fs_bsize, ibuf, fsi);
+ last = howmany(lastlbn - lbn, blksperindir) < NINDIR(&sblock) ?
+ howmany(lastlbn - lbn, blksperindir) : NINDIR(&sblock);
+ for (i = 0; i < last; i++) {
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ iptr = ((ufs1_daddr_t *)ibuf)[i];
+ else
+ iptr = ((ufs2_daddr_t *)ibuf)[i];
+ if (iptr == 0)
+ continue;
+ if (cond_bl_upd(&iptr, bp, fsi, fso, Nflag)) {
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ ((ufs1_daddr_t *)ibuf)[i] = iptr;
+ else
+ ((ufs2_daddr_t *)ibuf)[i] = iptr;
+ }
+ if (blksperindir == 1)
+ continue;
+ indirchk(blksperindir / NINDIR(&sblock), lbn + blksperindir * i,
+ iptr, lastlbn, bp, fsi, fso, Nflag);
+ }
+ free(ibuf);
+
+ DBG_LEAVE;
+ return;
+}
diff --git a/sbin/gvinum/Makefile b/sbin/gvinum/Makefile
new file mode 100644
index 0000000..6d41497
--- /dev/null
+++ b/sbin/gvinum/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG= gvinum
+SRCS= gvinum.c gvinum.h geom_vinum_share.c
+MAN= gvinum.8
+
+CFLAGS+= -I${.CURDIR}/../../sys
+
+DPADD= ${LIBREADLINE} ${LIBTERMCAP} ${LIBDEVSTAT} ${LIBKVM} ${LIBGEOM}
+LDADD= -lreadline -ltermcap -ldevstat -lkvm -lgeom
+
+.PATH: ${.CURDIR}/../../sys/geom/vinum
+
+.include <bsd.prog.mk>
diff --git a/sbin/gvinum/gvinum.8 b/sbin/gvinum/gvinum.8
new file mode 100644
index 0000000..582e509
--- /dev/null
+++ b/sbin/gvinum/gvinum.8
@@ -0,0 +1,397 @@
+.\" Copyright (c) 2005 Chris Jones
+.\" All rights reserved.
+.\"
+.\" This software was developed for the FreeBSD Project by Chris Jones
+.\" thanks to the support of Google's Summer of Code program and
+.\" mentoring by Lukas Ertl.
+.\"
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 1, 2005
+.Dt GVINUM 8
+.Os
+.Sh NAME
+.Nm gvinum
+.Nd Logical Volume Manager control program
+.Sh SYNOPSIS
+.Nm
+.Op Ar command
+.Op Fl options
+.Sh COMMANDS
+.Bl -tag -width indent
+.It Xo
+.Ic checkparity
+.Op Fl f
+.Ar plex
+.Xc
+Check the parity blocks of a RAID-5 plex. The parity check will start at the
+beginning of the plex if the
+.Fl f
+flag is specified, or otherwise at the location of the parity check pointer,
+the first location at which plex's parity is incorrect. All subdisks in the
+plex must be up for a parity check.
+.It Xo
+.Ic create
+.Op Ar description-file
+.Xc
+Create a volume as described in
+.Ar description-file .
+If no
+.Ar description-file
+provided, opens an editor and provides the current
+.Nm
+configuration for editing.
+.It Xo
+.Ic help
+.Xc
+Provides a synopsis of
+.Nm
+commands and arguments.
+.It Xo
+.Ic l | list
+.Op Fl r
+.Op Fl v
+.Op Fl V
+.Op Ar volume | plex | subdisk
+.Xc
+.It Xo
+.Ic ld
+.Op Fl r
+.Op Fl v
+.Op Fl V
+.Op Ar drive ...
+.Xc
+.It Xo
+.Ic ls
+.Op Fl r
+.Op Fl v
+.Op Fl V
+.Op Ar subdisk ...
+.Xc
+.It Xo
+.Ic lp
+.Op Fl r
+.Op Fl v
+.Op Fl V
+.Op Ar plex ...
+.Xc
+.It Xo
+.Ic lv
+.Op Fl r
+.Op Fl v
+.Op Fl V
+.Op Ar volume ...
+.Xc
+List information about the relevant object(s). The
+.Fl r
+flag provides recursive display, showing each object's subordinate objects in
+proper relation. The
+.Fl v
+and
+.Fl V
+flags provide progressively more detailed output.
+.It Xo
+.Ic move | mv
+.Fl f
+.Ar drive subdisk
+.Op Ar ...
+.Xc
+Move the subdisk(s) to the specified drive. The
+.Fl f
+flag is required, as all data on the indicated subdisk(s) will be destroyed as
+part of the move. This can currently only be done when the subdisk is
+not being accessed.
+.Pp
+If the subdisk(s) form part of a RAID-5 plex, the disk(s) will need to be set
+to the 'up' state and the plex will require a
+.Ic rebuildparity
+command; if the subdisk(s) form part of a plex that is mirrored with other
+plexes, the plex will require restarting and will sync once restarted. Moving
+more than one subdisk in a RAID-5 plex or subdisks from both sides of a
+mirrored plex volume will destroy data. Note that parity rebuilds and syncing
+must be started manually after a move.
+.It Xo
+.Ic printconfig
+.Xc
+Write a copy of the current configuration to standard output.
+.It Xo
+.Ic quit
+.Xc
+Exit
+.Nm
+when running in interactive mode. Normally this would be done by entering the
+EOF character.
+.It Xo
+.Ic rename
+.Op Fl r
+.Ar drive | subdisk | plex | volume
+.Ar newname
+.Xc
+Change the name of the specified object. The
+.Fl r
+flag will recursively rename subordinate objects.
+.Pp
+Note that device nodes will not be renamed until
+.Nm
+is restarted.
+.It Xo
+.Ic rebuildparity
+.Op Fl f
+.Ar plex
+.Xc
+Rebuild the parity blocks of a RAID-5 plex. The parity rebuild will start at
+the beginning of the plex if the
+.Fl f
+flag is specified, or otherwise at the location of the parity check pointer.
+All subdisks in the plex must be up for a parity check.
+.It Xo
+.Ic rm
+.Op Fl r
+.Ar volume | plex | subdisk
+.Xc
+Remove an object and, if
+.Fl r
+is specified, its subordinate objects.
+.It Xo
+.Ic saveconfig
+.Xc
+Save
+.Nm
+configuration to disk after configuration failures.
+.It Xo
+.Ic setstate
+.Op Fl f
+.Ar state
+.Ar volume | plex | subdisk | drive
+.Xc
+Set state without influencing other objects, for diagnostic purposes
+only. The
+.Fl f
+flag forces state changes regardless of whether they are legal.
+.It Xo
+.Ic start
+.Xc
+Read configuration from all vinum drives.
+.It Xo
+.Ic start
+.Op Fl S Ar size
+.Ar volume | plex | subdisk
+.Xc
+Allow the system to access the objects. The
+.Fl S
+flag is currently ignored.
+.El
+.Sh DESCRIPTION
+The
+.Nm
+utility communicates with the kernel component of the GVinum logical volume
+manager. It is designed either for interactive use, when started without
+command line arguments, or to execute a single command if the command is
+supplied on the command line. In interactive mode,
+.Nm
+maintains a command line history.
+.Sh OPTIONS
+.Nm
+commands may be followed by an option.
+.Bl -tag -width indent
+.It Fl f
+The
+.Fl f
+.Pq Dq force
+option overrides safety checks. It should be used with extreme caution. This
+option is required in order to use the
+.Ic move
+command.
+.It Fl r
+The
+.Fl r
+.Pq Dq recursive
+option applies the command recursively to subordinate objects. For example, in
+conjunction with the
+.Ic lv
+command, the
+.Fl r
+option will also show information about the plexes and subdisks belonging to
+the volume. It is also used by the
+.Ic rename
+command to indicate that subordinate objects such as subdisks should be renamed
+to match the object(s) specified and by the
+.Ic rm
+command to delete plexes belonging to a volume and so on.
+.It Fl v
+The
+.Fl v
+.Pq Dq verbose
+option provides more detailed output.
+.It Fl V
+The
+.Fl V
+.Pq Dq very verbose
+option provides even more detailed output than
+.Fl v .
+.El
+.Sh FILES
+.Bl -tag -width /dev/gvinum/plex
+.It Pa /dev/gvinum
+directory with device nodes for
+.Nm
+objects
+.It Pa /dev/gvinum/plex
+directory containing device nodes for
+.Nm
+plexes
+.It Pa /dev/gvinum/sd
+directory containing device nodes for
+.Nm
+subdisks
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width EDITOR
+.It Ev EDITOR
+The name of the editor to use for editing configuration files, by
+default
+.Nm vi .
+.El
+.Sh SEE ALSO
+.Xr geom 4 ,
+.Xr geom 8
+.Pp
+.Sh AUTHORS
+.An Lukas Ertl Aq le@freebsd.org
+.An Chris Jones Aq soc-cjones@freebsd.org
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.3 . The
+.Nm vinum
+utility, on which
+.Nm
+is based, was written by Greg Lehey.
+.Pp
+.Nm
+was written by Lukas Ertl. The move and rename commands and
+documentation were added by Chris Jones through the 2005 Google Summer
+of Code program.
+.Sh BUGS
+.Xr gvinum 8 does not rename devices in
+.Pa /dev/gvinum
+until reloaded.
+.Pp
+The
+.Fl S
+initsize flag to
+.Ic start
+is ignored.
+.Pp
+The
+.Ic stop
+command does not work.
+.Pp
+Moving subdisks that are not part of a mirrored or RAID-5 volume will
+destroy data. It is perhaps a bug to permit this.
+.Pp
+Plexes in which subdisks have been moved do not automatically sync or
+rebuild parity. This may leave data unprotected and is perhaps unwise.
+.Pp
+.Xr gvinum 8 does not yet fully implement all functions found in
+.Xr vinum 4 . Specifically, the following commands from
+.Xr vinum 4 are not supported:
+.Bl -tag -width indent
+.It Ic attach Ar plex volume Op Cm rename
+.It Xo
+.Ic attach Ar subdisk plex
+.Op Ar offset
+.Op Cm rename
+.Xc
+Attach a plex to a volume, or a subdisk to a plex.
+.It Xo
+.Ic concat
+.Op Fl f
+.Op Fl n Ar name
+.Op Fl v
+.Ar drives
+.Xc
+Create a concatenated volume from the specified drives.
+.It Ic debug
+Cause the volume manager to enter the kernel debugger.
+.It Ic debug Ar flags
+Set debugging flags.
+.It Xo
+.Ic detach
+.Op Fl f
+.Op Ar plex | subdisk
+.Xc
+Detach a plex or subdisk from the volume or plex to which it is
+attached.
+.It Ic dumpconfig Op Ar drive ...
+List the configuration information stored on the specified drives, or all
+drives in the system if no drive names are specified.
+.It Xo
+.Ic info
+.Op Fl v
+.Op Fl V
+.Xc
+List information about volume manager state.
+.It Ic label Ar volume
+Create a volume label.
+.It Xo
+.Ic mirror
+.Op Fl f
+.Op Fl n Ar name
+.Op Fl s
+.Op Fl v
+.Ar drives
+.Xc
+Create a mirrored volume from the specified drives.
+.It Ic resetconfig
+Reset the complete
+.Nm
+configuration.
+.It Xo
+.Ic resetstats
+.Op Fl r
+.Op Ar volume | plex | subdisk
+.Xc
+Reset statistics counters for the specified objects, or for all objects if none
+are specified.
+.It Ic setdaemon Op Ar value
+Set daemon configuration.
+.It Xo
+.Ic stop
+.Op Fl f
+.Op Ar volume | plex | subdisk
+.Xc
+Terminate access to the objects, or stop
+.Nm
+if no parameters are specified.
+.It Xo
+.Ic stripe
+.Op Fl f
+.Op Fl n Ar name
+.Op Fl v
+.Ar drives
+.Xc
+Create a striped volume from the specified drives.
+.El
diff --git a/sbin/gvinum/gvinum.c b/sbin/gvinum/gvinum.c
new file mode 100644
index 0000000..23a21b9
--- /dev/null
+++ b/sbin/gvinum/gvinum.c
@@ -0,0 +1,906 @@
+/*
+ * Copyright (c) 2004 Lukas Ertl, 2005 Chris Jones
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project
+ * by Chris Jones thanks to the support of Google's Summer of Code
+ * program and mentoring by Lukas Ertl.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/utsname.h>
+
+#include <geom/vinum/geom_vinum_var.h>
+#include <geom/vinum/geom_vinum_share.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <libgeom.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <paths.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <unistd.h>
+
+#include "gvinum.h"
+
+void gvinum_create(int, char **);
+void gvinum_help(void);
+void gvinum_list(int, char **);
+void gvinum_move(int, char **);
+void gvinum_parityop(int, char **, int);
+void gvinum_printconfig(int, char **);
+void gvinum_rename(int, char **);
+void gvinum_rm(int, char **);
+void gvinum_saveconfig(void);
+void gvinum_setstate(int, char **);
+void gvinum_start(int, char **);
+void gvinum_stop(int, char **);
+void parseline(int, char **);
+void printconfig(FILE *, char *);
+
+int
+main(int argc, char **argv)
+{
+ int line, tokens;
+ char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
+
+ /* Load the module if necessary. */
+ if (kldfind(GVINUMMOD) < 0 && kldload(GVINUMMOD) < 0)
+ err(1, GVINUMMOD ": Kernel module not available");
+
+ /* Arguments given on the command line. */
+ if (argc > 1) {
+ argc--;
+ argv++;
+ parseline(argc, argv);
+
+ /* Interactive mode. */
+ } else {
+ for (;;) {
+ inputline = readline("gvinum -> ");
+ if (inputline == NULL) {
+ if (ferror(stdin)) {
+ err(1, "can't read input");
+ } else {
+ printf("\n");
+ exit(0);
+ }
+ } else if (*inputline) {
+ add_history(inputline);
+ strcpy(buffer, inputline);
+ free(inputline);
+ line++; /* count the lines */
+ tokens = gv_tokenize(buffer, token, GV_MAXARGS);
+ if (tokens)
+ parseline(tokens, token);
+ }
+ }
+ }
+ exit(0);
+}
+
+void
+gvinum_create(int argc, char **argv)
+{
+ struct gctl_req *req;
+ struct gv_drive *d;
+ struct gv_plex *p;
+ struct gv_sd *s;
+ struct gv_volume *v;
+ FILE *tmp;
+ int drives, errors, fd, line, plexes, plex_in_volume;
+ int sd_in_plex, status, subdisks, tokens, volumes;
+ const char *errstr;
+ char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed;
+ char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS];
+ char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME];
+
+ if (argc == 2) {
+ if ((tmp = fopen(argv[1], "r")) == NULL) {
+ warn("can't open '%s' for reading", argv[1]);
+ return;
+ }
+ } else {
+ snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
+
+ if ((fd = mkstemp(tmpfile)) == -1) {
+ warn("temporary file not accessible");
+ return;
+ }
+ if ((tmp = fdopen(fd, "w")) == NULL) {
+ warn("can't open '%s' for writing", tmpfile);
+ return;
+ }
+ printconfig(tmp, "# ");
+ fclose(tmp);
+
+ ed = getenv("EDITOR");
+ if (ed == NULL)
+ ed = _PATH_VI;
+
+ snprintf(commandline, sizeof(commandline), "%s %s", ed,
+ tmpfile);
+ status = system(commandline);
+ if (status != 0) {
+ warn("couldn't exec %s; status: %d", ed, status);
+ return;
+ }
+
+ if ((tmp = fopen(tmpfile, "r")) == NULL) {
+ warn("can't open '%s' for reading", tmpfile);
+ return;
+ }
+ }
+
+ req = gctl_get_handle();
+ gctl_ro_param(req, "class", -1, "VINUM");
+ gctl_ro_param(req, "verb", -1, "create");
+
+ drives = volumes = plexes = subdisks = 0;
+ plex_in_volume = sd_in_plex = 0;
+ errors = 0;
+ line = 1;
+ while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
+
+ /* Skip empty lines and comments. */
+ if (*buf == '\0' || *buf == '#') {
+ line++;
+ continue;
+ }
+
+ /* Kill off the newline. */
+ buf[strlen(buf) - 1] = '\0';
+
+ /*
+ * Copy the original input line in case we need it for error
+ * output.
+ */
+ strncpy(original, buf, sizeof(buf));
+
+ tokens = gv_tokenize(buf, token, GV_MAXARGS);
+ if (tokens <= 0) {
+ line++;
+ continue;
+ }
+
+ /* Volume definition. */
+ if (!strcmp(token[0], "volume")) {
+ v = gv_new_volume(tokens, token);
+ if (v == NULL) {
+ warnx("line %d: invalid volume definition",
+ line);
+ warnx("line %d: '%s'", line, original);
+ errors++;
+ line++;
+ continue;
+ }
+
+ /* Reset plex count for this volume. */
+ plex_in_volume = 0;
+
+ /*
+ * Set default volume name for following plex
+ * definitions.
+ */
+ strncpy(volume, v->name, sizeof(volume));
+
+ snprintf(buf1, sizeof(buf1), "volume%d", volumes);
+ gctl_ro_param(req, buf1, sizeof(*v), v);
+ volumes++;
+
+ /* Plex definition. */
+ } else if (!strcmp(token[0], "plex")) {
+ p = gv_new_plex(tokens, token);
+ if (p == NULL) {
+ warnx("line %d: invalid plex definition", line);
+ warnx("line %d: '%s'", line, original);
+ errors++;
+ line++;
+ continue;
+ }
+
+ /* Reset subdisk count for this plex. */
+ sd_in_plex = 0;
+
+ /* Default name. */
+ if (strlen(p->name) == 0) {
+ snprintf(p->name, GV_MAXPLEXNAME, "%s.p%d",
+ volume, plex_in_volume++);
+ }
+
+ /* Default volume. */
+ if (strlen(p->volume) == 0) {
+ snprintf(p->volume, GV_MAXVOLNAME, "%s",
+ volume);
+ }
+
+ /*
+ * Set default plex name for following subdisk
+ * definitions.
+ */
+ strncpy(plex, p->name, GV_MAXPLEXNAME);
+
+ snprintf(buf1, sizeof(buf1), "plex%d", plexes);
+ gctl_ro_param(req, buf1, sizeof(*p), p);
+ plexes++;
+
+ /* Subdisk definition. */
+ } else if (!strcmp(token[0], "sd")) {
+ s = gv_new_sd(tokens, token);
+ if (s == NULL) {
+ warnx("line %d: invalid subdisk "
+ "definition:", line);
+ warnx("line %d: '%s'", line, original);
+ errors++;
+ line++;
+ continue;
+ }
+
+ /* Default name. */
+ if (strlen(s->name) == 0) {
+ snprintf(s->name, GV_MAXSDNAME, "%s.s%d",
+ plex, sd_in_plex++);
+ }
+
+ /* Default plex. */
+ if (strlen(s->plex) == 0)
+ snprintf(s->plex, GV_MAXPLEXNAME, "%s", plex);
+
+ snprintf(buf1, sizeof(buf1), "sd%d", subdisks);
+ gctl_ro_param(req, buf1, sizeof(*s), s);
+ subdisks++;
+
+ /* Subdisk definition. */
+ } else if (!strcmp(token[0], "drive")) {
+ d = gv_new_drive(tokens, token);
+ if (d == NULL) {
+ warnx("line %d: invalid drive definition:",
+ line);
+ warnx("line %d: '%s'", line, original);
+ errors++;
+ line++;
+ continue;
+ }
+
+ snprintf(buf1, sizeof(buf1), "drive%d", drives);
+ gctl_ro_param(req, buf1, sizeof(*d), d);
+ drives++;
+
+ /* Everything else is bogus. */
+ } else {
+ warnx("line %d: invalid definition:", line);
+ warnx("line %d: '%s'", line, original);
+ errors++;
+ }
+ line++;
+ }
+
+ fclose(tmp);
+ unlink(tmpfile);
+
+ if (!errors && (volumes || plexes || subdisks || drives)) {
+ gctl_ro_param(req, "volumes", sizeof(int), &volumes);
+ gctl_ro_param(req, "plexes", sizeof(int), &plexes);
+ gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
+ gctl_ro_param(req, "drives", sizeof(int), &drives);
+ errstr = gctl_issue(req);
+ if (errstr != NULL)
+ warnx("create failed: %s", errstr);
+ }
+ gctl_free(req);
+ gvinum_list(0, NULL);
+}
+
+void
+gvinum_help(void)
+{
+ printf("COMMANDS\n"
+ "checkparity [-f] plex\n"
+ " Check the parity blocks of a RAID-5 plex.\n"
+ "create description-file\n"
+ " Create as per description-file or open editor.\n"
+ "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n"
+ " List information about specified objects.\n"
+ "ld [-r] [-v] [-V] [volume]\n"
+ " List information about drives.\n"
+ "ls [-r] [-v] [-V] [subdisk]\n"
+ " List information about subdisks.\n"
+ "lp [-r] [-v] [-V] [plex]\n"
+ " List information about plexes.\n"
+ "lv [-r] [-v] [-V] [volume]\n"
+ " List information about volumes.\n"
+ "move | mv -f drive object ...\n"
+ " Move the object(s) to the specified drive.\n"
+ "quit Exit the vinum program when running in interactive mode."
+ " Nor-\n"
+ " mally this would be done by entering the EOF character.\n"
+ "rename [-r] [drive | subdisk | plex | volume] newname\n"
+ " Change the name of the specified object.\n"
+ "rebuildparity plex [-f]\n"
+ " Rebuild the parity blocks of a RAID-5 plex.\n"
+ "rm [-r] volume | plex | subdisk | drive\n"
+ " Remove an object.\n"
+ "saveconfig\n"
+ " Save vinum configuration to disk after configuration"
+ " failures.\n"
+ "setstate [-f] state [volume | plex | subdisk | drive]\n"
+ " Set state without influencing other objects, for"
+ " diagnostic pur-\n"
+ " poses only.\n"
+ "start [-S size] volume | plex | subdisk\n"
+ " Allow the system to access the objects.\n"
+ );
+
+ return;
+}
+
+void
+gvinum_setstate(int argc, char **argv)
+{
+ struct gctl_req *req;
+ int flags, i;
+ const char *errstr;
+
+ flags = 0;
+
+ optreset = 1;
+ optind = 1;
+
+ while ((i = getopt(argc, argv, "f")) != -1) {
+ switch (i) {
+ case 'f':
+ flags |= GV_FLAG_F;
+ break;
+ case '?':
+ default:
+ warn("invalid flag: %c", i);
+ return;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2) {
+ warnx("usage: setstate [-f] <state> <obj>");
+ return;
+ }
+
+ /*
+ * XXX: This hack is needed to avoid tripping over (now) invalid
+ * 'classic' vinum states and will go away later.
+ */
+ if (strcmp(argv[0], "up") && strcmp(argv[0], "down") &&
+ strcmp(argv[0], "stale")) {
+ warnx("invalid state '%s'", argv[0]);
+ return;
+ }
+
+ req = gctl_get_handle();
+ gctl_ro_param(req, "class", -1, "VINUM");
+ gctl_ro_param(req, "verb", -1, "setstate");
+ gctl_ro_param(req, "state", -1, argv[0]);
+ gctl_ro_param(req, "object", -1, argv[1]);
+ gctl_ro_param(req, "flags", sizeof(int), &flags);
+
+ errstr = gctl_issue(req);
+ if (errstr != NULL)
+ warnx("%s", errstr);
+ gctl_free(req);
+}
+
+void
+gvinum_list(int argc, char **argv)
+{
+ struct gctl_req *req;
+ int flags, i, j;
+ const char *errstr;
+ char buf[20], *cmd, config[GV_CFG_LEN + 1];
+
+ flags = 0;
+ cmd = "list";
+
+ if (argc) {
+ optreset = 1;
+ optind = 1;
+ cmd = argv[0];
+ while ((j = getopt(argc, argv, "rsvV")) != -1) {
+ switch (j) {
+ case 'r':
+ flags |= GV_FLAG_R;
+ break;
+ case 's':
+ flags |= GV_FLAG_S;
+ break;
+ case 'v':
+ flags |= GV_FLAG_V;
+ break;
+ case 'V':
+ flags |= GV_FLAG_V;
+ flags |= GV_FLAG_VV;
+ break;
+ case '?':
+ default:
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ }
+
+ req = gctl_get_handle();
+ gctl_ro_param(req, "class", -1, "VINUM");
+ gctl_ro_param(req, "verb", -1, "list");
+ gctl_ro_param(req, "cmd", -1, cmd);
+ gctl_ro_param(req, "argc", sizeof(int), &argc);
+ gctl_ro_param(req, "flags", sizeof(int), &flags);
+ gctl_rw_param(req, "config", sizeof(config), config);
+ if (argc) {
+ for (i = 0; i < argc; i++) {
+ snprintf(buf, sizeof(buf), "argv%d", i);
+ gctl_ro_param(req, buf, -1, argv[i]);
+ }
+ }
+ errstr = gctl_issue(req);
+ if (errstr != NULL) {
+ warnx("can't get configuration: %s", errstr);
+ gctl_free(req);
+ return;
+ }
+
+ printf("%s", config);
+ gctl_free(req);
+ return;
+}
+
+/* Note that move is currently of form '[-r] target object [...]' */
+void
+gvinum_move(int argc, char **argv)
+{
+ struct gctl_req *req;
+ const char *errstr;
+ char buf[20];
+ int flags, i, j;
+
+ flags = 0;
+ if (argc) {
+ optreset = 1;
+ optind = 1;
+ while ((j = getopt(argc, argv, "f")) != -1) {
+ switch (j) {
+ case 'f':
+ flags |= GV_FLAG_F;
+ break;
+ case '?':
+ default:
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ }
+
+ switch (argc) {
+ case 0:
+ warnx("no destination or object(s) to move specified");
+ return;
+ case 1:
+ warnx("no object(s) to move specified");
+ return;
+ default:
+ break;
+ }
+
+ req = gctl_get_handle();
+ gctl_ro_param(req, "class", -1, "VINUM");
+ gctl_ro_param(req, "verb", -1, "move");
+ gctl_ro_param(req, "argc", sizeof(int), &argc);
+ gctl_ro_param(req, "flags", sizeof(int), &flags);
+ gctl_ro_param(req, "destination", -1, argv[0]);
+ for (i = 1; i < argc; i++) {
+ snprintf(buf, sizeof(buf), "argv%d", i);
+ gctl_ro_param(req, buf, -1, argv[i]);
+ }
+ errstr = gctl_issue(req);
+ if (errstr != NULL)
+ warnx("can't move object(s): %s", errstr);
+ gctl_free(req);
+ return;
+}
+
+void
+gvinum_printconfig(int argc, char **argv)
+{
+ printconfig(stdout, "");
+}
+
+void
+gvinum_parityop(int argc, char **argv, int rebuild)
+{
+ struct gctl_req *req;
+ int flags, i, rv;
+ off_t offset;
+ const char *errstr;
+ char *op, *msg;
+
+ if (rebuild) {
+ op = "rebuildparity";
+ msg = "Rebuilding";
+ } else {
+ op = "checkparity";
+ msg = "Checking";
+ }
+
+ optreset = 1;
+ optind = 1;
+ flags = 0;
+ while ((i = getopt(argc, argv, "fv")) != -1) {
+ switch (i) {
+ case 'f':
+ flags |= GV_FLAG_F;
+ break;
+ case 'v':
+ flags |= GV_FLAG_V;
+ break;
+ case '?':
+ default:
+ warnx("invalid flag '%c'", i);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ warn("usage: %s [-f] [-v] <plex>", op);
+ return;
+ }
+
+ do {
+ rv = 0;
+ req = gctl_get_handle();
+ gctl_ro_param(req, "class", -1, "VINUM");
+ gctl_ro_param(req, "verb", -1, "parityop");
+ gctl_ro_param(req, "flags", sizeof(int), &flags);
+ gctl_ro_param(req, "rebuild", sizeof(int), &rebuild);
+ gctl_rw_param(req, "rv", sizeof(int), &rv);
+ gctl_rw_param(req, "offset", sizeof(off_t), &offset);
+ gctl_ro_param(req, "plex", -1, argv[0]);
+ errstr = gctl_issue(req);
+ if (errstr) {
+ warnx("%s\n", errstr);
+ gctl_free(req);
+ break;
+ }
+ gctl_free(req);
+ if (flags & GV_FLAG_V) {
+ printf("\r%s at %s ... ", msg,
+ gv_roughlength(offset, 1));
+ }
+ if (rv == 1) {
+ printf("Parity incorrect at offset 0x%jx\n",
+ (intmax_t)offset);
+ if (!rebuild)
+ break;
+ }
+ fflush(stdout);
+
+ /* Clear the -f flag. */
+ flags &= ~GV_FLAG_F;
+ } while (rv >= 0);
+
+ if ((rv == 2) && (flags & GV_FLAG_V)) {
+ if (rebuild)
+ printf("Rebuilt parity on %s\n", argv[0]);
+ else
+ printf("%s has correct parity\n", argv[0]);
+ }
+}
+
+void
+gvinum_rename(int argc, char **argv)
+{
+ struct gctl_req *req;
+ const char *errstr;
+ int flags, j;
+
+ flags = 0;
+
+ if (argc) {
+ optreset = 1;
+ optind = 1;
+ while ((j = getopt(argc, argv, "r")) != -1) {
+ switch (j) {
+ case 'r':
+ flags |= GV_FLAG_R;
+ break;
+ case '?':
+ default:
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ }
+
+ switch (argc) {
+ case 0:
+ warnx("no object to rename specified");
+ return;
+ case 1:
+ warnx("no new name specified");
+ return;
+ case 2:
+ break;
+ default:
+ warnx("more than one new name specified");
+ return;
+ }
+
+ req = gctl_get_handle();
+ gctl_ro_param(req, "class", -1, "VINUM");
+ gctl_ro_param(req, "verb", -1, "rename");
+ gctl_ro_param(req, "flags", sizeof(int), &flags);
+ gctl_ro_param(req, "object", -1, argv[0]);
+ gctl_ro_param(req, "newname", -1, argv[1]);
+ errstr = gctl_issue(req);
+ if (errstr != NULL)
+ warnx("can't rename object: %s", errstr);
+ gctl_free(req);
+ return;
+}
+
+void
+gvinum_rm(int argc, char **argv)
+{
+ struct gctl_req *req;
+ int flags, i, j;
+ const char *errstr;
+ char buf[20], *cmd;
+
+ cmd = argv[0];
+ flags = 0;
+ optreset = 1;
+ optind = 1;
+ while ((j = getopt(argc, argv, "r")) != -1) {
+ switch (j) {
+ case 'r':
+ flags |= GV_FLAG_R;
+ break;
+ case '?':
+ default:
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ req = gctl_get_handle();
+ gctl_ro_param(req, "class", -1, "VINUM");
+ gctl_ro_param(req, "verb", -1, "remove");
+ gctl_ro_param(req, "argc", sizeof(int), &argc);
+ gctl_ro_param(req, "flags", sizeof(int), &flags);
+ if (argc) {
+ for (i = 0; i < argc; i++) {
+ snprintf(buf, sizeof(buf), "argv%d", i);
+ gctl_ro_param(req, buf, -1, argv[i]);
+ }
+ }
+ errstr = gctl_issue(req);
+ if (errstr != NULL) {
+ warnx("can't remove: %s", errstr);
+ gctl_free(req);
+ return;
+ }
+ gctl_free(req);
+ gvinum_list(0, NULL);
+}
+
+void
+gvinum_saveconfig(void)
+{
+ struct gctl_req *req;
+ const char *errstr;
+
+ req = gctl_get_handle();
+ gctl_ro_param(req, "class", -1, "VINUM");
+ gctl_ro_param(req, "verb", -1, "saveconfig");
+ errstr = gctl_issue(req);
+ if (errstr != NULL)
+ warnx("can't save configuration: %s", errstr);
+ gctl_free(req);
+}
+
+void
+gvinum_start(int argc, char **argv)
+{
+ struct gctl_req *req;
+ int i, initsize, j;
+ const char *errstr;
+ char buf[20];
+
+ /* 'start' with no arguments is a no-op. */
+ if (argc == 1)
+ return;
+
+ initsize = 0;
+
+ optreset = 1;
+ optind = 1;
+ while ((j = getopt(argc, argv, "S")) != -1) {
+ switch (j) {
+ case 'S':
+ initsize = atoi(optarg);
+ break;
+ case '?':
+ default:
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!initsize)
+ initsize = 512;
+
+ req = gctl_get_handle();
+ gctl_ro_param(req, "class", -1, "VINUM");
+ gctl_ro_param(req, "verb", -1, "start");
+ gctl_ro_param(req, "argc", sizeof(int), &argc);
+ gctl_ro_param(req, "initsize", sizeof(int), &initsize);
+ if (argc) {
+ for (i = 0; i < argc; i++) {
+ snprintf(buf, sizeof(buf), "argv%d", i);
+ gctl_ro_param(req, buf, -1, argv[i]);
+ }
+ }
+ errstr = gctl_issue(req);
+ if (errstr != NULL) {
+ warnx("can't start: %s", errstr);
+ gctl_free(req);
+ return;
+ }
+
+ gctl_free(req);
+ gvinum_list(0, NULL);
+}
+
+void
+gvinum_stop(int argc, char **argv)
+{
+ int fileid;
+
+ fileid = kldfind(GVINUMMOD);
+ if (fileid == -1) {
+ warn("cannot find " GVINUMMOD);
+ return;
+ }
+ if (kldunload(fileid) != 0) {
+ warn("cannot unload " GVINUMMOD);
+ return;
+ }
+
+ warnx(GVINUMMOD " unloaded");
+ exit(0);
+}
+
+void
+parseline(int argc, char **argv)
+{
+ if (argc <= 0)
+ return;
+
+ if (!strcmp(argv[0], "create"))
+ gvinum_create(argc, argv);
+ else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
+ exit(0);
+ else if (!strcmp(argv[0], "help"))
+ gvinum_help();
+ else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l"))
+ gvinum_list(argc, argv);
+ else if (!strcmp(argv[0], "ld"))
+ gvinum_list(argc, argv);
+ else if (!strcmp(argv[0], "lp"))
+ gvinum_list(argc, argv);
+ else if (!strcmp(argv[0], "ls"))
+ gvinum_list(argc, argv);
+ else if (!strcmp(argv[0], "lv"))
+ gvinum_list(argc, argv);
+ else if (!strcmp(argv[0], "move"))
+ gvinum_move(argc, argv);
+ else if (!strcmp(argv[0], "mv"))
+ gvinum_move(argc, argv);
+ else if (!strcmp(argv[0], "printconfig"))
+ gvinum_printconfig(argc, argv);
+ else if (!strcmp(argv[0], "rename"))
+ gvinum_rename(argc, argv);
+ else if (!strcmp(argv[0], "rm"))
+ gvinum_rm(argc, argv);
+ else if (!strcmp(argv[0], "saveconfig"))
+ gvinum_saveconfig();
+ else if (!strcmp(argv[0], "setstate"))
+ gvinum_setstate(argc, argv);
+ else if (!strcmp(argv[0], "start"))
+ gvinum_start(argc, argv);
+ else if (!strcmp(argv[0], "stop"))
+ gvinum_stop(argc, argv);
+ else if (!strcmp(argv[0], "checkparity"))
+ gvinum_parityop(argc, argv, 0);
+ else if (!strcmp(argv[0], "rebuildparity"))
+ gvinum_parityop(argc, argv, 1);
+ else
+ printf("unknown command '%s'\n", argv[0]);
+
+ return;
+}
+
+/*
+ * The guts of printconfig. This is called from gvinum_printconfig and from
+ * gvinum_create when called without an argument, in order to give the user
+ * something to edit.
+ */
+void
+printconfig(FILE *of, char *comment)
+{
+ struct gctl_req *req;
+ struct utsname uname_s;
+ const char *errstr;
+ time_t now;
+ char buf[GV_CFG_LEN + 1];
+
+ uname(&uname_s);
+ time(&now);
+
+ req = gctl_get_handle();
+ gctl_ro_param(req, "class", -1, "VINUM");
+ gctl_ro_param(req, "verb", -1, "getconfig");
+ gctl_ro_param(req, "comment", -1, comment);
+ gctl_rw_param(req, "config", sizeof(buf), buf);
+ errstr = gctl_issue(req);
+ if (errstr != NULL) {
+ warnx("can't get configuration: %s", errstr);
+ return;
+ }
+ gctl_free(req);
+
+ fprintf(of, "# Vinum configuration of %s, saved at %s",
+ uname_s.nodename,
+ ctime(&now));
+
+ if (*comment != '\0')
+ fprintf(of, "# Current configuration:\n");
+
+ fprintf(of, buf);
+}
diff --git a/sbin/gvinum/gvinum.h b/sbin/gvinum/gvinum.h
new file mode 100644
index 0000000..d1b45a0
--- /dev/null
+++ b/sbin/gvinum/gvinum.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 1997, 1998
+ * Nan Yang Computer Services Limited. All rights reserved.
+ *
+ * This software is distributed under the so-called ``Berkeley
+ * License'':
+ *
+ * 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 Nan Yang Computer
+ * Services Limited.
+ * 4. Neither the name of the Company 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 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 company 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.
+ */
+
+/* $FreeBSD$ */
+
+#define GVINUMMOD "geom_vinum"
diff --git a/sbin/idmapd/Makefile b/sbin/idmapd/Makefile
new file mode 100644
index 0000000..462bc59
--- /dev/null
+++ b/sbin/idmapd/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.2 (Berkeley) 3/27/94
+#
+# $FreeBSD$
+
+PROG= idmapd
+MAN= idmapd.8
+
+CFLAGS+= -DNFS -I/usr/src/sys
+WARNS?= 2
+
+.PATH:
+
+.include <bsd.prog.mk>
diff --git a/sbin/idmapd/idmapd.8 b/sbin/idmapd/idmapd.8
new file mode 100644
index 0000000..256897d
--- /dev/null
+++ b/sbin/idmapd/idmapd.8
@@ -0,0 +1,63 @@
+.\" copyright (c) 2003
+.\" the regents of the university of michigan
+.\" all rights reserved
+.\"
+.\" permission is granted to use, copy, create derivative works and redistribute
+.\" this software and such derivative works for any purpose, so long as the name
+.\" of the university of michigan is not used in any advertising or publicity
+.\" pertaining to the use or distribution of this software without specific,
+.\" written prior authorization. if the above copyright notice or any other
+.\" identification of the university of michigan is included in any copy of any
+.\" portion of this software, then the disclaimer below must also be included.
+.\"
+.\" this software is provided as is, without representation from the university
+.\" of michigan as to its fitness for any purpose, and without warranty by the
+.\" university of michigan of any kind, either express or implied, including
+.\" without limitation the implied warranties of merchantability and fitness for
+.\" a particular purpose. the regents of the university of michigan shall not be
+.\" liable for any damages, including special, indirect, incidental, or
+.\" consequential damages, with respect to any claim arising out of or in
+.\" connection with the use of the software, even if it has been or is hereafter
+.\" advised of the possibility of such damages.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 14, 2003
+.Dt IDMAPD 8
+.Os
+.Sh NAME
+.Nm idmapd
+.Nd name/UID mapper for NFSv4
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Op Fl d Ar domainname
+.Sh DESCRIPTION
+The
+.Nm
+daemon normally runs in the background and services UID/GID-to-name and
+name-to-UID/GID mapping
+requests from the NFSv4 client.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl d
+Set the domain part of the name string to the specified string.
+.It Fl v
+Be verbose, and do not run in the background.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /etc/master.passwd" -compact
+.It Pa /etc/pwd.db
+The insecure password database file.
+.It Pa /etc/spwd.db
+The secure password database file.
+.It Pa /etc/master.passwd
+The current password file.
+.It Pa /etc/passwd
+A Version 7 format password file.
+.El
+.Sh SEE ALSO
+.Xr getpwnam 3 ,
+.Xr getpwuid 3 ,
+.Xr mount_nfs 8
diff --git a/sbin/idmapd/idmapd.c b/sbin/idmapd/idmapd.c
new file mode 100644
index 0000000..fa3a082
--- /dev/null
+++ b/sbin/idmapd/idmapd.c
@@ -0,0 +1,418 @@
+/* $FreeBSD$ */
+/* $Id: idmapd.c,v 1.5 2003/11/05 14:58:58 rees Exp $ */
+
+/*
+ * copyright (c) 2003
+ * the regents of the university of michigan
+ * all rights reserved
+ *
+ * permission is granted to use, copy, create derivative works and redistribute
+ * this software and such derivative works for any purpose, so long as the name
+ * of the university of michigan is not used in any advertising or publicity
+ * pertaining to the use or distribution of this software without specific,
+ * written prior authorization. if the above copyright notice or any other
+ * identification of the university of michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must also be included.
+ *
+ * this software is provided as is, without representation from the university
+ * of michigan as to its fitness for any purpose, and without warranty by the
+ * university of michigan of any kind, either express or implied, including
+ * without limitation the implied warranties of merchantability and fitness for
+ * a particular purpose. the regents of the university of michigan shall not be
+ * liable for any damages, including special, indirect, incidental, or
+ * consequential damages, with respect to any claim arising out of or in
+ * connection with the use of the software, even if it has been or is hereafter
+ * advised of the possibility of such damages.
+ */
+
+/* XXX ignores the domain of received names. */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+
+#include <nfs4client/nfs4_dev.h>
+#include <nfs4client/nfs4_idmap.h>
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+
+#define DEV_PATH "/dev/nfs4"
+
+#define DOMAIN "@FreeBSD.org"
+#define BADUSER "nobody"
+#define BADGROUP "nogroup"
+#define BADUID 65534
+#define BADGID 65533
+
+struct idmap_e {
+ struct nfs4dev_msg msg;
+ TAILQ_ENTRY(idmap_e) next;
+};
+
+int fd, verbose;
+char *domain = DOMAIN;
+
+TAILQ_HEAD(, idmap_e) upcall_q;
+
+#define add_idmap_e(E) do { \
+ assert(E != NULL); \
+ TAILQ_INSERT_TAIL(&upcall_q, E, next); \
+} while(0)
+
+#define remove_idmap_e(E) do { \
+ assert(E != NULL && !TAILQ_EMPTY(&upcall_q)); \
+ E = TAILQ_FIRST(&upcall_q); \
+ TAILQ_REMOVE(&upcall_q, E, next); \
+} while(0)
+
+#define get_idmap_e(E) do { \
+ if ((E = (struct idmap_e *) malloc(sizeof(struct idmap_e))) == NULL) {\
+ fprintf(stderr, "get_idmap_e(): error in malloc\n");\
+ } } while(0)
+
+#define put_idmap_e(E) free(E)
+
+/* from marius */
+int
+validateascii(char *string, u_int32_t len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (string[i] == '\0')
+ break;
+ if (string[i] & 0x80)
+ return (-1);
+ }
+
+ if (string[i] != '\0')
+ return (-1);
+ return (i + 1);
+}
+
+char *
+idmap_prune_domain(struct idmap_msg * m)
+{
+ size_t i;
+ size_t len;
+ char * ret = NULL;
+
+ if (m == NULL)
+ return NULL;
+
+ len = m->id_namelen;
+
+ if (validateascii(m->id_name, len) < 0) {
+ fprintf(stderr, "msg has invalid ascii\n");
+ return NULL;
+ }
+
+ for (i=0; i < len && m->id_name[i] != '@' ; i++);
+
+ ret = (char *)malloc(i+1);
+ if (ret == NULL)
+ return NULL;
+
+ bcopy(m->id_name, ret, i);
+ ret[i] = '\0';
+
+ return ret;
+}
+
+int
+idmap_add_domain(struct idmap_msg * m, char * name)
+{
+ size_t len, nlen;
+
+ if (m == NULL || name == NULL)
+ return -1;
+
+ len = strlen(name);
+
+ nlen = len + strlen(domain);
+
+ if (nlen > IDMAP_MAXNAMELEN)
+ return -1;
+
+ bcopy(name, &m->id_name[0], len);
+ bcopy(domain, &m->id_name[len], strlen(domain));
+
+ m->id_name[nlen] = '\0';
+ m->id_namelen = nlen;
+
+ return 0;
+}
+
+int
+idmap_name(struct idmap_msg * m, char *name)
+{
+ if (m == NULL || name == NULL || m->id_namelen != 0)
+ return -1;
+
+ if (idmap_add_domain(m, name))
+ return -1;
+
+ return 0;
+}
+
+int
+idmap_id(struct idmap_msg * m, ident_t id)
+{
+ if (m == NULL || m->id_namelen == 0) {
+ fprintf(stderr, "idmap_id: bad msg\n");
+ return -1;
+ }
+
+ switch(m->id_type) {
+ case IDMAP_TYPE_UID:
+ m->id_id.uid = id.uid;
+ break;
+ case IDMAP_TYPE_GID:
+ m->id_id.gid = id.gid;
+ break;
+ default:
+ return -1;
+ break;
+ };
+
+ return 0;
+}
+
+int
+idmap_service(struct idmap_e * e)
+{
+ struct idmap_msg * m;
+ struct passwd * pwd;
+ struct group * grp;
+ ident_t id;
+ char * name;
+
+ if (e == NULL) {
+ fprintf(stderr, "bad entry\n");
+ return -1;
+ }
+
+ if (e->msg.msg_vers != NFS4DEV_VERSION) {
+ fprintf(stderr, "kernel/userland version mismatch! %d/%d\n",
+ e->msg.msg_vers, NFS4DEV_VERSION);
+ return -1;
+ }
+
+ if (e->msg.msg_type != NFS4DEV_TYPE_IDMAP) {
+ fprintf(stderr, "bad type!\n");
+ return -1;
+ }
+
+ if (e->msg.msg_len != sizeof(struct idmap_msg)) {
+ fprintf(stderr, "bad message length: %zu/%zu\n", e->msg.msg_len,
+ sizeof(struct idmap_msg));
+ return -1;
+ }
+
+ if (verbose)
+ printf("servicing msg xid: %x\n", e->msg.msg_xid);
+
+
+ m = (struct idmap_msg *)e->msg.msg_data;
+
+ if (m->id_namelen != 0 && m->id_namelen != strlen(m->id_name)) {
+ fprintf(stderr, "bad name length in idmap_msg\n");
+ return -1;
+ }
+
+ switch (m->id_type) {
+ case IDMAP_TYPE_UID:
+ if (m->id_namelen == 0) {
+ /* id to name */
+ pwd = getpwuid(m->id_id.uid);
+
+ if (pwd == NULL) {
+ fprintf(stderr, "unknown uid %d!\n",
+ (uint32_t)m->id_id.uid);
+ name = BADUSER;
+ } else
+ name = pwd->pw_name;
+
+ if (idmap_name(m, name))
+ return -1;
+
+ } else {
+ /* name to id */
+ name = idmap_prune_domain(m);
+ if (name == NULL)
+ return -1;
+
+ pwd = getpwnam(name);
+
+ if (pwd == NULL) {
+ fprintf(stderr, "unknown username %s!\n", name);
+
+ id.uid = (uid_t)BADUID;
+ } else
+ id.uid = pwd->pw_uid;
+
+ free(name);
+
+ if (idmap_id(m, id))
+ return -1;
+ }
+ break;
+ case IDMAP_TYPE_GID:
+ if (m->id_namelen == 0) {
+ /* id to name */
+ grp = getgrgid(m->id_id.gid);
+
+ if (grp == NULL) {
+ fprintf(stderr, "unknown gid %d!\n",
+ (uint32_t)m->id_id.gid);
+ name = BADGROUP;
+ } else
+ name = grp->gr_name;
+
+ if (idmap_name(m, name))
+ return -1;
+ } else {
+ /* name to id */
+ name = idmap_prune_domain(m);
+ if (name == NULL)
+ return -1;
+
+ grp = getgrnam(name);
+
+ if (grp == NULL) {
+ fprintf(stderr, "unknown groupname %s!\n", name);
+
+ id.gid = (gid_t)BADGID;
+ } else
+ id.gid = grp->gr_gid;
+
+ free(name);
+
+ if (idmap_id(m, id))
+ return -1;
+ }
+ break;
+ default:
+ fprintf(stderr, "bad idmap type: %d\n", m->id_type);
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+int
+main(int argc, char ** argv)
+{
+ int error = 0;
+ struct idmap_e * entry;
+ fd_set read_fds, write_fds;
+ int maxfd;
+ int ret, ch;
+
+ while ((ch = getopt(argc, argv, "d:v")) != -1) {
+ switch (ch) {
+ case 'd':
+ domain = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ fprintf(stderr, "usage: %s [-v] [-d domain]\n", argv[0]);
+ exit(1);
+ break;
+ }
+ }
+
+
+ TAILQ_INIT(&upcall_q);
+
+ fd = open(DEV_PATH, O_RDWR, S_IRUSR | S_IWUSR);
+
+ if (fd < 0) {
+ perror(DEV_PATH);
+ exit(1);
+ }
+
+ if (!verbose)
+ daemon(0,0);
+
+ maxfd = fd;
+ for (;;) {
+ struct timeval timo = {1, 0};
+ do {
+ FD_ZERO(&read_fds);
+ FD_ZERO(&write_fds);
+
+ FD_SET(fd, &read_fds);
+ FD_SET(fd, &write_fds);
+
+ ret = select(maxfd+1, &read_fds, &write_fds, NULL, &timo);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret <= 0) {
+ if (ret != 0)
+ perror("select");
+ continue;
+ }
+
+
+ if (FD_ISSET(fd, &read_fds)) {
+ for (;;) {
+ get_idmap_e(entry);
+
+ error = ioctl(fd, NFS4DEVIOCGET, &entry->msg);
+
+ if (error == -1) {
+ if (errno != EAGAIN)
+ perror("get ioctl:");
+ put_idmap_e(entry);
+ break;
+ }
+
+ switch (entry->msg.msg_type ) {
+ case NFS4DEV_TYPE_IDMAP:
+ if (idmap_service(entry))
+ entry->msg.msg_error = EIO;
+ break;
+ default:
+ fprintf(stderr, "unknown nfs4dev_msg type\n");
+ entry->msg.msg_error = EIO;
+ break;
+ }
+
+ add_idmap_e(entry);
+ }
+ }
+
+ if (FD_ISSET(fd, &write_fds)) {
+ while (!TAILQ_EMPTY(&upcall_q)) {
+ remove_idmap_e(entry);
+
+ error = ioctl(fd, NFS4DEVIOCPUT, &entry->msg);
+
+ if (error == -1) {
+ if (errno != EAGAIN)
+ perror("put ioctl");
+ break;
+ }
+ put_idmap_e(entry);
+ }
+ }
+ }
+
+ /* never reached */
+ exit(1);
+}
diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile
new file mode 100644
index 0000000..7d00149
--- /dev/null
+++ b/sbin/ifconfig/Makefile
@@ -0,0 +1,42 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= ifconfig
+
+SRCS= ifconfig.c # base support
+
+#
+# NB: The order here defines the order in which the constructors
+# are called. This in turn defines the default order in which
+# status is displayed. Probably should add a priority mechanism
+# to the registration process so we don't depend on this aspect
+# of the toolchain.
+#
+SRCS+= af_link.c # LLC support
+SRCS+= af_inet.c # IPv4 support
+SRCS+= af_inet6.c # IPv6 support
+SRCS+= af_atalk.c # AppleTalk support
+
+SRCS+= ifclone.c # clone device support
+SRCS+= ifmac.c # MAC support
+SRCS+= ifmedia.c # SIOC[GS]IFMEDIA support
+SRCS+= ifvlan.c # SIOC[GS]ETVLAN support
+SRCS+= ifieee80211.c # SIOC[GS]IEEE80211 support
+
+SRCS+= ifcarp.c # SIOC[GS]VH support
+SRCS+= ifpfsync.c # pfsync(4) support
+
+SRCS+= ifbridge.c # bridge support
+
+.if !defined(RELEASE_CRUNCH)
+SRCS+= af_ipx.c # IPX support
+DPADD= ${LIBIPX}
+LDADD= -lipx
+.endif
+
+MAN= ifconfig.8
+
+CFLAGS+= -Wall -Wmissing-prototypes -Wcast-qual -Wwrite-strings -Wnested-externs
+WARNS?= 0
+
+.include <bsd.prog.mk>
diff --git a/sbin/ifconfig/af_atalk.c b/sbin/ifconfig/af_atalk.c
new file mode 100644
index 0000000..0ceb8b9
--- /dev/null
+++ b/sbin/ifconfig/af_atalk.c
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ * 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 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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h> /* for RTX_IFA */
+
+#include <netatalk/at.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+
+#include "ifconfig.h"
+
+static struct netrange at_nr; /* AppleTalk net range */
+static struct ifaliasreq at_addreq;
+
+/* XXX FIXME -- should use strtoul for better parsing. */
+static void
+setatrange(const char *range, int dummy __unused, int s,
+ const struct afswtch *afp)
+{
+ u_int first = 123, last = 123;
+
+ if (sscanf(range, "%u-%u", &first, &last) != 2
+ || first == 0 || first > 0xffff
+ || last == 0 || last > 0xffff || first > last)
+ errx(1, "%s: illegal net range: %u-%u", range, first, last);
+ at_nr.nr_firstnet = htons(first);
+ at_nr.nr_lastnet = htons(last);
+}
+
+static void
+setatphase(const char *phase, int dummy __unused, int s,
+ const struct afswtch *afp)
+{
+ if (!strcmp(phase, "1"))
+ at_nr.nr_phase = 1;
+ else if (!strcmp(phase, "2"))
+ at_nr.nr_phase = 2;
+ else
+ errx(1, "%s: illegal phase", phase);
+}
+
+static void
+at_status(int s __unused, const struct rt_addrinfo * info)
+{
+ struct sockaddr_at *sat, null_sat;
+ struct netrange *nr;
+
+ memset(&null_sat, 0, sizeof(null_sat));
+
+ sat = (struct sockaddr_at *)info->rti_info[RTAX_IFA];
+ if (sat == NULL)
+ return;
+ nr = &sat->sat_range.r_netrange;
+ printf("\tatalk %d.%d range %d-%d phase %d",
+ ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node,
+ ntohs(nr->nr_firstnet), ntohs(nr->nr_lastnet), nr->nr_phase);
+ if (flags & IFF_POINTOPOINT) {
+ /* note RTAX_BRD overlap with IFF_BROADCAST */
+ sat = (struct sockaddr_at *)info->rti_info[RTAX_BRD];
+ if (!sat)
+ sat = &null_sat;
+ printf("--> %d.%d",
+ ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node);
+ }
+ if (flags & IFF_BROADCAST) {
+ /* note RTAX_BRD overlap with IFF_POINTOPOINT */
+ sat = (struct sockaddr_at *)info->rti_info[RTAX_BRD];
+ if (sat)
+ printf(" broadcast %d.%d",
+ ntohs(sat->sat_addr.s_net),
+ sat->sat_addr.s_node);
+ }
+
+ putchar('\n');
+}
+
+static void
+at_getaddr(const char *addr, int which)
+{
+ struct sockaddr_at *sat = (struct sockaddr_at *) &at_addreq.ifra_addr;
+ u_int net, node;
+
+ sat->sat_family = AF_APPLETALK;
+ sat->sat_len = sizeof(*sat);
+ if (which == MASK)
+ errx(1, "AppleTalk does not use netmasks");
+ if (sscanf(addr, "%u.%u", &net, &node) != 2
+ || net > 0xffff || node > 0xfe)
+ errx(1, "%s: illegal address", addr);
+ sat->sat_addr.s_net = htons(net);
+ sat->sat_addr.s_node = node;
+}
+
+static void
+at_postproc(int s, const struct afswtch *afp)
+{
+ struct sockaddr_at *sat = (struct sockaddr_at *) &at_addreq.ifra_addr;
+
+ if (at_nr.nr_phase == 0)
+ at_nr.nr_phase = 2; /* Default phase 2 */
+ if (at_nr.nr_firstnet == 0)
+ at_nr.nr_firstnet = /* Default range of one */
+ at_nr.nr_lastnet = sat->sat_addr.s_net;
+ printf("\tatalk %d.%d range %d-%d phase %d\n",
+ ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node,
+ ntohs(at_nr.nr_firstnet), ntohs(at_nr.nr_lastnet),
+ at_nr.nr_phase);
+ if ((u_short) ntohs(at_nr.nr_firstnet) >
+ (u_short) ntohs(sat->sat_addr.s_net)
+ || (u_short) ntohs(at_nr.nr_lastnet) <
+ (u_short) ntohs(sat->sat_addr.s_net))
+ errx(1, "AppleTalk address is not in range");
+ sat->sat_range.r_netrange = at_nr;
+}
+
+static struct cmd atalk_cmds[] = {
+ DEF_CMD_ARG("range", setatrange),
+ DEF_CMD_ARG("phase", setatphase),
+};
+
+static struct afswtch af_atalk = {
+ .af_name = "atalk",
+ .af_af = AF_APPLETALK,
+ .af_status = at_status,
+ .af_getaddr = at_getaddr,
+ .af_postproc = at_postproc,
+ .af_difaddr = SIOCDIFADDR,
+ .af_aifaddr = SIOCAIFADDR,
+ .af_ridreq = &at_addreq,
+ .af_addreq = &at_addreq,
+};
+
+static __constructor void
+atalk_ctor(void)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ int i;
+
+ for (i = 0; i < N(atalk_cmds); i++)
+ cmd_register(&atalk_cmds[i]);
+ af_register(&af_atalk);
+#undef N
+}
diff --git a/sbin/ifconfig/af_inet.c b/sbin/ifconfig/af_inet.c
new file mode 100644
index 0000000..7678daa
--- /dev/null
+++ b/sbin/ifconfig/af_inet.c
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ * 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 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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h> /* for RTX_IFA */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+#include <net/if_var.h> /* for struct ifaddr */
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "ifconfig.h"
+
+static struct ifaliasreq in_addreq;
+static struct ifreq in_ridreq;
+
+static void
+in_status(int s __unused, const struct rt_addrinfo * info)
+{
+ struct sockaddr_in *sin, null_sin;
+
+ memset(&null_sin, 0, sizeof(null_sin));
+
+ sin = (struct sockaddr_in *)info->rti_info[RTAX_IFA];
+ if (sin == NULL)
+ return;
+
+ printf("\tinet %s ", inet_ntoa(sin->sin_addr));
+
+ if (flags & IFF_POINTOPOINT) {
+ /* note RTAX_BRD overlap with IFF_BROADCAST */
+ sin = (struct sockaddr_in *)info->rti_info[RTAX_BRD];
+ if (!sin)
+ sin = &null_sin;
+ printf("--> %s ", inet_ntoa(sin->sin_addr));
+ }
+
+ sin = (struct sockaddr_in *)info->rti_info[RTAX_NETMASK];
+ if (!sin)
+ sin = &null_sin;
+ printf("netmask 0x%lx ", (unsigned long)ntohl(sin->sin_addr.s_addr));
+
+ if (flags & IFF_BROADCAST) {
+ /* note RTAX_BRD overlap with IFF_POINTOPOINT */
+ sin = (struct sockaddr_in *)info->rti_info[RTAX_BRD];
+ if (sin && sin->sin_addr.s_addr != 0)
+ printf("broadcast %s", inet_ntoa(sin->sin_addr));
+ }
+ putchar('\n');
+}
+
+#define SIN(x) ((struct sockaddr_in *) &(x))
+static struct sockaddr_in *sintab[] = {
+ SIN(in_ridreq.ifr_addr), SIN(in_addreq.ifra_addr),
+ SIN(in_addreq.ifra_mask), SIN(in_addreq.ifra_broadaddr)
+};
+
+static void
+in_getaddr(const char *s, int which)
+{
+#define MIN(a,b) ((a)<(b)?(a):(b))
+ struct sockaddr_in *sin = sintab[which];
+ struct hostent *hp;
+ struct netent *np;
+
+ sin->sin_len = sizeof(*sin);
+ if (which != MASK)
+ sin->sin_family = AF_INET;
+
+ if (which == ADDR) {
+ char *p = NULL;
+
+ if((p = strrchr(s, '/')) != NULL) {
+ /* address is `name/masklen' */
+ int masklen;
+ int ret;
+ struct sockaddr_in *min = sintab[MASK];
+ *p = '\0';
+ ret = sscanf(p+1, "%u", &masklen);
+ if(ret != 1 || (masklen < 0 || masklen > 32)) {
+ *p = '/';
+ errx(1, "%s: bad value", s);
+ }
+ min->sin_len = sizeof(*min);
+ min->sin_addr.s_addr = htonl(~((1LL << (32 - masklen)) - 1) &
+ 0xffffffff);
+ }
+ }
+
+ if (inet_aton(s, &sin->sin_addr))
+ return;
+ if ((hp = gethostbyname(s)) != 0)
+ bcopy(hp->h_addr, (char *)&sin->sin_addr,
+ MIN(hp->h_length, sizeof(sin->sin_addr)));
+ else if ((np = getnetbyname(s)) != 0)
+ sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY);
+ else
+ errx(1, "%s: bad value", s);
+#undef MIN
+}
+
+static void
+in_status_tunnel(int s)
+{
+ char src[NI_MAXHOST];
+ char dst[NI_MAXHOST];
+ struct ifreq ifr;
+ const struct sockaddr *sa = (const struct sockaddr *) &ifr.ifr_addr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, name, IFNAMSIZ);
+
+ if (ioctl(s, SIOCGIFPSRCADDR, (caddr_t)&ifr) < 0)
+ return;
+ if (sa->sa_family != AF_INET)
+ return;
+ if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0)
+ src[0] = '\0';
+
+ if (ioctl(s, SIOCGIFPDSTADDR, (caddr_t)&ifr) < 0)
+ return;
+ if (sa->sa_family != AF_INET)
+ return;
+ if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0)
+ dst[0] = '\0';
+
+ printf("\ttunnel inet %s --> %s\n", src, dst);
+}
+
+static void
+in_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres)
+{
+ struct ifaliasreq addreq;
+
+ memset(&addreq, 0, sizeof(addreq));
+ strncpy(addreq.ifra_name, name, IFNAMSIZ);
+ memcpy(&addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len);
+ memcpy(&addreq.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len);
+
+ if (ioctl(s, SIOCSIFPHYADDR, &addreq) < 0)
+ warn("SIOCSIFPHYADDR");
+}
+
+static struct afswtch af_inet = {
+ .af_name = "inet",
+ .af_af = AF_INET,
+ .af_status = in_status,
+ .af_getaddr = in_getaddr,
+ .af_status_tunnel = in_status_tunnel,
+ .af_settunnel = in_set_tunnel,
+ .af_difaddr = SIOCDIFADDR,
+ .af_aifaddr = SIOCAIFADDR,
+ .af_ridreq = &in_ridreq,
+ .af_addreq = &in_addreq,
+};
+
+static __constructor void
+inet_ctor(void)
+{
+ af_register(&af_inet);
+}
diff --git a/sbin/ifconfig/af_inet6.c b/sbin/ifconfig/af_inet6.c
new file mode 100644
index 0000000..2aae382
--- /dev/null
+++ b/sbin/ifconfig/af_inet6.c
@@ -0,0 +1,540 @@
+/*
+ * 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.
+ * 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 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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h> /* for RTX_IFA */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+
+#include <arpa/inet.h>
+
+#include <netinet/in.h>
+#include <net/if_var.h> /* for struct ifaddr */
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <netinet6/nd6.h> /* Define ND6_INFINITE_LIFETIME */
+
+#include "ifconfig.h"
+
+static struct in6_ifreq in6_ridreq;
+static struct in6_aliasreq in6_addreq =
+ { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ 0,
+ { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME } };
+static int ip6lifetime;
+
+static void in6_fillscopeid(struct sockaddr_in6 *sin6);
+static int prefix(void *, int);
+static char *sec2str(time_t);
+static int explicit_prefix = 0;
+
+static char addr_buf[MAXHOSTNAMELEN *2 + 1]; /*for getnameinfo()*/
+
+static void
+setifprefixlen(const char *addr, int dummy __unused, int s,
+ const struct afswtch *afp)
+{
+ if (afp->af_getprefix != NULL)
+ afp->af_getprefix(addr, MASK);
+ explicit_prefix = 1;
+}
+
+static void
+setip6flags(const char *dummyaddr __unused, int flag, int dummysoc __unused,
+ const struct afswtch *afp)
+{
+ if (afp->af_af != AF_INET6)
+ err(1, "address flags can be set only for inet6 addresses");
+
+ if (flag < 0)
+ in6_addreq.ifra_flags &= ~(-flag);
+ else
+ in6_addreq.ifra_flags |= flag;
+}
+
+static void
+setip6lifetime(const char *cmd, const char *val, int s,
+ const struct afswtch *afp)
+{
+ time_t newval, t;
+ char *ep;
+
+ t = time(NULL);
+ newval = (time_t)strtoul(val, &ep, 0);
+ if (val == ep)
+ errx(1, "invalid %s", cmd);
+ if (afp->af_af != AF_INET6)
+ errx(1, "%s not allowed for the AF", cmd);
+ if (strcmp(cmd, "vltime") == 0) {
+ in6_addreq.ifra_lifetime.ia6t_expire = t + newval;
+ in6_addreq.ifra_lifetime.ia6t_vltime = newval;
+ } else if (strcmp(cmd, "pltime") == 0) {
+ in6_addreq.ifra_lifetime.ia6t_preferred = t + newval;
+ in6_addreq.ifra_lifetime.ia6t_pltime = newval;
+ }
+}
+
+static void
+setip6pltime(const char *seconds, int dummy __unused, int s,
+ const struct afswtch *afp)
+{
+ setip6lifetime("pltime", seconds, s, afp);
+}
+
+static void
+setip6vltime(const char *seconds, int dummy __unused, int s,
+ const struct afswtch *afp)
+{
+ setip6lifetime("vltime", seconds, s, afp);
+}
+
+static void
+setip6eui64(const char *cmd, int dummy __unused, int s,
+ const struct afswtch *afp)
+{
+ struct ifaddrs *ifap, *ifa;
+ const struct sockaddr_in6 *sin6 = NULL;
+ const struct in6_addr *lladdr = NULL;
+ struct in6_addr *in6;
+
+ if (afp->af_af != AF_INET6)
+ errx(EXIT_FAILURE, "%s not allowed for the AF", cmd);
+ in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr;
+ if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0)
+ errx(EXIT_FAILURE, "interface index is already filled");
+ if (getifaddrs(&ifap) != 0)
+ err(EXIT_FAILURE, "getifaddrs");
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family == AF_INET6 &&
+ strcmp(ifa->ifa_name, name) == 0) {
+ sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr;
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+ lladdr = &sin6->sin6_addr;
+ break;
+ }
+ }
+ }
+ if (!lladdr)
+ errx(EXIT_FAILURE, "could not determine link local address");
+
+ memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8);
+
+ freeifaddrs(ifap);
+}
+
+static void
+in6_fillscopeid(struct sockaddr_in6 *sin6)
+{
+#if defined(__KAME__) && defined(KAME_SCOPEID)
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+ sin6->sin6_scope_id =
+ ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
+ sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0;
+ }
+#endif
+}
+
+static void
+in6_status(int s __unused, const struct rt_addrinfo * info)
+{
+ struct sockaddr_in6 *sin, null_sin;
+ struct in6_ifreq ifr6;
+ int s6;
+ u_int32_t flags6;
+ struct in6_addrlifetime lifetime;
+ time_t t = time(NULL);
+ int error;
+ u_int32_t scopeid;
+
+ memset(&null_sin, 0, sizeof(null_sin));
+
+ sin = (struct sockaddr_in6 *)info->rti_info[RTAX_IFA];
+ if (sin == NULL)
+ return;
+
+ strncpy(ifr6.ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name));
+ if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ warn("socket(AF_INET6,SOCK_DGRAM)");
+ return;
+ }
+ ifr6.ifr_addr = *sin;
+ if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
+ warn("ioctl(SIOCGIFAFLAG_IN6)");
+ close(s6);
+ return;
+ }
+ flags6 = ifr6.ifr_ifru.ifru_flags6;
+ memset(&lifetime, 0, sizeof(lifetime));
+ ifr6.ifr_addr = *sin;
+ if (ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr6) < 0) {
+ warn("ioctl(SIOCGIFALIFETIME_IN6)");
+ close(s6);
+ return;
+ }
+ lifetime = ifr6.ifr_ifru.ifru_lifetime;
+ close(s6);
+
+ /* XXX: embedded link local addr check */
+ if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) &&
+ *(u_short *)&sin->sin6_addr.s6_addr[2] != 0) {
+ u_short index;
+
+ index = *(u_short *)&sin->sin6_addr.s6_addr[2];
+ *(u_short *)&sin->sin6_addr.s6_addr[2] = 0;
+ if (sin->sin6_scope_id == 0)
+ sin->sin6_scope_id = ntohs(index);
+ }
+ scopeid = sin->sin6_scope_id;
+
+ error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf,
+ sizeof(addr_buf), NULL, 0, NI_NUMERICHOST);
+ if (error != 0)
+ inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf,
+ sizeof(addr_buf));
+ printf("\tinet6 %s ", addr_buf);
+
+ if (flags & IFF_POINTOPOINT) {
+ /* note RTAX_BRD overlap with IFF_BROADCAST */
+ sin = (struct sockaddr_in6 *)info->rti_info[RTAX_BRD];
+ /*
+ * some of the interfaces do not have valid destination
+ * address.
+ */
+ if (sin && sin->sin6_family == AF_INET6) {
+ int error;
+
+ /* XXX: embedded link local addr check */
+ if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) &&
+ *(u_short *)&sin->sin6_addr.s6_addr[2] != 0) {
+ u_short index;
+
+ index = *(u_short *)&sin->sin6_addr.s6_addr[2];
+ *(u_short *)&sin->sin6_addr.s6_addr[2] = 0;
+ if (sin->sin6_scope_id == 0)
+ sin->sin6_scope_id = ntohs(index);
+ }
+
+ error = getnameinfo((struct sockaddr *)sin,
+ sin->sin6_len, addr_buf,
+ sizeof(addr_buf), NULL, 0,
+ NI_NUMERICHOST);
+ if (error != 0)
+ inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf,
+ sizeof(addr_buf));
+ printf("--> %s ", addr_buf);
+ }
+ }
+
+ sin = (struct sockaddr_in6 *)info->rti_info[RTAX_NETMASK];
+ if (!sin)
+ sin = &null_sin;
+ printf("prefixlen %d ", prefix(&sin->sin6_addr,
+ sizeof(struct in6_addr)));
+
+ if ((flags6 & IN6_IFF_ANYCAST) != 0)
+ printf("anycast ");
+ if ((flags6 & IN6_IFF_TENTATIVE) != 0)
+ printf("tentative ");
+ if ((flags6 & IN6_IFF_DUPLICATED) != 0)
+ printf("duplicated ");
+ if ((flags6 & IN6_IFF_DETACHED) != 0)
+ printf("detached ");
+ if ((flags6 & IN6_IFF_DEPRECATED) != 0)
+ printf("deprecated ");
+ if ((flags6 & IN6_IFF_AUTOCONF) != 0)
+ printf("autoconf ");
+ if ((flags6 & IN6_IFF_TEMPORARY) != 0)
+ printf("temporary ");
+
+ if (scopeid)
+ printf("scopeid 0x%x ", scopeid);
+
+ if (ip6lifetime && (lifetime.ia6t_preferred || lifetime.ia6t_expire)) {
+ printf("pltime ");
+ if (lifetime.ia6t_preferred) {
+ printf("%s ", lifetime.ia6t_preferred < t
+ ? "0" : sec2str(lifetime.ia6t_preferred - t));
+ } else
+ printf("infty ");
+
+ printf("vltime ");
+ if (lifetime.ia6t_expire) {
+ printf("%s ", lifetime.ia6t_expire < t
+ ? "0" : sec2str(lifetime.ia6t_expire - t));
+ } else
+ printf("infty ");
+ }
+
+ putchar('\n');
+}
+
+#define SIN6(x) ((struct sockaddr_in6 *) &(x))
+static struct sockaddr_in6 *sin6tab[] = {
+ SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr),
+ SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr)
+};
+
+static void
+in6_getprefix(const char *plen, int which)
+{
+ struct sockaddr_in6 *sin = sin6tab[which];
+ u_char *cp;
+ int len = atoi(plen);
+
+ if ((len < 0) || (len > 128))
+ errx(1, "%s: bad value", plen);
+ sin->sin6_len = sizeof(*sin);
+ if (which != MASK)
+ sin->sin6_family = AF_INET6;
+ if ((len == 0) || (len == 128)) {
+ memset(&sin->sin6_addr, 0xff, sizeof(struct in6_addr));
+ return;
+ }
+ memset((void *)&sin->sin6_addr, 0x00, sizeof(sin->sin6_addr));
+ for (cp = (u_char *)&sin->sin6_addr; len > 7; len -= 8)
+ *cp++ = 0xff;
+ *cp = 0xff << (8 - len);
+}
+
+static void
+in6_getaddr(const char *s, int which)
+{
+ struct sockaddr_in6 *sin = sin6tab[which];
+ struct addrinfo hints, *res;
+ int error = -1;
+
+ newaddr &= 1;
+
+ sin->sin6_len = sizeof(*sin);
+ if (which != MASK)
+ sin->sin6_family = AF_INET6;
+
+ if (which == ADDR) {
+ char *p = NULL;
+ if((p = strrchr(s, '/')) != NULL) {
+ *p = '\0';
+ in6_getprefix(p + 1, MASK);
+ explicit_prefix = 1;
+ }
+ }
+
+ if (sin->sin6_family == AF_INET6) {
+ bzero(&hints, sizeof(struct addrinfo));
+ hints.ai_family = AF_INET6;
+ error = getaddrinfo(s, NULL, &hints, &res);
+ }
+ if (error != 0) {
+ if (inet_pton(AF_INET6, s, &sin->sin6_addr) != 1)
+ errx(1, "%s: bad value", s);
+ } else
+ bcopy(res->ai_addr, sin, res->ai_addrlen);
+}
+
+static int
+prefix(void *val, int size)
+{
+ u_char *name = (u_char *)val;
+ int byte, bit, plen = 0;
+
+ for (byte = 0; byte < size; byte++, plen += 8)
+ if (name[byte] != 0xff)
+ break;
+ if (byte == size)
+ return (plen);
+ for (bit = 7; bit != 0; bit--, plen++)
+ if (!(name[byte] & (1 << bit)))
+ break;
+ for (; bit != 0; bit--)
+ if (name[byte] & (1 << bit))
+ return(0);
+ byte++;
+ for (; byte < size; byte++)
+ if (name[byte])
+ return(0);
+ return (plen);
+}
+
+static char *
+sec2str(time_t total)
+{
+ static char result[256];
+ int days, hours, mins, secs;
+ int first = 1;
+ char *p = result;
+
+ if (0) {
+ days = total / 3600 / 24;
+ hours = (total / 3600) % 24;
+ mins = (total / 60) % 60;
+ secs = total % 60;
+
+ if (days) {
+ first = 0;
+ p += sprintf(p, "%dd", days);
+ }
+ if (!first || hours) {
+ first = 0;
+ p += sprintf(p, "%dh", hours);
+ }
+ if (!first || mins) {
+ first = 0;
+ p += sprintf(p, "%dm", mins);
+ }
+ sprintf(p, "%ds", secs);
+ } else
+ sprintf(result, "%lu", (unsigned long)total);
+
+ return(result);
+}
+
+static void
+in6_postproc(int s, const struct afswtch *afp)
+{
+ if (explicit_prefix == 0) {
+ /* Aggregatable address architecture defines all prefixes
+ are 64. So, it is convenient to set prefixlen to 64 if
+ it is not specified. */
+ setifprefixlen("64", 0, s, afp);
+ /* in6_getprefix("64", MASK) if MASK is available here... */
+ }
+}
+
+static void
+in6_status_tunnel(int s)
+{
+ char src[NI_MAXHOST];
+ char dst[NI_MAXHOST];
+ struct in6_ifreq in6_ifr;
+ const struct sockaddr *sa = (const struct sockaddr *) &in6_ifr.ifr_addr;
+
+ memset(&in6_ifr, 0, sizeof(in6_ifr));
+ strncpy(in6_ifr.ifr_name, name, IFNAMSIZ);
+
+ if (ioctl(s, SIOCGIFPSRCADDR_IN6, (caddr_t)&in6_ifr) < 0)
+ return;
+ if (sa->sa_family != AF_INET6)
+ return;
+ in6_fillscopeid(&in6_ifr.ifr_addr);
+ if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0,
+ NI_NUMERICHOST) != 0)
+ src[0] = '\0';
+
+ if (ioctl(s, SIOCGIFPDSTADDR_IN6, (caddr_t)&in6_ifr) < 0)
+ return;
+ if (sa->sa_family != AF_INET6)
+ return;
+ in6_fillscopeid(&in6_ifr.ifr_addr);
+ if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0,
+ NI_NUMERICHOST) != 0)
+ dst[0] = '\0';
+
+ printf("\ttunnel inet6 %s --> %s\n", src, dst);
+}
+
+static void
+in6_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres)
+{
+ struct in6_aliasreq in6_addreq;
+
+ memset(&in6_addreq, 0, sizeof(in6_addreq));
+ strncpy(in6_addreq.ifra_name, name, IFNAMSIZ);
+ memcpy(&in6_addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len);
+ memcpy(&in6_addreq.ifra_dstaddr, dstres->ai_addr,
+ dstres->ai_addr->sa_len);
+
+ if (ioctl(s, SIOCSIFPHYADDR_IN6, &in6_addreq) < 0)
+ warn("SIOCSIFPHYADDR_IN6");
+}
+
+static struct cmd inet6_cmds[] = {
+ DEF_CMD_ARG("prefixlen", setifprefixlen),
+ DEF_CMD("anycast", IN6_IFF_ANYCAST, setip6flags),
+ DEF_CMD("tentative", IN6_IFF_TENTATIVE, setip6flags),
+ DEF_CMD("-tentative", -IN6_IFF_TENTATIVE, setip6flags),
+ DEF_CMD("deprecated", IN6_IFF_DEPRECATED, setip6flags),
+ DEF_CMD("-deprecated", -IN6_IFF_DEPRECATED, setip6flags),
+ DEF_CMD("autoconf", IN6_IFF_AUTOCONF, setip6flags),
+ DEF_CMD("-autoconf", -IN6_IFF_AUTOCONF, setip6flags),
+ DEF_CMD_ARG("pltime", setip6pltime),
+ DEF_CMD_ARG("vltime", setip6vltime),
+ DEF_CMD("eui64", 0, setip6eui64),
+};
+
+static struct afswtch af_inet6 = {
+ .af_name = "inet6",
+ .af_af = AF_INET6,
+ .af_status = in6_status,
+ .af_getaddr = in6_getaddr,
+ .af_getprefix = in6_getprefix,
+ .af_postproc = in6_postproc,
+ .af_status_tunnel = in6_status_tunnel,
+ .af_settunnel = in6_set_tunnel,
+ .af_difaddr = SIOCDIFADDR_IN6,
+ .af_aifaddr = SIOCAIFADDR_IN6,
+ .af_ridreq = &in6_addreq,
+ .af_addreq = &in6_addreq,
+};
+
+static void
+in6_Lopt_cb(const char *optarg __unused)
+{
+ ip6lifetime++; /* print IPv6 address lifetime */
+}
+static struct option in6_Lopt = { "L", "[-L]", in6_Lopt_cb };
+
+static __constructor void
+inet6_ctor(void)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ int i;
+
+ for (i = 0; i < N(inet6_cmds); i++)
+ cmd_register(&inet6_cmds[i]);
+ af_register(&af_inet6);
+ opt_register(&in6_Lopt);
+#undef N
+}
diff --git a/sbin/ifconfig/af_ipx.c b/sbin/ifconfig/af_ipx.c
new file mode 100644
index 0000000..d16e2eb
--- /dev/null
+++ b/sbin/ifconfig/af_ipx.c
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ * 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 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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <net/if_var.h>
+#define IPXIP
+#define IPTUNNEL
+#include <netipx/ipx.h>
+#include <netipx/ipx_if.h>
+
+#include "ifconfig.h"
+
+static struct ifaliasreq ipx_addreq;
+static struct ifreq ipx_ridreq;
+
+static void
+ipx_status(int s __unused, const struct rt_addrinfo * info)
+{
+ struct sockaddr_ipx *sipx, null_sipx;
+
+ sipx = (struct sockaddr_ipx *)info->rti_info[RTAX_IFA];
+ if (sipx == NULL)
+ return;
+
+ printf("\tipx %s ", ipx_ntoa(sipx->sipx_addr));
+
+ if (flags & IFF_POINTOPOINT) {
+ sipx = (struct sockaddr_ipx *)info->rti_info[RTAX_BRD];
+ if (!sipx) {
+ memset(&null_sipx, 0, sizeof(null_sipx));
+ sipx = &null_sipx;
+ }
+ printf("--> %s ", ipx_ntoa(sipx->sipx_addr));
+ }
+ putchar('\n');
+}
+
+#define SIPX(x) ((struct sockaddr_ipx *) &(x))
+struct sockaddr_ipx *sipxtab[] = {
+ SIPX(ipx_ridreq.ifr_addr), SIPX(ipx_addreq.ifra_addr),
+ SIPX(ipx_addreq.ifra_mask), SIPX(ipx_addreq.ifra_broadaddr)
+};
+
+static void
+ipx_getaddr(const char *addr, int which)
+{
+ struct sockaddr_ipx *sipx = sipxtab[which];
+
+ sipx->sipx_family = AF_IPX;
+ sipx->sipx_len = sizeof(*sipx);
+ sipx->sipx_addr = ipx_addr(addr);
+ if (which == MASK)
+ printf("Attempt to set IPX netmask will be ineffectual\n");
+}
+
+static void
+ipx_postproc(int s, const struct afswtch *afp)
+{
+ if (setipdst) {
+ struct ipxip_req rq;
+ int size = sizeof(rq);
+
+ rq.rq_ipx = ipx_addreq.ifra_addr;
+ rq.rq_ip = ipx_addreq.ifra_dstaddr;
+
+ if (setsockopt(s, 0, SO_IPXIP_ROUTE, &rq, size) < 0)
+ Perror("Encapsulation Routing");
+ }
+}
+
+static struct afswtch af_ipx = {
+ .af_name = "ipx",
+ .af_af = AF_IPX,
+ .af_status = ipx_status,
+ .af_getaddr = ipx_getaddr,
+ .af_postproc = ipx_postproc,
+ .af_difaddr = SIOCDIFADDR,
+ .af_aifaddr = SIOCAIFADDR,
+ .af_ridreq = &ipx_ridreq,
+ .af_addreq = &ipx_addreq,
+};
+
+static __constructor void
+ipx_ctor(void)
+{
+ af_register(&af_ipx);
+}
diff --git a/sbin/ifconfig/af_link.c b/sbin/ifconfig/af_link.c
new file mode 100644
index 0000000..d610750
--- /dev/null
+++ b/sbin/ifconfig/af_link.c
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ * 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 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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h> /* for RTX_IFA */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/ethernet.h>
+
+#include "ifconfig.h"
+
+static struct ifreq link_ridreq;
+
+static void
+link_status(int s __unused, const struct rt_addrinfo *info)
+{
+ const struct sockaddr_dl *sdl =
+ (const struct sockaddr_dl *) info->rti_info[RTAX_IFA];
+
+ if (sdl != NULL && sdl->sdl_alen > 0) {
+ if (sdl->sdl_type == IFT_ETHER &&
+ sdl->sdl_alen == ETHER_ADDR_LEN)
+ printf("\tether %s\n",
+ ether_ntoa((const struct ether_addr *)LLADDR(sdl)));
+ else {
+ int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
+
+ printf("\tlladdr %s\n", link_ntoa(sdl) + n);
+ }
+ }
+}
+
+static void
+link_getaddr(const char *addr, int which)
+{
+ char *temp;
+ struct sockaddr_dl sdl;
+ struct sockaddr *sa = &link_ridreq.ifr_addr;
+
+ if (which != ADDR)
+ errx(1, "can't set link-level netmask or broadcast");
+ if ((temp = malloc(strlen(addr) + 2)) == NULL)
+ errx(1, "malloc failed");
+ temp[0] = ':';
+ strcpy(temp + 1, addr);
+ sdl.sdl_len = sizeof(sdl);
+ link_addr(temp, &sdl);
+ free(temp);
+ if (sdl.sdl_alen > sizeof(sa->sa_data))
+ errx(1, "malformed link-level address");
+ sa->sa_family = AF_LINK;
+ sa->sa_len = sdl.sdl_alen;
+ bcopy(LLADDR(&sdl), sa->sa_data, sdl.sdl_alen);
+}
+
+static struct afswtch af_link = {
+ .af_name = "link",
+ .af_af = AF_LINK,
+ .af_status = link_status,
+ .af_getaddr = link_getaddr,
+ .af_aifaddr = SIOCSIFLLADDR,
+ .af_addreq = &link_ridreq,
+};
+static struct afswtch af_ether = {
+ .af_name = "ether",
+ .af_af = AF_LINK,
+ .af_status = link_status,
+ .af_getaddr = link_getaddr,
+ .af_aifaddr = SIOCSIFLLADDR,
+ .af_addreq = &link_ridreq,
+};
+static struct afswtch af_lladdr = {
+ .af_name = "lladdr",
+ .af_af = AF_LINK,
+ .af_status = link_status,
+ .af_getaddr = link_getaddr,
+ .af_aifaddr = SIOCSIFLLADDR,
+ .af_addreq = &link_ridreq,
+};
+
+static __constructor void
+link_ctor(void)
+{
+ af_register(&af_link);
+ af_register(&af_ether);
+ af_register(&af_lladdr);
+}
diff --git a/sbin/ifconfig/ifbridge.c b/sbin/ifconfig/ifbridge.c
new file mode 100644
index 0000000..c42213f
--- /dev/null
+++ b/sbin/ifconfig/ifbridge.c
@@ -0,0 +1,565 @@
+/*-
+ * Copyright 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_bridgevar.h>
+#include <net/route.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+
+#include "ifconfig.h"
+
+static int
+get_val(const char *cp, u_long *valp)
+{
+ char *endptr;
+ u_long val;
+
+ errno = 0;
+ val = strtoul(cp, &endptr, 0);
+ if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE)
+ return (-1);
+
+ *valp = val;
+ return (0);
+}
+
+static int
+do_cmd(int sock, u_long op, void *arg, size_t argsize, int set)
+{
+ struct ifdrv ifd;
+
+ memset(&ifd, 0, sizeof(ifd));
+
+ strlcpy(ifd.ifd_name, ifr.ifr_name, sizeof(ifd.ifd_name));
+ ifd.ifd_cmd = op;
+ ifd.ifd_len = argsize;
+ ifd.ifd_data = arg;
+
+ return (ioctl(sock, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd));
+}
+
+static void
+do_bridgeflag(int sock, const char *ifs, int flag, int set)
+{
+ struct ifbreq req;
+
+ strlcpy(req.ifbr_ifsname, ifs, sizeof(req.ifbr_ifsname));
+
+ if (do_cmd(sock, BRDGGIFFLGS, &req, sizeof(req), 0) < 0)
+ err(1, "unable to get bridge flags");
+
+ if (set)
+ req.ifbr_ifsflags |= flag;
+ else
+ req.ifbr_ifsflags &= ~flag;
+
+ if (do_cmd(sock, BRDGSIFFLGS, &req, sizeof(req), 1) < 0)
+ err(1, "unable to set bridge flags");
+}
+
+static void
+bridge_interfaces(int s, const char *prefix)
+{
+ static const char *stpstates[] = {
+ "disabled",
+ "listening",
+ "learning",
+ "forwarding",
+ "blocking",
+ };
+ struct ifbifconf bifc;
+ struct ifbreq *req;
+ char *inbuf = NULL, *ninbuf;
+ char *p, *pad;
+ int i, len = 8192;
+
+ pad = strdup(prefix);
+ if (pad == NULL)
+ err(1, "strdup");
+ /* replace the prefix with whitespace */
+ for (p = pad; *p != '\0'; p++) {
+ if(isprint(*p))
+ *p = ' ';
+ }
+
+ for (;;) {
+ ninbuf = realloc(inbuf, len);
+ if (ninbuf == NULL)
+ err(1, "unable to allocate interface buffer");
+ bifc.ifbic_len = len;
+ bifc.ifbic_buf = inbuf = ninbuf;
+ if (do_cmd(s, BRDGGIFS, &bifc, sizeof(bifc), 0) < 0)
+ err(1, "unable to get interface list");
+ if ((bifc.ifbic_len + sizeof(*req)) < len)
+ break;
+ len *= 2;
+ }
+
+ for (i = 0; i < bifc.ifbic_len / sizeof(*req); i++) {
+ req = bifc.ifbic_req + i;
+ printf("%s%s ", prefix, req->ifbr_ifsname);
+ printb("flags", req->ifbr_ifsflags, IFBIFBITS);
+ printf("\n");
+
+ if (req->ifbr_ifsflags & IFBIF_STP) {
+ printf("%s", pad);
+ printf("port %u priority %u",
+ req->ifbr_portno, req->ifbr_priority);
+ printf(" path cost %u", req->ifbr_path_cost);
+ if (req->ifbr_state <
+ sizeof(stpstates) / sizeof(stpstates[0]))
+ printf(" %s", stpstates[req->ifbr_state]);
+ else
+ printf(" <unknown state %d>",
+ req->ifbr_state);
+ printf("\n");
+ }
+ }
+
+ free(inbuf);
+}
+
+static void
+bridge_addresses(int s, const char *prefix)
+{
+ struct ifbaconf ifbac;
+ struct ifbareq *ifba;
+ char *inbuf = NULL, *ninbuf;
+ int i, len = 8192;
+ struct ether_addr ea;
+
+ for (;;) {
+ ninbuf = realloc(inbuf, len);
+ if (ninbuf == NULL)
+ err(1, "unable to allocate address buffer");
+ ifbac.ifbac_len = len;
+ ifbac.ifbac_buf = inbuf = ninbuf;
+ if (do_cmd(s, BRDGRTS, &ifbac, sizeof(ifbac), 0) < 0)
+ err(1, "unable to get address cache");
+ if ((ifbac.ifbac_len + sizeof(*ifba)) < len)
+ break;
+ len *= 2;
+ }
+
+ for (i = 0; i < ifbac.ifbac_len / sizeof(*ifba); i++) {
+ ifba = ifbac.ifbac_req + i;
+ memcpy(ea.octet, ifba->ifba_dst,
+ sizeof(ea.octet));
+ printf("%s%s %s %lu ", prefix, ether_ntoa(&ea),
+ ifba->ifba_ifsname, ifba->ifba_expire);
+ printb("flags", ifba->ifba_flags, IFBAFBITS);
+ printf("\n");
+ }
+
+ free(inbuf);
+}
+
+static void
+bridge_status(int s)
+{
+ struct ifbrparam param;
+ u_int16_t pri;
+ u_int8_t ht, fd, ma;
+
+ if (do_cmd(s, BRDGGPRI, &param, sizeof(param), 0) < 0)
+ return;
+ pri = param.ifbrp_prio;
+
+ if (do_cmd(s, BRDGGHT, &param, sizeof(param), 0) < 0)
+ return;
+ ht = param.ifbrp_hellotime;
+
+ if (do_cmd(s, BRDGGFD, &param, sizeof(param), 0) < 0)
+ return;
+ fd = param.ifbrp_fwddelay;
+
+ if (do_cmd(s, BRDGGMA, &param, sizeof(param), 0) < 0)
+ return;
+ ma = param.ifbrp_maxage;
+
+ printf("\tpriority %u hellotime %u fwddelay %u maxage %u\n",
+ pri, ht, fd, ma);
+
+ bridge_interfaces(s, "\tmember: ");
+
+ return;
+
+}
+
+static void
+setbridge_add(const char *val, int d, int s, const struct afswtch *afp)
+{
+ struct ifbreq req;
+
+ memset(&req, 0, sizeof(req));
+ strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname));
+ if (do_cmd(s, BRDGADD, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGADD %s", val);
+}
+
+static void
+setbridge_delete(const char *val, int d, int s, const struct afswtch *afp)
+{
+ struct ifbreq req;
+
+ memset(&req, 0, sizeof(req));
+ strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname));
+ if (do_cmd(s, BRDGDEL, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGDEL %s", val);
+}
+
+static void
+setbridge_discover(const char *val, int d, int s, const struct afswtch *afp)
+{
+
+ do_bridgeflag(s, val, IFBIF_DISCOVER, 1);
+}
+
+static void
+unsetbridge_discover(const char *val, int d, int s, const struct afswtch *afp)
+{
+
+ do_bridgeflag(s, val, IFBIF_DISCOVER, 0);
+}
+
+static void
+setbridge_learn(const char *val, int d, int s, const struct afswtch *afp)
+{
+
+ do_bridgeflag(s, val, IFBIF_LEARNING, 1);
+}
+
+static void
+unsetbridge_learn(const char *val, int d, int s, const struct afswtch *afp)
+{
+
+ do_bridgeflag(s, val, IFBIF_LEARNING, 0);
+}
+
+static void
+setbridge_span(const char *val, int d, int s, const struct afswtch *afp)
+{
+ struct ifbreq req;
+
+ memset(&req, 0, sizeof(req));
+ strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname));
+ if (do_cmd(s, BRDGADDS, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGADDS %s", val);
+}
+
+static void
+unsetbridge_span(const char *val, int d, int s, const struct afswtch *afp)
+{
+ struct ifbreq req;
+
+ memset(&req, 0, sizeof(req));
+ strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname));
+ if (do_cmd(s, BRDGDELS, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGDELS %s", val);
+}
+
+static void
+setbridge_stp(const char *val, int d, int s, const struct afswtch *afp)
+{
+
+ do_bridgeflag(s, val, IFBIF_STP, 1);
+}
+
+static void
+unsetbridge_stp(const char *val, int d, int s, const struct afswtch *afp)
+{
+
+ do_bridgeflag(s, val, IFBIF_STP, 0);
+}
+
+static void
+setbridge_flush(const char *val, int d, int s, const struct afswtch *afp)
+{
+ struct ifbreq req;
+
+ memset(&req, 0, sizeof(req));
+ req.ifbr_ifsflags = IFBF_FLUSHDYN;
+ if (do_cmd(s, BRDGFLUSH, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGFLUSH");
+}
+
+static void
+setbridge_flushall(const char *val, int d, int s, const struct afswtch *afp)
+{
+ struct ifbreq req;
+
+ memset(&req, 0, sizeof(req));
+ req.ifbr_ifsflags = IFBF_FLUSHALL;
+ if (do_cmd(s, BRDGFLUSH, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGFLUSH");
+}
+
+static void
+setbridge_static(const char *val, const char *mac, int s,
+ const struct afswtch *afp)
+{
+ struct ifbareq req;
+ struct ether_addr *ea;
+
+ memset(&req, 0, sizeof(req));
+ strlcpy(req.ifba_ifsname, val, sizeof(req.ifba_ifsname));
+
+ ea = ether_aton(mac);
+ if (ea == NULL)
+ errx(1, "%s: invalid address: %s", val, mac);
+
+ memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst));
+ req.ifba_flags = IFBAF_STATIC;
+
+ if (do_cmd(s, BRDGSADDR, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGSADDR %s", val);
+}
+
+static void
+setbridge_deladdr(const char *val, int d, int s, const struct afswtch *afp)
+{
+ struct ifbareq req;
+ struct ether_addr *ea;
+
+ memset(&req, 0, sizeof(req));
+
+ ea = ether_aton(val);
+ if (ea == NULL)
+ errx(1, "invalid address: %s", val);
+
+ memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst));
+
+ if (do_cmd(s, BRDGDADDR, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGDADDR %s", val);
+}
+
+static void
+setbridge_addr(const char *val, int d, int s, const struct afswtch *afp)
+{
+
+ bridge_addresses(s, "");
+}
+
+static void
+setbridge_maxaddr(const char *arg, int d, int s, const struct afswtch *afp)
+{
+ struct ifbrparam param;
+ u_long val;
+
+ if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0)
+ errx(1, "invalid value: %s", arg);
+
+ param.ifbrp_csize = val & 0xffffffff;
+
+ if (do_cmd(s, BRDGSCACHE, &param, sizeof(param), 1) < 0)
+ err(1, "BRDGSCACHE %s", arg);
+}
+
+static void
+setbridge_hellotime(const char *arg, int d, int s, const struct afswtch *afp)
+{
+ struct ifbrparam param;
+ u_long val;
+
+ if (get_val(arg, &val) < 0 || (val & ~0xff) != 0)
+ errx(1, "invalid value: %s", arg);
+
+ param.ifbrp_hellotime = val & 0xff;
+
+ if (do_cmd(s, BRDGSHT, &param, sizeof(param), 1) < 0)
+ err(1, "BRDGSHT %s", arg);
+}
+
+static void
+setbridge_fwddelay(const char *arg, int d, int s, const struct afswtch *afp)
+{
+ struct ifbrparam param;
+ u_long val;
+
+ if (get_val(arg, &val) < 0 || (val & ~0xff) != 0)
+ errx(1, "invalid value: %s", arg);
+
+ param.ifbrp_fwddelay = val & 0xff;
+
+ if (do_cmd(s, BRDGSFD, &param, sizeof(param), 1) < 0)
+ err(1, "BRDGSFD %s", arg);
+}
+
+static void
+setbridge_maxage(const char *arg, int d, int s, const struct afswtch *afp)
+{
+ struct ifbrparam param;
+ u_long val;
+
+ if (get_val(arg, &val) < 0 || (val & ~0xff) != 0)
+ errx(1, "invalid value: %s", arg);
+
+ param.ifbrp_maxage = val & 0xff;
+
+ if (do_cmd(s, BRDGSMA, &param, sizeof(param), 1) < 0)
+ err(1, "BRDGSMA %s", arg);
+}
+
+static void
+setbridge_priority(const char *arg, int d, int s, const struct afswtch *afp)
+{
+ struct ifbrparam param;
+ u_long val;
+
+ if (get_val(arg, &val) < 0 || (val & ~0xffff) != 0)
+ errx(1, "invalid value: %s", arg);
+
+ param.ifbrp_prio = val & 0xffff;
+
+ if (do_cmd(s, BRDGSPRI, &param, sizeof(param), 1) < 0)
+ err(1, "BRDGSPRI %s", arg);
+}
+
+static void
+setbridge_ifpriority(const char *ifn, const char *pri, int s,
+ const struct afswtch *afp)
+{
+ struct ifbreq req;
+ u_long val;
+
+ memset(&req, 0, sizeof(req));
+
+ if (get_val(pri, &val) < 0 || (val & ~0xff) != 0)
+ errx(1, "invalid value: %s", pri);
+
+ strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
+ req.ifbr_priority = val & 0xff;
+
+ if (do_cmd(s, BRDGSIFPRIO, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGSIFPRIO %s", pri);
+}
+
+static void
+setbridge_ifpathcost(const char *ifn, const char *cost, int s,
+ const struct afswtch *afp)
+{
+ struct ifbreq req;
+ u_long val;
+
+ memset(&req, 0, sizeof(req));
+
+ if (get_val(cost, &val) < 0 || (val & ~0xff) != 0)
+ errx(1, "invalid value: %s", cost);
+
+ strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
+ req.ifbr_path_cost = val & 0xffff;
+
+ if (do_cmd(s, BRDGSIFCOST, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGSIFCOST %s", cost);
+}
+
+static void
+setbridge_timeout(const char *arg, int d, int s, const struct afswtch *afp)
+{
+ struct ifbrparam param;
+ u_long val;
+
+ if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0)
+ errx(1, "invalid value: %s", arg);
+
+ param.ifbrp_ctime = val & 0xffffffff;
+
+ if (do_cmd(s, BRDGSTO, &param, sizeof(param), 1) < 0)
+ err(1, "BRDGSTO %s", arg);
+}
+
+static struct cmd bridge_cmds[] = {
+ DEF_CMD_ARG("addm", setbridge_add),
+ DEF_CMD_ARG("deletem", setbridge_delete),
+ DEF_CMD_ARG("discover", setbridge_discover),
+ DEF_CMD_ARG("-discover", unsetbridge_discover),
+ DEF_CMD_ARG("learn", setbridge_learn),
+ DEF_CMD_ARG("-learn", unsetbridge_learn),
+ DEF_CMD_ARG("span", setbridge_span),
+ DEF_CMD_ARG("-span", unsetbridge_span),
+ DEF_CMD_ARG("stp", setbridge_stp),
+ DEF_CMD_ARG("-stp", unsetbridge_stp),
+ DEF_CMD("flush", 0, setbridge_flush),
+ DEF_CMD("flushall", 0, setbridge_flushall),
+ DEF_CMD_ARG2("static", setbridge_static),
+ DEF_CMD_ARG("deladdr", setbridge_deladdr),
+ DEF_CMD("addr", 1, setbridge_addr),
+ DEF_CMD_ARG("maxaddr", setbridge_maxaddr),
+ DEF_CMD_ARG("hellotime", setbridge_hellotime),
+ DEF_CMD_ARG("fwddelay", setbridge_fwddelay),
+ DEF_CMD_ARG("maxage", setbridge_maxage),
+ DEF_CMD_ARG("priority", setbridge_priority),
+ DEF_CMD_ARG2("ifpriority", setbridge_ifpriority),
+ DEF_CMD_ARG2("ifpathcost", setbridge_ifpathcost),
+ DEF_CMD_ARG("timeout", setbridge_timeout),
+};
+static struct afswtch af_bridge = {
+ .af_name = "af_bridge",
+ .af_af = AF_UNSPEC,
+ .af_other_status = bridge_status,
+};
+
+static __constructor void
+bridge_ctor(void)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ int i;
+
+ for (i = 0; i < N(bridge_cmds); i++)
+ cmd_register(&bridge_cmds[i]);
+ af_register(&af_bridge);
+#undef N
+}
diff --git a/sbin/ifconfig/ifcarp.c b/sbin/ifconfig/ifcarp.c
new file mode 100644
index 0000000..36f1bb6
--- /dev/null
+++ b/sbin/ifconfig/ifcarp.c
@@ -0,0 +1,199 @@
+/* $FreeBSD$ */
+/* from $OpenBSD: ifconfig.c,v 1.82 2003/10/19 05:43:35 mcbride Exp $ */
+
+/*
+ * Copyright (c) 2002 Michael Shalayeff. All rights reserved.
+ * Copyright (c) 2003 Ryan McBride. 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 OR HIS RELATIVES 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 MIND, 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/sockio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netinet/ip_carp.h>
+#include <net/route.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+
+#include "ifconfig.h"
+
+static const char *carp_states[] = { CARP_STATES };
+
+void carp_status(int s);
+void setcarp_advbase(const char *,int, int, const struct afswtch *rafp);
+void setcarp_advskew(const char *, int, int, const struct afswtch *rafp);
+void setcarp_passwd(const char *, int, int, const struct afswtch *rafp);
+void setcarp_vhid(const char *, int, int, const struct afswtch *rafp);
+
+void
+carp_status(int s)
+{
+ const char *state;
+ struct carpreq carpr;
+
+ memset((char *)&carpr, 0, sizeof(struct carpreq));
+ ifr.ifr_data = (caddr_t)&carpr;
+
+ if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1)
+ return;
+
+ if (carpr.carpr_vhid > 0) {
+ if (carpr.carpr_state > CARP_MAXSTATE)
+ state = "<UNKNOWN>";
+ else
+ state = carp_states[carpr.carpr_state];
+
+ printf("\tcarp: %s vhid %d advbase %d advskew %d\n",
+ state, carpr.carpr_vhid, carpr.carpr_advbase,
+ carpr.carpr_advskew);
+ }
+
+ return;
+
+}
+
+void
+setcarp_passwd(const char *val, int d, int s, const struct afswtch *afp)
+{
+ struct carpreq carpr;
+
+ memset((char *)&carpr, 0, sizeof(struct carpreq));
+ ifr.ifr_data = (caddr_t)&carpr;
+
+ if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1)
+ err(1, "SIOCGVH");
+
+ /* XXX Should hash the password into the key here, perhaps? */
+ strlcpy(carpr.carpr_key, val, CARP_KEY_LEN);
+
+ if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSVH");
+
+ return;
+}
+
+void
+setcarp_vhid(const char *val, int d, int s, const struct afswtch *afp)
+{
+ int vhid;
+ struct carpreq carpr;
+
+ vhid = atoi(val);
+
+ if (vhid <= 0)
+ errx(1, "vhid must be greater than 0");
+
+ memset((char *)&carpr, 0, sizeof(struct carpreq));
+ ifr.ifr_data = (caddr_t)&carpr;
+
+ if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1)
+ err(1, "SIOCGVH");
+
+ carpr.carpr_vhid = vhid;
+
+ if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSVH");
+
+ return;
+}
+
+void
+setcarp_advskew(const char *val, int d, int s, const struct afswtch *afp)
+{
+ int advskew;
+ struct carpreq carpr;
+
+ advskew = atoi(val);
+
+ memset((char *)&carpr, 0, sizeof(struct carpreq));
+ ifr.ifr_data = (caddr_t)&carpr;
+
+ if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1)
+ err(1, "SIOCGVH");
+
+ carpr.carpr_advskew = advskew;
+
+ if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSVH");
+
+ return;
+}
+
+void
+setcarp_advbase(const char *val, int d, int s, const struct afswtch *afp)
+{
+ int advbase;
+ struct carpreq carpr;
+
+ advbase = atoi(val);
+
+ memset((char *)&carpr, 0, sizeof(struct carpreq));
+ ifr.ifr_data = (caddr_t)&carpr;
+
+ if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1)
+ err(1, "SIOCGVH");
+
+ carpr.carpr_advbase = advbase;
+
+ if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSVH");
+
+ return;
+}
+
+static struct cmd carp_cmds[] = {
+ DEF_CMD_ARG("advbase", setcarp_advbase),
+ DEF_CMD_ARG("advskew", setcarp_advskew),
+ DEF_CMD_ARG("pass", setcarp_passwd),
+ DEF_CMD_ARG("vhid", setcarp_vhid),
+};
+static struct afswtch af_carp = {
+ .af_name = "af_carp",
+ .af_af = AF_UNSPEC,
+ .af_other_status = carp_status,
+};
+
+static __constructor void
+carp_ctor(void)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ int i;
+
+ for (i = 0; i < N(carp_cmds); i++)
+ cmd_register(&carp_cmds[i]);
+ af_register(&af_carp);
+#undef N
+}
diff --git a/sbin/ifconfig/ifclone.c b/sbin/ifconfig/ifclone.c
new file mode 100644
index 0000000..8b613ad
--- /dev/null
+++ b/sbin/ifconfig/ifclone.c
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ * 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 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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ifconfig.h"
+
+static void
+list_cloners(void)
+{
+ struct if_clonereq ifcr;
+ char *cp, *buf;
+ int idx;
+ int s;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s == -1)
+ err(1, "socket(AF_INET,SOCK_DGRAM)");
+
+ memset(&ifcr, 0, sizeof(ifcr));
+
+ if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0)
+ err(1, "SIOCIFGCLONERS for count");
+
+ buf = malloc(ifcr.ifcr_total * IFNAMSIZ);
+ if (buf == NULL)
+ err(1, "unable to allocate cloner name buffer");
+
+ ifcr.ifcr_count = ifcr.ifcr_total;
+ ifcr.ifcr_buffer = buf;
+
+ if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0)
+ err(1, "SIOCIFGCLONERS for names");
+
+ /*
+ * In case some disappeared in the mean time, clamp it down.
+ */
+ if (ifcr.ifcr_count > ifcr.ifcr_total)
+ ifcr.ifcr_count = ifcr.ifcr_total;
+
+ for (cp = buf, idx = 0; idx < ifcr.ifcr_count; idx++, cp += IFNAMSIZ) {
+ if (idx > 0)
+ putchar(' ');
+ printf("%s", cp);
+ }
+
+ putchar('\n');
+ free(buf);
+}
+
+void
+clone_create(void)
+{
+ int s;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s == -1)
+ err(1, "socket(AF_INET,SOCK_DGRAM)");
+
+ memset(&ifr, 0, sizeof(ifr));
+ (void) strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCIFCREATE, &ifr) < 0)
+ err(1, "SIOCIFCREATE");
+
+ /*
+ * If we get a different name back then we put in, we probably
+ * want to print it out, but we might change our mind later so
+ * we just signal our intrest and leave the printout for later.
+ */
+ if (strcmp(name, ifr.ifr_name) != 0) {
+ printname = 1;
+ strlcpy(name, ifr.ifr_name, sizeof(name));
+ }
+
+ close(s);
+}
+
+static void
+clone_destroy(const char *val, int d, int s, const struct afswtch *rafp)
+{
+
+ (void) strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCIFDESTROY, &ifr) < 0)
+ err(1, "SIOCIFDESTROY");
+ /*
+ * If we create and destroy an interface in the same command,
+ * there isn't any reason to print it's name.
+ */
+ printname = 0;
+}
+
+static struct cmd clone_cmds[] = {
+ DEF_CMD("destroy", 0, clone_destroy),
+ DEF_CMD("unplumb", 0, clone_destroy),
+};
+
+static void
+clone_Copt_cb(const char *optarg __unused)
+{
+ list_cloners();
+ exit(0);
+}
+static struct option clone_Copt = { "C", "[-C]", clone_Copt_cb };
+
+static __constructor void
+clone_ctor(void)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ int i;
+
+ for (i = 0; i < N(clone_cmds); i++)
+ cmd_register(&clone_cmds[i]);
+ opt_register(&clone_Copt);
+#undef N
+}
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
new file mode 100644
index 0000000..7c086fc
--- /dev/null
+++ b/sbin/ifconfig/ifconfig.8
@@ -0,0 +1,1393 @@
+.\" 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.
+.\" 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 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: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94
+.\" $FreeBSD$
+.\"
+.Dd December 26, 2005
+.Dt IFCONFIG 8
+.Os
+.Sh NAME
+.Nm ifconfig
+.Nd configure network interface parameters
+.Sh SYNOPSIS
+.Nm
+.Op Fl L
+.Op Fl k
+.Op Fl m
+.Ar interface
+.Op Cm create
+.Op Ar address_family
+.Oo
+.Ar address
+.Op Ar dest_address
+.Oc
+.Op Ar parameters
+.Nm
+.Ar interface
+.Cm destroy
+.Nm
+.Fl a
+.Op Fl L
+.Op Fl d
+.Op Fl m
+.Op Fl u
+.Op Fl v
+.Op Ar address_family
+.Nm
+.Fl l
+.Op Fl d
+.Op Fl u
+.Op Ar address_family
+.Nm
+.Op Fl L
+.Op Fl d
+.Op Fl k
+.Op Fl m
+.Op Fl u
+.Op Fl v
+.Op Fl C
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to assign an address
+to a network interface and/or configure
+network interface parameters.
+The
+.Nm
+utility must be used at boot time to define the network address
+of each interface present on a machine; it may also be used at
+a later time to redefine an interface's address
+or other operating parameters.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Ar address
+For the
+.Tn DARPA Ns -Internet
+family,
+the address is either a host name present in the host name data
+base,
+.Xr hosts 5 ,
+or a
+.Tn DARPA
+Internet address expressed in the Internet standard
+.Dq dot notation .
+.Pp
+It is also possible to use the CIDR notation (also known as the
+slash notation) to include the netmask.
+That is, one can specify an address like
+.Li 192.168.0.1/16 .
+.Pp
+For
+.Dq inet6
+family, it is also possible to specify the prefix length using the slash
+notation, like
+.Li ::1/128 .
+See the
+.Cm prefixlen
+parameter below for more information.
+.\" For the Xerox Network Systems(tm) family,
+.\" addresses are
+.\" .Ar net:a.b.c.d.e.f ,
+.\" where
+.\" .Ar net
+.\" is the assigned network number (in decimal),
+.\" and each of the six bytes of the host number,
+.\" .Ar a
+.\" through
+.\" .Ar f ,
+.\" are specified in hexadecimal.
+.\" The host number may be omitted on IEEE 802 protocol
+.\" (Ethernet, FDDI, and Token Ring) interfaces,
+.\" which use the hardware physical address,
+.\" and on interfaces other than the first.
+.\" For the
+.\" .Tn ISO
+.\" family, addresses are specified as a long hexadecimal string,
+.\" as in the Xerox family.
+.\" However, two consecutive dots imply a zero
+.\" byte, and the dots are optional, if the user wishes to (carefully)
+.\" count out long strings of digits in network byte order.
+.Pp
+The link-level
+.Pq Dq link
+address
+is specified as a series of colon-separated hex digits.
+This can be used to
+e.g.\& set a new MAC address on an ethernet interface, though the
+mechanism used is not ethernet-specific.
+If the interface is already
+up when this option is used, it will be briefly brought down and
+then brought back up again in order to ensure that the receive
+filter in the underlying ethernet hardware is properly reprogrammed.
+.It Ar address_family
+Specify the
+address family
+which affects interpretation of the remaining parameters.
+Since an interface can receive transmissions in differing protocols
+with different naming schemes, specifying the address family is recommended.
+The address or protocol families currently
+supported are
+.Dq inet ,
+.Dq inet6 ,
+.Dq atalk ,
+.Dq ipx ,
+.\" .Dq iso ,
+and
+.Dq link .
+.\" and
+.\" .Dq ns .
+The default is
+.Dq inet .
+.Dq ether
+and
+.Dq lladdr
+are synonyms for
+.Dq link .
+.It Ar dest_address
+Specify the address of the correspondent on the other end
+of a point to point link.
+.It Ar interface
+This
+parameter is a string of the form
+.Dq name unit ,
+for example,
+.Dq Li ed0 .
+.El
+.Pp
+The following parameters may be set with
+.Nm :
+.Bl -tag -width indent
+.It Cm add
+Another name for the
+.Cm alias
+parameter.
+Introduced for compatibility
+with
+.Bsx .
+.It Cm alias
+Establish an additional network address for this interface.
+This is sometimes useful when changing network numbers, and
+one wishes to accept packets addressed to the old interface.
+If the address is on the same subnet as the first network address
+for this interface, a non-conflicting netmask must be given.
+Usually
+.Li 0xffffffff
+is most appropriate.
+.It Fl alias
+Remove the network address specified.
+This would be used if you incorrectly specified an alias, or it
+was no longer needed.
+If you have incorrectly set an NS address having the side effect
+of specifying the host portion, removing all NS addresses will
+allow you to respecify the host portion.
+.It Cm anycast
+(Inet6 only.)
+Specify that the address configured is an anycast address.
+Based on the current specification,
+only routers may configure anycast addresses.
+Anycast address will not be used as source address of any of outgoing
+IPv6 packets.
+.It Cm arp
+Enable the use of the Address Resolution Protocol
+.Pq Xr arp 4
+in mapping
+between network level addresses and link level addresses (default).
+This is currently implemented for mapping between
+.Tn DARPA
+Internet
+addresses and
+.Tn IEEE
+802 48-bit MAC addresses (Ethernet, FDDI, and Token Ring addresses).
+.It Fl arp
+Disable the use of the Address Resolution Protocol
+.Pq Xr arp 4 .
+.It Cm staticarp
+If the Address Resolution Protocol is enabled,
+the host will only reply to requests for its addresses,
+and will never send any requests.
+.It Fl staticarp
+If the Address Resolution Protocol is enabled,
+the host will perform normally,
+sending out requests and listening for replies.
+.It Cm broadcast
+(Inet only.)
+Specify the address to use to represent broadcasts to the
+network.
+The default broadcast address is the address with a host part of all 1's.
+.It Cm debug
+Enable driver dependent debugging code; usually, this turns on
+extra console error logging.
+.It Fl debug
+Disable driver dependent debugging code.
+.It Cm promisc
+Put interface into permanently promiscuous mode.
+.It Fl promisc
+Disable permanently promiscuous mode.
+.It Cm delete
+Another name for the
+.Fl alias
+parameter.
+.It Cm down
+Mark an interface
+.Dq down .
+When an interface is marked
+.Dq down ,
+the system will not attempt to
+transmit messages through that interface.
+If possible, the interface will be reset to disable reception as well.
+This action does not automatically disable routes using the interface.
+.It Cm eui64
+(Inet6 only.)
+Fill interface index
+(lowermost 64bit of an IPv6 address)
+automatically.
+.It Cm ipdst
+This is used to specify an Internet host who is willing to receive
+IP packets encapsulating IPX packets bound for a remote network.
+An apparent point to point link is constructed, and
+the address specified will be taken as the IPX address and network
+of the destination.
+.It Cm maclabel Ar label
+If Mandatory Access Control support is enabled in the kernel,
+set the MAC label to
+.Ar label .
+.\" (see
+.\" .Xr maclabel 7 ) .
+.It Cm media Ar type
+If the driver supports the media selection system, set the media type
+of the interface to
+.Ar type .
+Some interfaces support the mutually exclusive use of one of several
+different physical media connectors.
+For example, a 10Mbit/s Ethernet
+interface might support the use of either
+.Tn AUI
+or twisted pair connectors.
+Setting the media type to
+.Cm 10base5/AUI
+would change the currently active connector to the AUI port.
+Setting it to
+.Cm 10baseT/UTP
+would activate twisted pair.
+Refer to the interfaces' driver
+specific documentation or man page for a complete list of the
+available types.
+.It Cm mediaopt Ar opts
+If the driver supports the media selection system, set the specified
+media options on the interface.
+The
+.Ar opts
+argument
+is a comma delimited list of options to apply to the interface.
+Refer to the interfaces' driver specific man page for a complete
+list of available options.
+.It Fl mediaopt Ar opts
+If the driver supports the media selection system, disable the
+specified media options on the interface.
+.It Cm mode Ar mode
+If the driver supports the media selection system, set the specified
+operating mode on the interface to
+.Ar mode .
+For IEEE 802.11 wireless interfaces that support multiple operating modes
+this directive is used to select between 802.11a
+.Pq Cm 11a ,
+802.11b
+.Pq Cm 11b ,
+and 802.11g
+.Pq Cm 11g
+operating modes.
+.It Cm name Ar name
+Set the interface name to
+.Ar name .
+.It Cm rxcsum , txcsum
+If the driver supports user-configurable checksum offloading,
+enable receive (or transmit) checksum offloading on the interface.
+Some drivers may not be able to enable these flags independently
+of each other, so setting one may also set the other.
+The driver will offload as much checksum work as it can reliably
+support, the exact level of offloading varies between drivers.
+.It Fl rxcsum , txcsum
+If the driver supports user-configurable checksum offloading,
+disable receive (or transmit) checksum offloading on the interface.
+These settings may not always be independent of each other.
+.It Cm vlanmtu , vlanhwtag
+If the driver offers user-configurable VLAN support, enable
+reception of extended frames or tag processing in hardware,
+respectively.
+Note that this must be issued on a physical interface associated with
+.Xr vlan 4 ,
+not on a
+.Xr vlan 4
+interface itself.
+.It Fl vlanmtu , vlanhwtag
+If the driver offers user-configurable VLAN support, disable
+reception of extended frames or tag processing in hardware,
+respectively.
+.It Cm polling
+Turn on
+.Xr polling 4
+feature and disable interrupts on the interface, if driver supports
+this mode.
+.It Fl polling
+Turn off
+.Xr polling 4
+feature and enable interrupt mode on the interface.
+.It Cm create
+Create the specified network pseudo-device.
+If the interface is given without a unit number, try to create a new
+device with an arbitrary unit number.
+If creation of an arbitrary device is successful, the new device name is
+printed to standard output unless the interface is renamed or destroyed
+in the same
+.Nm
+invocation.
+.It Cm destroy
+Destroy the specified network pseudo-device.
+.It Cm plumb
+Another name for the
+.Cm create
+parameter.
+Included for
+.Tn Solaris
+compatibility.
+.It Cm unplumb
+Another name for the
+.Cm destroy
+parameter.
+Included for
+.Tn Solaris
+compatibility.
+.It Cm metric Ar n
+Set the routing metric of the interface to
+.Ar n ,
+default 0.
+The routing metric is used by the routing protocol
+.Pq Xr routed 8 .
+Higher metrics have the effect of making a route
+less favorable; metrics are counted as addition hops
+to the destination network or host.
+.It Cm mtu Ar n
+Set the maximum transmission unit of the interface to
+.Ar n ,
+default is interface specific.
+The MTU is used to limit the size of packets that are transmitted on an
+interface.
+Not all interfaces support setting the MTU, and some interfaces have
+range restrictions.
+.It Cm netmask Ar mask
+.\" (Inet and ISO.)
+(Inet only.)
+Specify how much of the address to reserve for subdividing
+networks into sub-networks.
+The mask includes the network part of the local address
+and the subnet part, which is taken from the host field of the address.
+The mask can be specified as a single hexadecimal number
+with a leading
+.Ql 0x ,
+with a dot-notation Internet address,
+or with a pseudo-network name listed in the network table
+.Xr networks 5 .
+The mask contains 1's for the bit positions in the 32-bit address
+which are to be used for the network and subnet parts,
+and 0's for the host part.
+The mask should contain at least the standard network portion,
+and the subnet field should be contiguous with the network
+portion.
+.Pp
+The netmask can also be specified in CIDR notation after the address.
+See the
+.Ar address
+option above for more information.
+.It Cm prefixlen Ar len
+(Inet6 only.)
+Specify that
+.Ar len
+bits are reserved for subdividing networks into sub-networks.
+The
+.Ar len
+must be integer, and for syntactical reason it must be between 0 to 128.
+It is almost always 64 under the current IPv6 assignment rule.
+If the parameter is omitted, 64 is used.
+.Pp
+The prefix can also be specified using the slash notation after the address.
+See the
+.Ar address
+option above for more information.
+.\" see
+.\" Xr eon 5 .
+.\" .It Cm nsellength Ar n
+.\" .Pf ( Tn ISO
+.\" only)
+.\" This specifies a trailing number of bytes for a received
+.\" .Tn NSAP
+.\" used for local identification, the remaining leading part of which is
+.\" taken to be the
+.\" .Tn NET
+.\" (Network Entity Title).
+.\" The default value is 1, which is conformant to US
+.\" .Tn GOSIP .
+.\" When an ISO address is set in an ifconfig command,
+.\" it is really the
+.\" .Tn NSAP
+.\" which is being specified.
+.\" For example, in
+.\" .Tn US GOSIP ,
+.\" 20 hex digits should be
+.\" specified in the
+.\" .Tn ISO NSAP
+.\" to be assigned to the interface.
+.\" There is some evidence that a number different from 1 may be useful
+.\" for
+.\" .Tn AFI
+.\" 37 type addresses.
+.It Cm range Ar netrange
+Under appletalk, set the interface to respond to a
+.Ar netrange
+of the form
+.Ar startnet Ns - Ns Ar endnet .
+Appletalk uses this scheme instead of
+netmasks though
+.Fx
+implements it internally as a set of netmasks.
+.It Cm remove
+Another name for the
+.Fl alias
+parameter.
+Introduced for compatibility
+with
+.Bsx .
+.It Cm phase
+The argument following this specifies the version (phase) of the
+Appletalk network attached to the interface.
+Values of 1 or 2 are permitted.
+.Sm off
+.It Cm link Op Cm 0 No - Cm 2
+.Sm on
+Enable special processing of the link level of the interface.
+These three options are interface specific in actual effect, however,
+they are in general used to select special modes of operation.
+An example
+of this is to enable SLIP compression, or to select the connector type
+for some Ethernet cards.
+Refer to the man page for the specific driver
+for more information.
+.Sm off
+.It Fl link Op Cm 0 No - Cm 2
+.Sm on
+Disable special processing at the link level with the specified interface.
+.It Cm monitor
+Put the interface in monitor mode.
+No packets are transmitted, and received packets are discarded after
+.Xr bpf 4
+processing.
+.It Fl monitor
+Take the interface out of monitor mode.
+.It Cm up
+Mark an interface
+.Dq up .
+This may be used to enable an interface after an
+.Dq Nm Cm down .
+It happens automatically when setting the first address on an interface.
+If the interface was reset when previously marked down,
+the hardware will be re-initialized.
+.El
+.Pp
+The following parameters are specific to IEEE 802.11 wireless interfaces:
+.Bl -tag -width indent
+.It Cm apbridge
+When operating as an access point, pass packets between
+wireless clients directly (default).
+To instead let them pass up through the
+system and be forwarded using some other mechanism, use
+.Fl apbridge .
+Disabling the internal bridging
+is useful when traffic is to be processed with
+packet filtering.
+.It Cm authmode Ar mode
+Set the desired authentication mode in infrastructure mode.
+Not all adaptors support all modes.
+The set of
+valid modes is
+.Cm none , open , shared
+(shared key),
+.Cm 8021x
+(IEEE 802.1x),
+and
+.Cm wpa
+(IEEE WPA/WPA2/802.11i).
+The
+.Cm 8021x
+and
+.Cm wpa
+modes are only useful when using an authentication service
+(a supplicant for client operation or an authenticator when
+operating as an access point).
+Modes are case insensitive.
+.It Cm bintval Ar interval
+Set the interval at which beacon frames are sent when operating in
+ad-hoc or ap mode.
+The
+.Ar interval
+parameter is specified in TU's (1/1024 msecs).
+By default beacon frames are transmitted every 100 TU's.
+.It Cm bssid Ar address
+Specify the MAC address of the access point to use when operating
+as a station in a BSS network.
+This overrides any automatic selection done by the system.
+To disable a previously selected access point, supply
+.Cm any , none ,
+or
+.Cm -
+for the address.
+This option is useful when more than one access points have the same SSID.
+Another name for the
+.Cm bssid
+parameter is
+.Cm ap .
+.It Cm burst
+Enable packet bursting.
+Packet bursting is a transmission technique whereby the wireless
+medium is acquired once to send multiple frames and the interframe
+spacing is reduced.
+This technique can significantly increase throughput by reducing
+transmission overhead.
+Packet bursting is supported by the 802.11e QoS specification
+and some devices that do not support QoS may still be capable.
+By default packet bursting is enabled if a device is capable
+of doing it.
+To disable packet bursting, use
+.Fl burst .
+.It Cm chanlist Ar channels
+Set the desired channels to use when scanning for access
+points, neighbors in an IBSS network, or looking for unoccupied
+channels when operating as an access point.
+The set of channels is specified as a comma-separated list with
+each element in the list representing either a single channel number or a range
+of the form
+.Dq Li a-b .
+Channel numbers must be in the range 1 to 255 and be permissible
+according to the operating characteristics of the device.
+.It Cm channel Ar number
+Set a single desired channel.
+Channels range from 1 to 255, but the exact selection available
+depends on the region your adaptor was manufactured for.
+Setting
+the channel to
+.Li 0 ,
+.Cm any ,
+or
+.Cm -
+will give you the default for your adaptor.
+Some
+adaptors ignore this setting unless you are in ad-hoc mode.
+Alternatively the frequency, in megahertz, may be specified
+instead of the channel number.
+.It Cm deftxkey Ar index
+Set the default key to use for transmission.
+Typically this is only set when using WEP encryption.
+The
+.Cm weptxkey
+is an alias for this request; it is provided for backwards compatibility.
+.It Cm dtimperiod Ar period
+Set the
+DTIM
+period for transmitting buffered multicast data frames when
+operating in ap mode.
+The
+.Ar period
+specifies the number of beacon intervals between DTIM
+and must be in the range 1 to 15.
+By default DTIM is 1 (i.e., DTIM occurs at each beacon).
+.It Cm fragthreshold Ar length
+Set the threshold for which transmitted frames are broken into fragments.
+The
+.Ar length
+argument is the frame size in bytes and must be in the range 256 to 2346.
+Setting
+.Ar length
+to
+.Li 2346 ,
+.Cm any ,
+or
+.Cm -
+disables transmit fragmentation.
+Not all adaptors honor the fragmentation threshold.
+.It Cm hidessid
+When operating as an access point, do not broadcast the SSID
+in beacon frames or respond to probe request frames unless
+they are directed to the ap (i.e., they include the ap's SSID).
+By default, the SSID is included in beacon frames and
+undirected probe request frames are answered.
+To re-enable the broadcast of the SSID etc., use
+.Fl hidessid .
+.It Cm list active
+Display the list of channels available for use taking into account
+any restrictions set with the
+.Cm chanlist
+and
+.Cm channel
+directives.
+.It Cm list caps
+Display the adaptor's capabilities, including the operating
+modes supported.
+.It Cm list chan
+Display the list of channels available for use.
+.Cm list freq
+is another way of requesting this information.
+.It Cm list mac
+Display the current MAC Access Control List state.
+Each address is prefixed with a character that indicates the
+current policy applied to it:
+.Ql +
+indicates the address is allowed access,
+.Ql -
+indicates the address is denied access,
+.Ql *
+indicates the address is present but the current policy open
+(so the ACL is not consulted).
+.It Cm list scan
+Display the access points and/or ad-hoc neighbors
+located in the vicinity.
+This information may be updated automatically by the adaptor
+and/or with a
+.Cm scan
+request.
+.Cm list ap
+is another way of requesting this information.
+.It Cm list sta
+When operating as an access point display the stations that are
+currently associated.
+When operating in ad-hoc mode display stations identified as
+neighbors in the IBSS.
+.It Cm list wme
+Display the current parameters to use when operating in WME mode.
+When WME mode is enabled for an adaptor this information will be
+displayed with the regular status; this command is mostly useful
+for examining parameters when WME mode is disabled.
+See the description of the
+.Cm wme
+directive for information on the various parameters.
+.It Cm mcastrate Ar rate
+Set the rate for transmitting multicast/broadcast frames.
+Rates are specified as megabits/second in decimal; e.g. 5.5 for 5.5 Mb/s.
+This rate should be valid for the current operating conditions;
+if an invalid rate is specified drivers are free to chose an
+appropriate rate.
+.It Cm powersave
+Enable powersave operation.
+When operating as a client, the station will conserve power by
+periodically turning off the radio and listening for
+messages from the access point telling it there are packets waiting.
+The station must then retrieve the packets.
+When operating as an access point, the station must honor power
+save operation of associated clients.
+Not all devices support power save operation, either as a client
+or as an access point.
+Use
+.Fl powersave
+to disable powersave operation.
+.It Cm powersavesleep Ar sleep
+Set the desired max powersave sleep time in milliseconds.
+.It Cm protmode Ar technique
+For interfaces operating in 802.11g, use the specified
+.Ar technique
+for protecting OFDM frames in a mixed 11b/11g network.
+The set of valid techniques is
+.Cm off , cts
+(CTS to self),
+and
+.Cm rtscts
+(RTS/CTS).
+Technique names are case insensitive.
+.It Cm pureg
+When operating as an access point in 802.11g mode allow only
+11g-capable stations to associate (11b-only stations are not
+permitted to associate).
+To allow both 11g and 11b-only stations to associate, use
+.Fl pureg .
+.It Cm roaming Ar mode
+When operating as a station, control how the system will
+behave when communication with the current access point
+is broken.
+The
+.Ar mode
+argument may be one of
+.Cm device
+(leave it to the hardware device to decide),
+.Cm auto
+(handle either in the device or the operating system\[em]as appropriate),
+.Cm manual
+(do nothing until explicitly instructed).
+By default, the device is left to handle this if it is
+capable; otherwise, the operating system will automatically
+attempt to reestablish communication.
+Manual mode is mostly useful when an application wants to
+control the selection of an access point.
+.It Cm rtsthreshold Ar length
+Set the threshold for which
+transmitted frames are preceded by transmission of an
+RTS
+control frame.
+The
+.Ar length
+argument
+is the frame size in bytes and must be in the range 1 to 2346.
+Setting
+.Ar length
+to
+.Li 2346 ,
+.Cm any ,
+or
+.Cm -
+disables transmission of RTS frames.
+Not all adaptors support setting the RTS threshold.
+.It Cm ssid Ar ssid
+Set the desired Service Set Identifier (aka network name).
+The SSID is a string up to 32 characters
+in length and may be specified as either a normal string or in
+hexadecimal when preceded by
+.Ql 0x .
+Additionally, the SSID may be cleared by setting it to
+.Ql - .
+.It Cm scan
+Initiate a scan of neighboring stations, wait for it to complete, and
+display all stations found.
+Only the super-user can initiate a scan.
+Depending on the capabilities of the APs, the following
+flags can be included in the output:
+.Bl -tag -width 3n
+.It Li E
+Extended Service Set (ESS).
+Indicates that the station is part of an infrastructure network
+(in contrast to an IBSS/ad-hoc network).
+.It Li I
+IBSS/ad-hoc network.
+Indicates that the station is part of an ad-hoc network
+(in contrast to an ESS network).
+.It Li P
+Privacy.
+Data confidentiality is required for all data frames
+exchanged within the BSS.
+This means that this BSS requires the station to
+use cryptographic means such as WEP, TKIP or AES-CCMP to
+encrypt/decrypt data frames being exchanged with others.
+.It Li S
+Short Preamble.
+Indicates that the network is using short preambles (defined
+in 802.11b High Rate/DSSS PHY, short preamble utilizes a
+56 bit sync field in contrast to a 128 bit field used in long
+preamble mode).
+.It Li s
+Short slot time.
+Indicates that the network is using a short slot time.
+.El
+.Pp
+The
+.Cm list scan
+request can be used to show recent scan results without
+initiating a new scan.
+.It Cm stationname Ar name
+Set the name of this station.
+It appears that the station name is not really part of the IEEE 802.11
+protocol though all interfaces seem to support it.
+As such it only
+seems to be meaningful to identical or virtually identical equipment.
+Setting the station name is identical in syntax to setting the SSID.
+.It Cm txpower Ar power
+Set the power used to transmit frames.
+The
+.Ar power
+argument
+is a unitless value in the range 0 to 100 that is interpreted
+by drivers to derive a device-specific value.
+Out of range values are truncated.
+Typically only a few discreet power settings are available and
+the driver will use the setting closest to the specified value.
+Not all adaptors support changing the transmit power.
+.It Cm wepmode Ar mode
+Set the desired WEP mode.
+Not all adaptors support all modes.
+The set of valid modes is
+.Cm off , on ,
+and
+.Cm mixed .
+The
+.Cm mixed
+mode explicitly tells the adaptor to allow association with access
+points which allow both encrypted and unencrypted traffic.
+On these adaptors,
+.Cm on
+means that the access point must only allow encrypted connections.
+On other adaptors,
+.Cm on
+is generally another name for
+.Cm mixed .
+Modes are case insensitive.
+.It Cm weptxkey Ar index
+Set the WEP key to be used for transmission.
+This is the same as setting the default transmission key with
+.Cm deftxkey .
+.It Cm wepkey Ar key Ns | Ns Ar index : Ns Ar key
+Set the selected WEP key.
+If an
+.Ar index
+is not given, key 1 is set.
+A WEP key will be either 5 or 13
+characters (40 or 104 bits) depending of the local network and the
+capabilities of the adaptor.
+It may be specified either as a plain
+string or as a string of hexadecimal digits preceded by
+.Ql 0x .
+For maximum portability, hex keys are recommended;
+the mapping of text keys to WEP encryption is usually driver-specific.
+In particular, the
+.Tn Windows
+drivers do this mapping differently to
+.Fx .
+A key may be cleared by setting it to
+.Ql - .
+If WEP is supported then there are at least four keys.
+Some adaptors support more than four keys.
+If that is the case, then the first four keys
+(1-4) will be the standard temporary keys and any others will be adaptor
+specific keys such as permanent keys stored in NVRAM.
+.It Cm wme
+Enable Wireless Multimedia Extensions (WME) support, if available,
+for the specified interface.
+WME is a subset of the IEEE 802.11e standard to support the
+efficient communication of realtime and multimedia data.
+To disable WME support, use
+.Fl wme .
+.Pp
+The following parameters are meaningful only when WME support is in use.
+Parameters are specified per-AC (Access Category) and
+split into those that are used by a station when acting
+as an access point and those for client stations in the BSS.
+The latter are received from the access point and may not be changed
+(at the station).
+The following Access Categories are recognized:
+.Pp
+.Bl -tag -width ".Cm AC_BK" -compact
+.It Cm AC_BE
+(or
+.Cm BE )
+best effort delivery,
+.It Cm AC_BK
+(or
+.Cm BK )
+background traffic,
+.It Cm AC_VI
+(or
+.Cm VI )
+video traffic,
+.It Cm AC_VO
+(or
+.Cm VO )
+voice traffic.
+.El
+.Pp
+AC parameters are case-insensitive.
+Traffic classification is done in the operating system using the
+vlan priority associated with data frames or the
+ToS (Type of Service) indication in IP-encapsulated frames.
+If neither information is present, traffic is assigned to the
+Best Effort (BE) category.
+.Bl -tag -width indent
+.It Cm ack Ar ac
+Set the ACK policy for QoS transmissions by the local station;
+this controls whether or not data frames transmitted by a station
+require an ACK response from the receiving station.
+To disable waiting for an ACK use
+.Fl ack .
+This parameter is applied only to the local station.
+.It Cm acm Ar ac
+Enable the Admission Control Mandatory (ACM) mechanism
+for transmissions by the local station.
+To disable the ACM use
+.Fl acm .
+On stations in a BSS this parameter is read-only and indicates
+the setting received from the access point.
+NB: ACM is not supported right now.
+.It Cm aifs Ar ac Ar count
+Set the Arbitration Inter Frame Spacing (AIFS)
+channel access parameter to use for transmissions
+by the local station.
+On stations in a BSS this parameter is read-only and indicates
+the setting received from the access point.
+.It Cm cwmin Ar ac Ar count
+Set the CWmin channel access parameter to use for transmissions
+by the local station.
+On stations in a BSS this parameter is read-only and indicates
+the setting received from the access point.
+.It Cm cwmax Ar ac Ar count
+Set the CWmax channel access parameter to use for transmissions
+by the local station.
+On stations in a BSS this parameter is read-only and indicates
+the setting received from the access point.
+.It Cm txoplimit Ar ac Ar limit
+Set the Transmission Opportunity Limit channel access parameter
+to use for transmissions by the local station.
+This parameter defines an interval of time when a WME station
+has the right to initiate transmissions onto the wireless medium.
+On stations in a BSS this parameter is read-only and indicates
+the setting received from the access point.
+.It Cm bss:aifs Ar ac Ar count
+Set the AIFS channel access parameter to send to stations in a BSS.
+This parameter is meaningful only when operating in ap mode.
+.It Cm bss:cwmin Ar ac Ar count
+Set the CWmin channel access parameter to send to stations in a BSS.
+This parameter is meaningful only when operating in ap mode.
+.It Cm bss:cwmax Ar ac Ar count
+Set the CWmax channel access parameter to send to stations in a BSS.
+This parameter is meaningful only when operating in ap mode.
+.It Cm bss:txoplimit Ar ac Ar limit
+Set the TxOpLimit channel access parameter to send to stations in a BSS.
+This parameter is meaningful only when operating in ap mode.
+.El
+.El
+.Pp
+The following parameters support an optional access control list
+feature available with some adaptors when operating in ap mode; see
+.Xr wlan_acl 4 .
+This facility allows an access point to accept/deny association
+requests based on the MAC address of the station.
+Note that this feature does not significantly enhance security
+as MAC address spoofing is easy to do.
+.Bl -tag -width indent
+.It Cm mac:add Ar address
+Add the specified MAC address to the database.
+Depending on the policy setting association requests from the
+specified station will be allowed or denied.
+.It Cm mac:allow
+Set the ACL policy to permit association only by
+stations registered in the database.
+.It Cm mac:del
+Delete the specified MAC address from the database.
+.It Cm mac:deny
+Set the ACL policy to deny association only by
+stations registered in the database.
+.It Cm mac:kick
+Force the specified station to be deauthenticated.
+This typically is done to block a station after updating the
+address database.
+.It Cm mac:open
+Set the ACL policy to allow all stations to associate.
+.It Cm mac:flush
+Delete all entries in the database.
+.El
+.Pp
+The following parameters are for compatibility with other systems:
+.Bl -tag -width indent
+.It Cm nwid Ar ssid
+Another name for the
+.Cm ssid
+parameter.
+Included for
+.Nx
+compatibility.
+.It Cm station Ar name
+Another name for the
+.Cm stationname
+parameter.
+Included for
+.Bsx
+compatibility.
+.It Cm wep
+Another way of saying
+.Cm wepmode on .
+Included for
+.Bsx
+compatibility.
+.It Fl wep
+Another way of saying
+.Cm wepmode off .
+Included for
+.Bsx
+compatibility.
+.It Cm nwkey key
+Another way of saying:
+.Dq Li "wepmode on weptxkey 1 wepkey 1:key wepkey 2:- wepkey 3:- wepkey 4:-" .
+Included for
+.Nx
+compatibility.
+.It Cm nwkey Xo
+.Sm off
+.Ar n : k1 , k2 , k3 , k4
+.Sm on
+.Xc
+Another way of saying
+.Dq Li "wepmode on weptxkey n wepkey 1:k1 wepkey 2:k2 wepkey 3:k3 wepkey 4:k4" .
+Included for
+.Nx
+compatibility.
+.It Fl nwkey
+Another way of saying
+.Cm wepmode off .
+Included for
+.Nx
+compatibility.
+.El
+.Pp
+The following parameters are specific to bridge interfaces:
+.Bl -tag -width indent
+.It Cm addm Ar interface
+Add the interface named by
+.Ar interface
+as a member of the bridge.
+The interface is put into promiscuous mode
+so that it can receive every packet sent on the network.
+.It Cm deletem Ar interface
+Remove the interface named by
+.Ar interface
+from the bridge.
+Promiscuous mode is disabled on the interface when
+it is removed from the bridge.
+.It Cm maxaddr Ar size
+Set the size of the bridge address cache to
+.Ar size .
+The default is 100 entries.
+.It Cm timeout Ar seconds
+Set the timeout of address cache entries to
+.Ar seconds
+seconds.
+If
+.Ar seconds
+is zero, then address cache entries will not be expired.
+The default is 240 seconds.
+.It Cm addr
+Display the addresses that have been learned by the bridge.
+.It Cm static Ar interface-name Ar address
+Add a static entry into the address cache pointing to
+.Ar interface-name .
+Static entries are never aged out of the cache or re-placed, even if the
+address is seen on a different interface.
+.It Cm deladdr Ar address
+Delete
+.Ar address
+from the address cache.
+.It Cm flush
+Delete all dynamically-learned addresses from the address cache.
+.It Cm flushall
+Delete all addresses, including static addresses, from the address cache.
+.It Cm discover Ar interface
+Mark an interface as a
+.Dq discovering
+interface.
+When the bridge has no address cache entry
+(either dynamic or static)
+for the destination address of a packet,
+the bridge will forward the packet to all
+member interfaces marked as
+.Dq discovering .
+This is the default for all interfaces added to a bridge.
+.It Cm -discover Ar interface
+Clear the
+.Dq discovering
+attribute on a member interface.
+For packets without the
+.Dq discovering
+attribute, the only packets forwarded on the interface are broadcast
+or multicast packets and packets for which the destination address
+is known to be on the interface's segment.
+.It Cm learn Ar interface
+Mark an interface as a
+.Dq learning
+interface.
+When a packet arrives on such an interface, the source
+address of the packet is entered into the address cache as being a
+destination address on the interface's segment.
+This is the default for all interfaces added to a bridge.
+.It Cm -learn Ar interface
+Clear the
+.Dq learning
+attribute on a member interface.
+.It Cm span Ar interface
+Add the interface named by
+.Ar interface
+as a span port on the bridge.
+Span ports transmit a copy of every frame received by the bridge.
+This is most useful for snooping a bridged network passively on
+another host connected to one of the span ports of the bridge.
+.It Cm -span Ar interface
+Delete the interface named by
+.Ar interface
+from the list of span ports of the bridge.
+.It Cm stp Ar interface
+Enable Spanning Tree protocol on
+.Ar interface .
+The
+.Xr if_bridge 4
+driver has support for the IEEE 802.1D Spanning Tree protocol (STP).
+Spanning Tree is used to detect and remove loops in a network topology.
+.It Cm -stp Ar interface
+Disable Spanning Tree protocol on
+.Ar interface .
+This is the default for all interfaces added to a bridge.
+.It Cm maxage Ar seconds
+Set the time that a Spanning Tree protocol configuration is valid.
+The default is 20 seconds.
+The minimum is 1 second and the maximum is 255 seconds.
+.It Cm fwddelay Ar seconds
+Set the time that must pass before an interface begins forwarding
+packets when Spanning Tree is enabled.
+The default is 15 seconds.
+The minimum is 1 second and the maximum is 255 seconds.
+.It Cm hellotime Ar seconds
+Set the time between broadcasting of Spanning Tree protocol
+configuration messages.
+The default is 2 seconds.
+The minimum is 1 second and the maximum is 255 seconds.
+.It Cm priority Ar value
+Set the bridge priority for Spanning Tree.
+The default is 32768.
+The minimum is 0 and the maximum is 65536.
+.It Cm ifpriority Ar interface Ar value
+Set the Spanning Tree priority of
+.Ar interface
+to
+.Ar value .
+The default is 128.
+The minimum is 0 and the maximum is 255.
+.It Cm ifpathcost Ar interface Ar value
+Set the Spanning Tree path cost of
+.Ar interface
+to
+.Ar value .
+The default is 55.
+The minimum is 0 and the maximum is 65535.
+.El
+.Pp
+The following parameters are specific to IP tunnel interfaces,
+.Xr gif 4 :
+.Bl -tag -width indent
+.It Cm tunnel Ar src_addr dest_addr
+Configure the physical source and destination address for IP tunnel
+interfaces.
+The arguments
+.Ar src_addr
+and
+.Ar dest_addr
+are interpreted as the outer source/destination for the encapsulating
+IPv4/IPv6 header.
+.It Fl tunnel
+Unconfigure the physical source and destination address for IP tunnel
+interfaces previously configured with
+.Cm tunnel .
+.It Cm deletetunnel
+Another name for the
+.Fl tunnel
+parameter.
+.El
+.Pp
+The following parameters are specific to
+.Xr pfsync 4
+interfaces:
+.Bl -tag -width indent
+.It Cm maxupd Ar n
+Set the maximum number of updates for a single state which
+can be collapsed into one.
+This is an 8-bit number; the default value is 128.
+.El
+.Pp
+The following parameters are specific to
+.Xr vlan 4
+interfaces:
+.Bl -tag -width indent
+.It Cm vlan Ar vlan_tag
+Set the VLAN tag value to
+.Ar vlan_tag .
+This value is a 16-bit number which is used to create an 802.1Q
+VLAN header for packets sent from the
+.Xr vlan 4
+interface.
+Note that
+.Cm vlan
+and
+.Cm vlandev
+must both be set at the same time.
+.It Cm vlandev Ar iface
+Associate the physical interface
+.Ar iface
+with a
+.Xr vlan 4
+interface.
+Packets transmitted through the
+.Xr vlan 4
+interface will be
+diverted to the specified physical interface
+.Ar iface
+with 802.1Q VLAN encapsulation.
+Packets with 802.1Q encapsulation received
+by the parent interface with the correct VLAN tag will be diverted to
+the associated
+.Xr vlan 4
+pseudo-interface.
+The
+.Xr vlan 4
+interface is assigned a
+copy of the parent interface's flags and the parent's ethernet address.
+The
+.Cm vlandev
+and
+.Cm vlan
+must both be set at the same time.
+If the
+.Xr vlan 4
+interface already has
+a physical interface associated with it, this command will fail.
+To
+change the association to another physical interface, the existing
+association must be cleared first.
+.Pp
+Note: if the hardware tagging capability
+is set on the parent interface, the
+.Xr vlan 4
+pseudo
+interface's behavior changes:
+the
+.Xr vlan 4
+interface recognizes that the
+parent interface supports insertion and extraction of VLAN tags on its
+own (usually in firmware) and that it should pass packets to and from
+the parent unaltered.
+.It Fl vlandev Op Ar iface
+If the driver is a
+.Xr vlan 4
+pseudo device, disassociate the parent interface from it.
+This breaks the link between the
+.Xr vlan 4
+interface and its parent,
+clears its VLAN tag, flags and its link address and shuts the interface down.
+The
+.Ar iface
+argument is useless and hence deprecated.
+.El
+.Pp
+The
+.Nm
+utility displays the current configuration for a network interface
+when no optional parameters are supplied.
+If a protocol family is specified,
+.Nm
+will report only the details specific to that protocol family.
+.Pp
+If the
+.Fl m
+flag is passed before an interface name,
+.Nm
+will display the capability list and all
+of the supported media for the specified interface.
+If
+.Fl L
+flag is supplied, address lifetime is displayed for IPv6 addresses,
+as time offset string.
+.Pp
+Optionally, the
+.Fl a
+flag may be used instead of an interface name.
+This flag instructs
+.Nm
+to display information about all interfaces in the system.
+The
+.Fl d
+flag limits this to interfaces that are down, and
+.Fl u
+limits this to interfaces that are up.
+When no arguments are given,
+.Fl a
+is implied.
+.Pp
+The
+.Fl l
+flag may be used to list all available interfaces on the system, with
+no other additional information.
+Use of this flag is mutually exclusive
+with all other flags and commands, except for
+.Fl d
+(only list interfaces that are down)
+and
+.Fl u
+(only list interfaces that are up).
+.Pp
+The
+.Fl v
+flag may be used to get more verbose status for an interface.
+.Pp
+The
+.Fl C
+flag may be used to list all of the interface cloners available on
+the system, with no additional information.
+Use of this flag is mutually exclusive with all other flags and commands.
+.Pp
+The
+.Fl k
+flag causes keying information for the interface, if available, to be
+printed.
+For example, the values of 802.11 WEP keys will be printed, if accessible to
+the current user.
+This information is not printed by default, as it may be considered
+sensitive.
+.Pp
+Only the super-user may modify the configuration of a network interface.
+.Sh NOTES
+The media selection system is relatively new and only some drivers support
+it (or have need for it).
+.Sh DIAGNOSTICS
+Messages indicating the specified interface does not exist, the
+requested address is unknown, or the user is not privileged and
+tried to alter an interface's configuration.
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr carp 4 ,
+.Xr netintro 4 ,
+.Xr pfsync 4 ,
+.Xr polling 4 ,
+.Xr vlan 4 ,
+.\" .Xr eon 5 ,
+.Xr rc 8 ,
+.Xr routed 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
+.Sh BUGS
+Basic IPv6 node operation requires a link-local address on each
+interface configured for IPv6.
+Normally, such an address is automatically configured by the
+kernel on each interface added to the system; this behaviour may
+be disabled by setting the sysctl MIB variable
+.Va net.inet6.ip6.auto_linklocal
+to 0.
+.Pp
+If you delete such an address using
+.Nm ,
+the kernel may act very oddly.
+Do this at your own risk.
diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c
new file mode 100644
index 0000000..1c22cf4
--- /dev/null
+++ b/sbin/ifconfig/ifconfig.c
@@ -0,0 +1,1065 @@
+/*
+ * 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)ifconfig.c 8.2 (Berkeley) 2/16/94";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/module.h>
+#include <sys/linker.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+/* IP */
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ifconfig.h"
+
+/*
+ * Since "struct ifreq" is composed of various union members, callers
+ * should pay special attention to interprete the value.
+ * (.e.g. little/big endian difference in the structure.)
+ */
+struct ifreq ifr;
+
+char name[IFNAMSIZ];
+int flags;
+int setaddr;
+int setipdst;
+int setmask;
+int doalias;
+int clearaddr;
+int newaddr = 1;
+int verbose;
+
+int supmedia = 0;
+int printkeys = 0; /* Print keying material for interfaces. */
+int printname = 0; /* Print the name of the created interface. */
+
+static int ifconfig(int argc, char *const *argv, const struct afswtch *afp);
+static void status(const struct afswtch *afp, int addrcount,
+ struct sockaddr_dl *sdl, struct if_msghdr *ifm,
+ struct ifa_msghdr *ifam);
+static void tunnel_status(int s);
+static void usage(void);
+
+static struct afswtch *af_getbyname(const char *name);
+static struct afswtch *af_getbyfamily(int af);
+static void af_other_status(int);
+
+static struct option *opts = NULL;
+
+void
+opt_register(struct option *p)
+{
+ p->next = opts;
+ opts = p;
+}
+
+static void
+usage(void)
+{
+ char options[1024];
+ struct option *p;
+
+ /* XXX not right but close enough for now */
+ options[0] = '\0';
+ for (p = opts; p != NULL; p = p->next) {
+ strlcat(options, p->opt_usage, sizeof(options));
+ strlcat(options, " ", sizeof(options));
+ }
+
+ fprintf(stderr,
+ "usage: ifconfig %sinterface address_family [address [dest_address]]\n"
+ " [parameters]\n"
+ " ifconfig interface create\n"
+ " ifconfig -a %s[-d] [-m] [-u] [-v] [address_family]\n"
+ " ifconfig -l [-d] [-u] [address_family]\n"
+ " ifconfig %s[-d] [-m] [-u] [-v]\n",
+ options, options, options);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c, all, namesonly, downonly, uponly;
+ int need_nl = 0, count = 0;
+ const struct afswtch *afp = NULL;
+ int addrcount, ifindex;
+ struct if_msghdr *ifm, *nextifm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr_dl *sdl;
+ char *buf, *lim, *next;
+ size_t needed;
+ int mib[6];
+ char options[1024];
+ struct option *p;
+
+ all = downonly = uponly = namesonly = verbose = 0;
+
+ /* Parse leading line options */
+ strlcpy(options, "adklmuv", sizeof(options));
+ for (p = opts; p != NULL; p = p->next)
+ strlcat(options, p->opt, sizeof(options));
+ while ((c = getopt(argc, argv, options)) != -1) {
+ switch (c) {
+ case 'a': /* scan all interfaces */
+ all++;
+ break;
+ case 'd': /* restrict scan to "down" interfaces */
+ downonly++;
+ break;
+ case 'k':
+ printkeys++;
+ break;
+ case 'l': /* scan interface names only */
+ namesonly++;
+ break;
+ case 'm': /* show media choices in status */
+ supmedia = 1;
+ break;
+ case 'u': /* restrict scan to "up" interfaces */
+ uponly++;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ for (p = opts; p != NULL; p = p->next)
+ if (p->opt[0] == c) {
+ p->cb(optarg);
+ break;
+ }
+ if (p == NULL)
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* -l cannot be used with -a or -m */
+ if (namesonly && (all || supmedia))
+ usage();
+
+ /* nonsense.. */
+ if (uponly && downonly)
+ usage();
+
+ /* no arguments is equivalent to '-a' */
+ if (!namesonly && argc < 1)
+ all = 1;
+
+ /* -a and -l allow an address family arg to limit the output */
+ if (all || namesonly) {
+ if (argc > 1)
+ usage();
+
+ ifindex = 0;
+ if (argc == 1) {
+ afp = af_getbyname(*argv);
+ if (afp == NULL)
+ usage();
+ if (afp->af_name != NULL)
+ argc--, argv++;
+ /* leave with afp non-zero */
+ }
+ } else {
+ /* not listing, need an argument */
+ if (argc < 1)
+ usage();
+
+ strncpy(name, *argv, sizeof(name));
+ argc--, argv++;
+
+ /* check and maybe load support for this interface */
+ ifmaybeload(name);
+
+ /*
+ * NOTE: We must special-case the `create' command right
+ * here as we would otherwise fail when trying to find
+ * the interface.
+ */
+ if (argc > 0 && (strcmp(argv[0], "create") == 0 ||
+ strcmp(argv[0], "plumb") == 0)) {
+ clone_create();
+ argc--, argv++;
+ if (argc == 0)
+ goto end;
+ }
+ ifindex = if_nametoindex(name);
+ if (ifindex == 0)
+ errx(1, "interface %s does not exist", name);
+ }
+
+ /* Check for address family */
+ if (argc > 0) {
+ afp = af_getbyname(*argv);
+ if (afp != NULL)
+ argc--, argv++;
+ }
+
+retry:
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0; /* address family */
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = ifindex; /* interface index */
+
+ /* if particular family specified, only ask about it */
+ if (afp != NULL)
+ mib[3] = afp->af_af;
+
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ errx(1, "iflist-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ errx(1, "malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
+ if (errno == ENOMEM && count++ < 10) {
+ warnx("Routing table grew, retrying");
+ free(buf);
+ sleep(1);
+ goto retry;
+ }
+ errx(1, "actual retrieval of interface table");
+ }
+ lim = buf + needed;
+
+ next = buf;
+ while (next < lim) {
+
+ ifm = (struct if_msghdr *)next;
+
+ if (ifm->ifm_type == RTM_IFINFO) {
+ if (ifm->ifm_data.ifi_datalen == 0)
+ ifm->ifm_data.ifi_datalen = sizeof(struct if_data);
+ sdl = (struct sockaddr_dl *)((char *)ifm + sizeof(struct if_msghdr) -
+ sizeof(struct if_data) + ifm->ifm_data.ifi_datalen);
+ flags = ifm->ifm_flags;
+ } else {
+ fprintf(stderr, "out of sync parsing NET_RT_IFLIST\n");
+ fprintf(stderr, "expected %d, got %d\n", RTM_IFINFO,
+ ifm->ifm_type);
+ fprintf(stderr, "msglen = %d\n", ifm->ifm_msglen);
+ fprintf(stderr, "buf:%p, next:%p, lim:%p\n", buf, next,
+ lim);
+ exit (1);
+ }
+
+ next += ifm->ifm_msglen;
+ ifam = NULL;
+ addrcount = 0;
+ while (next < lim) {
+
+ nextifm = (struct if_msghdr *)next;
+
+ if (nextifm->ifm_type != RTM_NEWADDR)
+ break;
+
+ if (ifam == NULL)
+ ifam = (struct ifa_msghdr *)nextifm;
+
+ addrcount++;
+ next += nextifm->ifm_msglen;
+ }
+ memcpy(name, sdl->sdl_data,
+ sizeof(name) < sdl->sdl_nlen ?
+ sizeof(name)-1 : sdl->sdl_nlen);
+ name[sizeof(name) < sdl->sdl_nlen ?
+ sizeof(name)-1 : sdl->sdl_nlen] = '\0';
+
+ if (all || namesonly) {
+ if (uponly)
+ if ((flags & IFF_UP) == 0)
+ continue; /* not up */
+ if (downonly)
+ if (flags & IFF_UP)
+ continue; /* not down */
+ if (namesonly) {
+ if (afp == NULL || afp->af_af != AF_LINK ||
+ sdl->sdl_type == IFT_ETHER) {
+ if (need_nl)
+ putchar(' ');
+ fputs(name, stdout);
+ need_nl++;
+ }
+ continue;
+ }
+ }
+
+ if (argc > 0)
+ ifconfig(argc, argv, afp);
+ else
+ status(afp, addrcount, sdl, ifm, ifam);
+ }
+ free(buf);
+
+ if (namesonly && need_nl > 0)
+ putchar('\n');
+end:
+ if (printname)
+ printf("%s\n", name);
+
+ exit (0);
+}
+
+static struct afswtch *afs = NULL;
+
+void
+af_register(struct afswtch *p)
+{
+ p->af_next = afs;
+ afs = p;
+}
+
+static struct afswtch *
+af_getbyname(const char *name)
+{
+ struct afswtch *afp;
+
+ for (afp = afs; afp != NULL; afp = afp->af_next)
+ if (strcmp(afp->af_name, name) == 0)
+ return afp;
+ return NULL;
+}
+
+static struct afswtch *
+af_getbyfamily(int af)
+{
+ struct afswtch *afp;
+
+ for (afp = afs; afp != NULL; afp = afp->af_next)
+ if (afp->af_af == af)
+ return afp;
+ return NULL;
+}
+
+static void
+af_other_status(int s)
+{
+ struct afswtch *afp;
+ uint8_t afmask[howmany(AF_MAX, NBBY)];
+
+ memset(afmask, 0, sizeof(afmask));
+ for (afp = afs; afp != NULL; afp = afp->af_next) {
+ if (afp->af_other_status == NULL)
+ continue;
+ if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af))
+ continue;
+ afp->af_other_status(s);
+ setbit(afmask, afp->af_af);
+ }
+}
+
+static void
+af_all_tunnel_status(int s)
+{
+ struct afswtch *afp;
+ uint8_t afmask[howmany(AF_MAX, NBBY)];
+
+ memset(afmask, 0, sizeof(afmask));
+ for (afp = afs; afp != NULL; afp = afp->af_next) {
+ if (afp->af_status_tunnel == NULL)
+ continue;
+ if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af))
+ continue;
+ afp->af_status_tunnel(s);
+ setbit(afmask, afp->af_af);
+ }
+}
+
+static struct cmd *cmds = NULL;
+
+void
+cmd_register(struct cmd *p)
+{
+ p->c_next = cmds;
+ cmds = p;
+}
+
+static const struct cmd *
+cmd_lookup(const char *name)
+{
+#define N(a) (sizeof(a)/sizeof(a[0]))
+ const struct cmd *p;
+
+ for (p = cmds; p != NULL; p = p->c_next)
+ if (strcmp(name, p->c_name) == 0)
+ return p;
+ return NULL;
+#undef N
+}
+
+struct callback {
+ callback_func *cb_func;
+ void *cb_arg;
+ struct callback *cb_next;
+};
+static struct callback *callbacks = NULL;
+
+void
+callback_register(callback_func *func, void *arg)
+{
+ struct callback *cb;
+
+ cb = malloc(sizeof(struct callback));
+ if (cb == NULL)
+ errx(1, "unable to allocate memory for callback");
+ cb->cb_func = func;
+ cb->cb_arg = arg;
+ cb->cb_next = callbacks;
+ callbacks = cb;
+}
+
+/* specially-handled commands */
+static void setifaddr(const char *, int, int, const struct afswtch *);
+static const struct cmd setifaddr_cmd = DEF_CMD("ifaddr", 0, setifaddr);
+
+static void setifdstaddr(const char *, int, int, const struct afswtch *);
+static const struct cmd setifdstaddr_cmd =
+ DEF_CMD("ifdstaddr", 0, setifdstaddr);
+
+static int
+ifconfig(int argc, char *const *argv, const struct afswtch *afp)
+{
+ struct callback *cb;
+ int s;
+
+ if (afp == NULL)
+ afp = af_getbyname("inet");
+ ifr.ifr_addr.sa_family =
+ afp->af_af == AF_LINK || afp->af_af == AF_UNSPEC ?
+ AF_INET : afp->af_af;
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+
+ if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket(family %u,SOCK_DGRAM", ifr.ifr_addr.sa_family);
+
+ while (argc > 0) {
+ const struct cmd *p;
+
+ p = cmd_lookup(*argv);
+ if (p == NULL) {
+ /*
+ * Not a recognized command, choose between setting
+ * the interface address and the dst address.
+ */
+ p = (setaddr ? &setifdstaddr_cmd : &setifaddr_cmd);
+ }
+ if (p->c_u.c_func || p->c_u.c_func2) {
+ if (p->c_parameter == NEXTARG) {
+ if (argv[1] == NULL)
+ errx(1, "'%s' requires argument",
+ p->c_name);
+ p->c_u.c_func(argv[1], 0, s, afp);
+ argc--, argv++;
+ } else if (p->c_parameter == OPTARG) {
+ p->c_u.c_func(argv[1], 0, s, afp);
+ if (argv[1] != NULL)
+ argc--, argv++;
+ } else if (p->c_parameter == NEXTARG2) {
+ if (argc < 3)
+ errx(1, "'%s' requires 2 arguments",
+ p->c_name);
+ p->c_u.c_func2(argv[1], argv[2], s, afp);
+ argc -= 2, argv += 2;
+ } else
+ p->c_u.c_func(*argv, p->c_parameter, s, afp);
+ }
+ argc--, argv++;
+ }
+
+ /*
+ * Do any post argument processing required by the address family.
+ */
+ if (afp->af_postproc != NULL)
+ afp->af_postproc(s, afp);
+ /*
+ * Do deferred callbacks registered while processing
+ * command-line arguments.
+ */
+ for (cb = callbacks; cb != NULL; cb = cb->cb_next)
+ cb->cb_func(s, cb->cb_arg);
+ /*
+ * Do deferred operations.
+ */
+ if (clearaddr) {
+ if (afp->af_ridreq == NULL || afp->af_difaddr == 0) {
+ warnx("interface %s cannot change %s addresses!",
+ name, afp->af_name);
+ clearaddr = 0;
+ }
+ }
+ if (clearaddr) {
+ int ret;
+ strncpy(afp->af_ridreq, name, sizeof ifr.ifr_name);
+ ret = ioctl(s, afp->af_difaddr, afp->af_ridreq);
+ if (ret < 0) {
+ if (errno == EADDRNOTAVAIL && (doalias >= 0)) {
+ /* means no previous address for interface */
+ } else
+ Perror("ioctl (SIOCDIFADDR)");
+ }
+ }
+ if (newaddr) {
+ if (afp->af_addreq == NULL || afp->af_aifaddr == 0) {
+ warnx("interface %s cannot change %s addresses!",
+ name, afp->af_name);
+ newaddr = 0;
+ }
+ }
+ if (newaddr && (setaddr || setmask)) {
+ strncpy(afp->af_addreq, name, sizeof ifr.ifr_name);
+ if (ioctl(s, afp->af_aifaddr, afp->af_addreq) < 0)
+ Perror("ioctl (SIOCAIFADDR)");
+ }
+
+ close(s);
+ return(0);
+}
+
+/*ARGSUSED*/
+static void
+setifaddr(const char *addr, int param, int s, const struct afswtch *afp)
+{
+ if (afp->af_getaddr == NULL)
+ return;
+ /*
+ * Delay the ioctl to set the interface addr until flags are all set.
+ * The address interpretation may depend on the flags,
+ * and the flags may change when the address is set.
+ */
+ setaddr++;
+ if (doalias == 0 && afp->af_af != AF_LINK)
+ clearaddr = 1;
+ afp->af_getaddr(addr, (doalias >= 0 ? ADDR : RIDADDR));
+}
+
+static void
+settunnel(const char *src, const char *dst, int s, const struct afswtch *afp)
+{
+ struct addrinfo *srcres, *dstres;
+ int ecode;
+
+ if (afp->af_settunnel == NULL) {
+ warn("address family %s does not support tunnel setup",
+ afp->af_name);
+ return;
+ }
+
+ if ((ecode = getaddrinfo(src, NULL, NULL, &srcres)) != 0)
+ errx(1, "error in parsing address string: %s",
+ gai_strerror(ecode));
+
+ if ((ecode = getaddrinfo(dst, NULL, NULL, &dstres)) != 0)
+ errx(1, "error in parsing address string: %s",
+ gai_strerror(ecode));
+
+ if (srcres->ai_addr->sa_family != dstres->ai_addr->sa_family)
+ errx(1,
+ "source and destination address families do not match");
+
+ afp->af_settunnel(s, srcres, dstres);
+
+ freeaddrinfo(srcres);
+ freeaddrinfo(dstres);
+}
+
+/* ARGSUSED */
+static void
+deletetunnel(const char *vname, int param, int s, const struct afswtch *afp)
+{
+
+ if (ioctl(s, SIOCDIFPHYADDR, &ifr) < 0)
+ err(1, "SIOCDIFPHYADDR");
+}
+
+static void
+setifnetmask(const char *addr, int dummy __unused, int s,
+ const struct afswtch *afp)
+{
+ if (afp->af_getaddr != NULL) {
+ setmask++;
+ afp->af_getaddr(addr, MASK);
+ }
+}
+
+static void
+setifbroadaddr(const char *addr, int dummy __unused, int s,
+ const struct afswtch *afp)
+{
+ if (afp->af_getaddr != NULL)
+ afp->af_getaddr(addr, DSTADDR);
+}
+
+static void
+setifipdst(const char *addr, int dummy __unused, int s,
+ const struct afswtch *afp)
+{
+ const struct afswtch *inet;
+
+ inet = af_getbyname("inet");
+ if (inet == NULL)
+ return;
+ inet->af_getaddr(addr, DSTADDR);
+ setipdst++;
+ clearaddr = 0;
+ newaddr = 0;
+}
+
+static void
+notealias(const char *addr, int param, int s, const struct afswtch *afp)
+{
+#define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr))
+ if (setaddr && doalias == 0 && param < 0)
+ if (afp->af_addreq != NULL && afp->af_ridreq != NULL)
+ bcopy((caddr_t)rqtosa(af_addreq),
+ (caddr_t)rqtosa(af_ridreq),
+ rqtosa(af_addreq)->sa_len);
+ doalias = param;
+ if (param < 0) {
+ clearaddr = 1;
+ newaddr = 0;
+ } else
+ clearaddr = 0;
+#undef rqtosa
+}
+
+/*ARGSUSED*/
+static void
+setifdstaddr(const char *addr, int param __unused, int s,
+ const struct afswtch *afp)
+{
+ if (afp->af_getaddr != NULL)
+ afp->af_getaddr(addr, DSTADDR);
+}
+
+/*
+ * Note: doing an SIOCIGIFFLAGS scribbles on the union portion
+ * of the ifreq structure, which may confuse other parts of ifconfig.
+ * Make a private copy so we can avoid that.
+ */
+static void
+setifflags(const char *vname, int value, int s, const struct afswtch *afp)
+{
+ struct ifreq my_ifr;
+
+ bcopy((char *)&ifr, (char *)&my_ifr, sizeof(struct ifreq));
+
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&my_ifr) < 0) {
+ Perror("ioctl (SIOCGIFFLAGS)");
+ exit(1);
+ }
+ strncpy(my_ifr.ifr_name, name, sizeof (my_ifr.ifr_name));
+ flags = (my_ifr.ifr_flags & 0xffff) | (my_ifr.ifr_flagshigh << 16);
+
+ if (value < 0) {
+ value = -value;
+ flags &= ~value;
+ } else
+ flags |= value;
+ my_ifr.ifr_flags = flags & 0xffff;
+ my_ifr.ifr_flagshigh = flags >> 16;
+ if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0)
+ Perror(vname);
+}
+
+void
+setifcap(const char *vname, int value, int s, const struct afswtch *afp)
+{
+
+ if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) < 0) {
+ Perror("ioctl (SIOCGIFCAP)");
+ exit(1);
+ }
+ flags = ifr.ifr_curcap;
+ if (value < 0) {
+ value = -value;
+ flags &= ~value;
+ } else
+ flags |= value;
+ ifr.ifr_reqcap = flags;
+ if (ioctl(s, SIOCSIFCAP, (caddr_t)&ifr) < 0)
+ Perror(vname);
+}
+
+static void
+setifmetric(const char *val, int dummy __unused, int s,
+ const struct afswtch *afp)
+{
+ strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
+ ifr.ifr_metric = atoi(val);
+ if (ioctl(s, SIOCSIFMETRIC, (caddr_t)&ifr) < 0)
+ warn("ioctl (set metric)");
+}
+
+static void
+setifmtu(const char *val, int dummy __unused, int s,
+ const struct afswtch *afp)
+{
+ strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
+ ifr.ifr_mtu = atoi(val);
+ if (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr) < 0)
+ warn("ioctl (set mtu)");
+}
+
+static void
+setifname(const char *val, int dummy __unused, int s,
+ const struct afswtch *afp)
+{
+ char *newname;
+
+ newname = strdup(val);
+ if (newname == NULL) {
+ warn("no memory to set ifname");
+ return;
+ }
+ ifr.ifr_data = newname;
+ if (ioctl(s, SIOCSIFNAME, (caddr_t)&ifr) < 0) {
+ warn("ioctl (set name)");
+ free(newname);
+ return;
+ }
+ strlcpy(name, newname, sizeof(name));
+ free(newname);
+
+ /*
+ * Even if we just created the interface, we don't need to print
+ * its name because we just nailed it down separately.
+ */
+ printname = 0;
+}
+
+/*
+ * Expand the compacted form of addresses as returned via the
+ * configuration read via sysctl().
+ */
+static void
+rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo)
+{
+ struct sockaddr *sa;
+ int i;
+
+ memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
+ for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
+ if ((rtinfo->rti_addrs & (1 << i)) == 0)
+ continue;
+ rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
+ cp += SA_SIZE(sa);
+ }
+}
+
+#define IFFBITS \
+"\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6SMART\7RUNNING" \
+"\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \
+"\20MULTICAST\21POLLING\22PPROMISC\23MONITOR\24STATICARP\25NEEDSGIANT"
+
+#define IFCAPBITS \
+"\020\1RXCSUM\2TXCSUM\3NETCONS\4VLAN_MTU\5VLAN_HWTAGGING\6JUMBO_MTU\7POLLING" \
+"\10VLAN_HWCSUM"
+
+/*
+ * Print the status of the interface. If an address family was
+ * specified, show only it; otherwise, show them all.
+ */
+static void
+status(const struct afswtch *afp, int addrcount, struct sockaddr_dl *sdl,
+ struct if_msghdr *ifm, struct ifa_msghdr *ifam)
+{
+ struct rt_addrinfo info;
+ int allfamilies, s;
+ struct ifstat ifs;
+
+ if (afp == NULL) {
+ allfamilies = 1;
+ afp = af_getbyname("inet");
+ } else
+ allfamilies = 0;
+
+ ifr.ifr_addr.sa_family = afp->af_af == AF_LINK ? AF_INET : afp->af_af;
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+
+ s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0);
+ if (s < 0)
+ err(1, "socket(family %u,SOCK_DGRAM)", ifr.ifr_addr.sa_family);
+
+ printf("%s: ", name);
+ printb("flags", flags, IFFBITS);
+ if (ifm->ifm_data.ifi_metric)
+ printf(" metric %ld", ifm->ifm_data.ifi_metric);
+ if (ifm->ifm_data.ifi_mtu)
+ printf(" mtu %ld", ifm->ifm_data.ifi_mtu);
+ putchar('\n');
+
+ if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) == 0) {
+ if (ifr.ifr_curcap != 0) {
+ printb("\toptions", ifr.ifr_curcap, IFCAPBITS);
+ putchar('\n');
+ }
+ if (supmedia && ifr.ifr_reqcap != 0) {
+ printb("\tcapabilities", ifr.ifr_reqcap, IFCAPBITS);
+ putchar('\n');
+ }
+ }
+
+ tunnel_status(s);
+
+ while (addrcount > 0) {
+ info.rti_addrs = ifam->ifam_addrs;
+ /* Expand the compacted addresses */
+ rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam,
+ &info);
+
+ if (allfamilies) {
+ const struct afswtch *p;
+ p = af_getbyfamily(info.rti_info[RTAX_IFA]->sa_family);
+ if (p != NULL && p->af_status != NULL)
+ p->af_status(s, &info);
+ } else if (afp->af_af == info.rti_info[RTAX_IFA]->sa_family)
+ afp->af_status(s, &info);
+ addrcount--;
+ ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen);
+ }
+ if (allfamilies || afp->af_af == AF_LINK) {
+ const struct afswtch *lafp;
+
+ /*
+ * Hack; the link level address is received separately
+ * from the routing information so any address is not
+ * handled above. Cobble together an entry and invoke
+ * the status method specially.
+ */
+ lafp = af_getbyname("lladdr");
+ if (lafp != NULL) {
+ info.rti_info[RTAX_IFA] = (struct sockaddr *)sdl;
+ lafp->af_status(s, &info);
+ }
+ }
+ if (allfamilies)
+ af_other_status(s);
+ else if (afp->af_other_status != NULL)
+ afp->af_other_status(s);
+
+ strncpy(ifs.ifs_name, name, sizeof ifs.ifs_name);
+ if (ioctl(s, SIOCGIFSTATUS, &ifs) == 0)
+ printf("%s", ifs.ascii);
+
+ close(s);
+ return;
+}
+
+static void
+tunnel_status(int s)
+{
+ af_all_tunnel_status(s);
+}
+
+void
+Perror(const char *cmd)
+{
+ switch (errno) {
+
+ case ENXIO:
+ errx(1, "%s: no such interface", cmd);
+ break;
+
+ case EPERM:
+ errx(1, "%s: permission denied", cmd);
+ break;
+
+ default:
+ err(1, "%s", cmd);
+ }
+}
+
+/*
+ * Print a value a la the %b format of the kernel's printf
+ */
+void
+printb(const char *s, unsigned v, const char *bits)
+{
+ int i, any = 0;
+ char c;
+
+ if (bits && *bits == 8)
+ printf("%s=%o", s, v);
+ else
+ printf("%s=%x", s, v);
+ bits++;
+ if (bits) {
+ putchar('<');
+ while ((i = *bits++) != '\0') {
+ if (v & (1 << (i-1))) {
+ if (any)
+ putchar(',');
+ any = 1;
+ for (; (c = *bits) > 32; bits++)
+ putchar(c);
+ } else
+ for (; *bits > 32; bits++)
+ ;
+ }
+ putchar('>');
+ }
+}
+
+void
+ifmaybeload(char *name)
+{
+ struct module_stat mstat;
+ int fileid, modid;
+ char ifkind[35], *cp, *dp;
+
+ /* turn interface and unit into module name */
+ strcpy(ifkind, "if_");
+ for (cp = name, dp = ifkind + 3;
+ (*cp != 0) && !isdigit(*cp); cp++, dp++)
+ *dp = *cp;
+ *dp = 0;
+
+ /* scan files in kernel */
+ mstat.version = sizeof(struct module_stat);
+ for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) {
+ /* scan modules in file */
+ for (modid = kldfirstmod(fileid); modid > 0;
+ modid = modfnext(modid)) {
+ if (modstat(modid, &mstat) < 0)
+ continue;
+ /* strip bus name if present */
+ if ((cp = strchr(mstat.name, '/')) != NULL) {
+ cp++;
+ } else {
+ cp = mstat.name;
+ }
+ /* already loaded? */
+ if (strncmp(name, cp, strlen(cp)) == 0 ||
+ strncmp(ifkind, cp, strlen(cp)) == 0)
+ return;
+ }
+ }
+
+ /* not present, we should try to load it */
+ kldload(ifkind);
+}
+
+static struct cmd basic_cmds[] = {
+ DEF_CMD("up", IFF_UP, setifflags),
+ DEF_CMD("down", -IFF_UP, setifflags),
+ DEF_CMD("arp", -IFF_NOARP, setifflags),
+ DEF_CMD("-arp", IFF_NOARP, setifflags),
+ DEF_CMD("debug", IFF_DEBUG, setifflags),
+ DEF_CMD("-debug", -IFF_DEBUG, setifflags),
+ DEF_CMD("promisc", IFF_PPROMISC, setifflags),
+ DEF_CMD("-promisc", -IFF_PPROMISC, setifflags),
+ DEF_CMD("add", IFF_UP, notealias),
+ DEF_CMD("alias", IFF_UP, notealias),
+ DEF_CMD("-alias", -IFF_UP, notealias),
+ DEF_CMD("delete", -IFF_UP, notealias),
+ DEF_CMD("remove", -IFF_UP, notealias),
+#ifdef notdef
+#define EN_SWABIPS 0x1000
+ DEF_CMD("swabips", EN_SWABIPS, setifflags),
+ DEF_CMD("-swabips", -EN_SWABIPS, setifflags),
+#endif
+ DEF_CMD_ARG("netmask", setifnetmask),
+ DEF_CMD_ARG("metric", setifmetric),
+ DEF_CMD_ARG("broadcast", setifbroadaddr),
+ DEF_CMD_ARG("ipdst", setifipdst),
+ DEF_CMD_ARG2("tunnel", settunnel),
+ DEF_CMD("-tunnel", 0, deletetunnel),
+ DEF_CMD("deletetunnel", 0, deletetunnel),
+ DEF_CMD("link0", IFF_LINK0, setifflags),
+ DEF_CMD("-link0", -IFF_LINK0, setifflags),
+ DEF_CMD("link1", IFF_LINK1, setifflags),
+ DEF_CMD("-link1", -IFF_LINK1, setifflags),
+ DEF_CMD("link2", IFF_LINK2, setifflags),
+ DEF_CMD("-link2", -IFF_LINK2, setifflags),
+ DEF_CMD("monitor", IFF_MONITOR, setifflags),
+ DEF_CMD("-monitor", -IFF_MONITOR, setifflags),
+ DEF_CMD("staticarp", IFF_STATICARP, setifflags),
+ DEF_CMD("-staticarp", -IFF_STATICARP, setifflags),
+ DEF_CMD("rxcsum", IFCAP_RXCSUM, setifcap),
+ DEF_CMD("-rxcsum", -IFCAP_RXCSUM, setifcap),
+ DEF_CMD("txcsum", IFCAP_TXCSUM, setifcap),
+ DEF_CMD("-txcsum", -IFCAP_TXCSUM, setifcap),
+ DEF_CMD("netcons", IFCAP_NETCONS, setifcap),
+ DEF_CMD("-netcons", -IFCAP_NETCONS, setifcap),
+ DEF_CMD("polling", IFCAP_POLLING, setifcap),
+ DEF_CMD("-polling", -IFCAP_POLLING, setifcap),
+ DEF_CMD("normal", -IFF_LINK0, setifflags),
+ DEF_CMD("compress", IFF_LINK0, setifflags),
+ DEF_CMD("noicmp", IFF_LINK1, setifflags),
+ DEF_CMD_ARG("mtu", setifmtu),
+ DEF_CMD_ARG("name", setifname),
+};
+
+static __constructor void
+ifconfig_ctor(void)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ int i;
+
+ for (i = 0; i < N(basic_cmds); i++)
+ cmd_register(&basic_cmds[i]);
+#undef N
+}
diff --git a/sbin/ifconfig/ifconfig.h b/sbin/ifconfig/ifconfig.h
new file mode 100644
index 0000000..dc68a81
--- /dev/null
+++ b/sbin/ifconfig/ifconfig.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1997 Peter Wemm.
+ * 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 for the FreeBSD Project
+ * by Peter Wemm.
+ * 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.
+ *
+ * so there!
+ *
+ * $FreeBSD$
+ */
+
+#define __constructor __attribute__((constructor))
+
+struct afswtch;
+struct cmd;
+
+typedef void c_func(const char *cmd, int arg, int s, const struct afswtch *afp);
+typedef void c_func2(const char *arg1, const char *arg2, int s, const struct afswtch *afp);
+
+struct cmd {
+ const char *c_name;
+ int c_parameter;
+#define NEXTARG 0xffffff /* has following arg */
+#define NEXTARG2 0xfffffe /* has 2 following args */
+#define OPTARG 0xfffffd /* has optional following arg */
+ union {
+ c_func *c_func;
+ c_func2 *c_func2;
+ } c_u;
+ struct cmd *c_next;
+};
+void cmd_register(struct cmd *);
+
+typedef void callback_func(int s, void *);
+void callback_register(callback_func *, void *);
+
+/*
+ * Macros for declaring command functions and initializing entries.
+ */
+#define DECL_CMD_FUNC(name, cmd, arg) \
+ void name(const char *cmd, int arg, int s, const struct afswtch *afp)
+#define DECL_CMD_FUNC2(name, arg1, arg2) \
+ void name(const char *arg1, const char *arg2, int s, const struct afswtch *afp)
+
+#define DEF_CMD(name, param, func) { name, param, { .c_func = func } }
+#define DEF_CMD_ARG(name, func) { name, NEXTARG, { .c_func = func } }
+#define DEF_CMD_OPTARG(name, func) { name, OPTARG, { .c_func = func } }
+#define DEF_CMD_ARG2(name, func) { name, NEXTARG2, { .c_func2 = func } }
+
+struct rt_addrinfo;
+struct addrinfo;
+
+enum {
+ RIDADDR,
+ ADDR,
+ MASK,
+ DSTADDR,
+};
+
+struct afswtch {
+ const char *af_name; /* as given on cmd line, e.g. "inet" */
+ short af_af; /* AF_* */
+ /*
+ * Status is handled one of two ways; if there is an
+ * address associated with the interface then the
+ * associated address family af_status method is invoked
+ * with the appropriate addressin info. Otherwise, if
+ * all possible info is to be displayed and af_other_status
+ * is defined then it is invoked after all address status
+ * is presented.
+ */
+ void (*af_status)(int, const struct rt_addrinfo *);
+ void (*af_other_status)(int);
+ /* parse address method */
+ void (*af_getaddr)(const char *, int);
+ /* parse prefix method (IPv6) */
+ void (*af_getprefix)(const char *, int);
+ void (*af_postproc)(int s, const struct afswtch *);
+ u_long af_difaddr; /* set dst if address ioctl */
+ u_long af_aifaddr; /* set if address ioctl */
+ void *af_ridreq; /* */
+ void *af_addreq; /* */
+ struct afswtch *af_next;
+
+ /* XXX doesn't fit model */
+ void (*af_status_tunnel)(int);
+ void (*af_settunnel)(int s, struct addrinfo *srcres,
+ struct addrinfo *dstres);
+};
+void af_register(struct afswtch *);
+
+struct option {
+ const char *opt;
+ const char *opt_usage;
+ void (*cb)(const char *arg);
+ struct option *next;
+};
+void opt_register(struct option *);
+
+extern struct ifreq ifr;
+extern char name[IFNAMSIZ]; /* name of interface */
+extern int allmedia;
+extern int supmedia;
+extern int printkeys;
+extern int printname;
+extern int flags;
+extern int newaddr;
+extern int verbose;
+extern int setipdst;
+
+void setifcap(const char *, int value, int s, const struct afswtch *);
+
+void Perror(const char *cmd);
+void printb(const char *s, unsigned value, const char *bits);
+
+void ifmaybeload(char *name);
+
+void clone_create(void);
diff --git a/sbin/ifconfig/ifieee80211.c b/sbin/ifconfig/ifieee80211.c
new file mode 100644
index 0000000..d45a457
--- /dev/null
+++ b/sbin/ifconfig/ifieee80211.c
@@ -0,0 +1,1970 @@
+/*
+ * Copyright 2001 The Aerospace Corporation. 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 Aerospace Corporation may not be used to endorse or
+ * promote products derived from this software.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION ``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 AEROSPACE CORPORATION 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.
+ *
+ * $FreeBSD$
+ */
+
+/*-
+ * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+ * NASA Ames Research Center.
+ *
+ * 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 the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 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 <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/if_media.h>
+#include <net/route.h>
+
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_crypto.h>
+#include <net80211/ieee80211_ioctl.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ifconfig.h"
+
+static void set80211(int s, int type, int val, int len, u_int8_t *data);
+static const char *get_string(const char *val, const char *sep,
+ u_int8_t *buf, int *lenp);
+static void print_string(const u_int8_t *buf, int len);
+
+static int
+isanyarg(const char *arg)
+{
+ return (strcmp(arg, "-") == 0 ||
+ strcasecmp(arg, "any") == 0 || strcasecmp(arg, "off") == 0);
+}
+
+static void
+set80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ int ssid;
+ int len;
+ u_int8_t data[IEEE80211_NWID_LEN];
+
+ ssid = 0;
+ len = strlen(val);
+ if (len > 2 && isdigit(val[0]) && val[1] == ':') {
+ ssid = atoi(val)-1;
+ val += 2;
+ }
+
+ bzero(data, sizeof(data));
+ len = sizeof(data);
+ if (get_string(val, NULL, data, &len) == NULL)
+ exit(1);
+
+ set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
+}
+
+static void
+set80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ int len;
+ u_int8_t data[33];
+
+ bzero(data, sizeof(data));
+ len = sizeof(data);
+ get_string(val, NULL, data, &len);
+
+ set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
+}
+
+/*
+ * Convert IEEE channel number to MHz frequency.
+ */
+static u_int
+ieee80211_ieee2mhz(u_int chan)
+{
+ if (chan == 14)
+ return 2484;
+ if (chan < 14) /* 0-13 */
+ return 2407 + chan*5;
+ if (chan < 27) /* 15-26 */
+ return 2512 + ((chan-15)*20);
+ return 5000 + (chan*5);
+}
+
+/*
+ * Convert MHz frequency to IEEE channel number.
+ */
+static u_int
+ieee80211_mhz2ieee(u_int freq)
+{
+ if (freq == 2484)
+ return 14;
+ if (freq < 2484)
+ return (freq - 2407) / 5;
+ if (freq < 5000)
+ return 15 + ((freq - 2512) / 20);
+ return (freq - 5000) / 5;
+}
+
+static void
+set80211channel(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ if (!isanyarg(val)) {
+ int v = atoi(val);
+ if (v > 255) /* treat as frequency */
+ v = ieee80211_mhz2ieee(v);
+ set80211(s, IEEE80211_IOC_CHANNEL, v, 0, NULL);
+ } else
+ set80211(s, IEEE80211_IOC_CHANNEL, IEEE80211_CHAN_ANY, 0, NULL);
+}
+
+static void
+set80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ int mode;
+
+ if (strcasecmp(val, "none") == 0) {
+ mode = IEEE80211_AUTH_NONE;
+ } else if (strcasecmp(val, "open") == 0) {
+ mode = IEEE80211_AUTH_OPEN;
+ } else if (strcasecmp(val, "shared") == 0) {
+ mode = IEEE80211_AUTH_SHARED;
+ } else if (strcasecmp(val, "8021x") == 0) {
+ mode = IEEE80211_AUTH_8021X;
+ } else if (strcasecmp(val, "wpa") == 0) {
+ mode = IEEE80211_AUTH_WPA;
+ } else {
+ errx(1, "unknown authmode");
+ }
+
+ set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
+}
+
+static void
+set80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ int mode;
+
+ if (strcasecmp(val, "off") == 0) {
+ mode = IEEE80211_POWERSAVE_OFF;
+ } else if (strcasecmp(val, "on") == 0) {
+ mode = IEEE80211_POWERSAVE_ON;
+ } else if (strcasecmp(val, "cam") == 0) {
+ mode = IEEE80211_POWERSAVE_CAM;
+ } else if (strcasecmp(val, "psp") == 0) {
+ mode = IEEE80211_POWERSAVE_PSP;
+ } else if (strcasecmp(val, "psp-cam") == 0) {
+ mode = IEEE80211_POWERSAVE_PSP_CAM;
+ } else {
+ errx(1, "unknown powersavemode");
+ }
+
+ set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
+}
+
+static void
+set80211powersave(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ if (d == 0)
+ set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
+ 0, NULL);
+ else
+ set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON,
+ 0, NULL);
+}
+
+static void
+set80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
+}
+
+static void
+set80211wepmode(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ int mode;
+
+ if (strcasecmp(val, "off") == 0) {
+ mode = IEEE80211_WEP_OFF;
+ } else if (strcasecmp(val, "on") == 0) {
+ mode = IEEE80211_WEP_ON;
+ } else if (strcasecmp(val, "mixed") == 0) {
+ mode = IEEE80211_WEP_MIXED;
+ } else {
+ errx(1, "unknown wep mode");
+ }
+
+ set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
+}
+
+static void
+set80211wep(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
+}
+
+static int
+isundefarg(const char *arg)
+{
+ return (strcmp(arg, "-") == 0 || strncasecmp(arg, "undef", 5) == 0);
+}
+
+static void
+set80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ if (isundefarg(val))
+ set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL);
+ else
+ set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
+}
+
+static void
+set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ int key = 0;
+ int len;
+ u_int8_t data[IEEE80211_KEYBUF_SIZE];
+
+ if (isdigit(val[0]) && val[1] == ':') {
+ key = atoi(val)-1;
+ val += 2;
+ }
+
+ bzero(data, sizeof(data));
+ len = sizeof(data);
+ get_string(val, NULL, data, &len);
+
+ set80211(s, IEEE80211_IOC_WEPKEY, key, len, data);
+}
+
+/*
+ * This function is purely a NetBSD compatability interface. The NetBSD
+ * interface is too inflexible, but it's there so we'll support it since
+ * it's not all that hard.
+ */
+static void
+set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ int txkey;
+ int i, len;
+ u_int8_t data[IEEE80211_KEYBUF_SIZE];
+
+ set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
+
+ if (isdigit(val[0]) && val[1] == ':') {
+ txkey = val[0]-'0'-1;
+ val += 2;
+
+ for (i = 0; i < 4; i++) {
+ bzero(data, sizeof(data));
+ len = sizeof(data);
+ val = get_string(val, ",", data, &len);
+ if (val == NULL)
+ exit(1);
+
+ set80211(s, IEEE80211_IOC_WEPKEY, i, len, data);
+ }
+ } else {
+ bzero(data, sizeof(data));
+ len = sizeof(data);
+ get_string(val, NULL, data, &len);
+ txkey = 0;
+
+ set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data);
+
+ bzero(data, sizeof(data));
+ for (i = 1; i < 4; i++)
+ set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data);
+ }
+
+ set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
+}
+
+static void
+set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ set80211(s, IEEE80211_IOC_RTSTHRESHOLD,
+ isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL);
+}
+
+static void
+set80211protmode(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ int mode;
+
+ if (strcasecmp(val, "off") == 0) {
+ mode = IEEE80211_PROTMODE_OFF;
+ } else if (strcasecmp(val, "cts") == 0) {
+ mode = IEEE80211_PROTMODE_CTS;
+ } else if (strcasecmp(val, "rtscts") == 0) {
+ mode = IEEE80211_PROTMODE_RTSCTS;
+ } else {
+ errx(1, "unknown protection mode");
+ }
+
+ set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
+}
+
+static void
+set80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ set80211(s, IEEE80211_IOC_TXPOWER, atoi(val), 0, NULL);
+}
+
+#define IEEE80211_ROAMING_DEVICE 0
+#define IEEE80211_ROAMING_AUTO 1
+#define IEEE80211_ROAMING_MANUAL 2
+
+static void
+set80211roaming(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ int mode;
+
+ if (strcasecmp(val, "device") == 0) {
+ mode = IEEE80211_ROAMING_DEVICE;
+ } else if (strcasecmp(val, "auto") == 0) {
+ mode = IEEE80211_ROAMING_AUTO;
+ } else if (strcasecmp(val, "manual") == 0) {
+ mode = IEEE80211_ROAMING_MANUAL;
+ } else {
+ errx(1, "unknown roaming mode");
+ }
+ set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL);
+}
+
+static void
+set80211wme(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ set80211(s, IEEE80211_IOC_WME, d, 0, NULL);
+}
+
+static void
+set80211hidessid(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL);
+}
+
+static void
+set80211apbridge(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL);
+}
+
+static void
+set80211chanlist(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ struct ieee80211req_chanlist chanlist;
+#define MAXCHAN (sizeof(chanlist.ic_channels)*NBBY)
+ char *temp, *cp, *tp;
+
+ temp = malloc(strlen(val) + 1);
+ if (temp == NULL)
+ errx(1, "malloc failed");
+ strcpy(temp, val);
+ memset(&chanlist, 0, sizeof(chanlist));
+ cp = temp;
+ for (;;) {
+ int first, last, f;
+
+ tp = strchr(cp, ',');
+ if (tp != NULL)
+ *tp++ = '\0';
+ switch (sscanf(cp, "%u-%u", &first, &last)) {
+ case 1:
+ if (first > MAXCHAN)
+ errx(-1, "channel %u out of range, max %zu",
+ first, MAXCHAN);
+ setbit(chanlist.ic_channels, first);
+ break;
+ case 2:
+ if (first > MAXCHAN)
+ errx(-1, "channel %u out of range, max %zu",
+ first, MAXCHAN);
+ if (last > MAXCHAN)
+ errx(-1, "channel %u out of range, max %zu",
+ last, MAXCHAN);
+ if (first > last)
+ errx(-1, "void channel range, %u > %u",
+ first, last);
+ for (f = first; f <= last; f++)
+ setbit(chanlist.ic_channels, f);
+ break;
+ }
+ if (tp == NULL)
+ break;
+ while (isspace(*tp))
+ tp++;
+ if (!isdigit(*tp))
+ break;
+ cp = tp;
+ }
+ set80211(s, IEEE80211_IOC_CHANLIST, 0,
+ sizeof(chanlist), (uint8_t *) &chanlist);
+#undef MAXCHAN
+}
+
+static void
+set80211bssid(const char *val, int d, int s, const struct afswtch *rafp)
+{
+
+ if (!isanyarg(val)) {
+ char *temp;
+ struct sockaddr_dl sdl;
+
+ temp = malloc(strlen(val) + 2);
+ if (temp == NULL)
+ errx(1, "malloc failed");
+ temp[0] = ':';
+ strcpy(temp + 1, val);
+ sdl.sdl_len = sizeof(sdl);
+ link_addr(temp, &sdl);
+ free(temp);
+ if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
+ errx(1, "malformed link-level address");
+ set80211(s, IEEE80211_IOC_BSSID, 0,
+ IEEE80211_ADDR_LEN, LLADDR(&sdl));
+ } else {
+ uint8_t zerobssid[IEEE80211_ADDR_LEN];
+ memset(zerobssid, 0, sizeof(zerobssid));
+ set80211(s, IEEE80211_IOC_BSSID, 0,
+ IEEE80211_ADDR_LEN, zerobssid);
+ }
+}
+
+static int
+getac(const char *ac)
+{
+ if (strcasecmp(ac, "ac_be") == 0 || strcasecmp(ac, "be") == 0)
+ return WME_AC_BE;
+ if (strcasecmp(ac, "ac_bk") == 0 || strcasecmp(ac, "bk") == 0)
+ return WME_AC_BK;
+ if (strcasecmp(ac, "ac_vi") == 0 || strcasecmp(ac, "vi") == 0)
+ return WME_AC_VI;
+ if (strcasecmp(ac, "ac_vo") == 0 || strcasecmp(ac, "vo") == 0)
+ return WME_AC_VO;
+ errx(1, "unknown wme access class %s", ac);
+}
+
+static
+DECL_CMD_FUNC2(set80211cwmin, ac, val)
+{
+ set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL);
+}
+
+static
+DECL_CMD_FUNC2(set80211cwmax, ac, val)
+{
+ set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL);
+}
+
+static
+DECL_CMD_FUNC2(set80211aifs, ac, val)
+{
+ set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL);
+}
+
+static
+DECL_CMD_FUNC2(set80211txoplimit, ac, val)
+{
+ set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211acm, ac, d)
+{
+ set80211(s, IEEE80211_IOC_WME_ACM, 1, getac(ac), NULL);
+}
+static
+DECL_CMD_FUNC(set80211noacm, ac, d)
+{
+ set80211(s, IEEE80211_IOC_WME_ACM, 0, getac(ac), NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211ackpolicy, ac, d)
+{
+ set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 1, getac(ac), NULL);
+}
+static
+DECL_CMD_FUNC(set80211noackpolicy, ac, d)
+{
+ set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 0, getac(ac), NULL);
+}
+
+static
+DECL_CMD_FUNC2(set80211bsscwmin, ac, val)
+{
+ set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val),
+ getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
+}
+
+static
+DECL_CMD_FUNC2(set80211bsscwmax, ac, val)
+{
+ set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val),
+ getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
+}
+
+static
+DECL_CMD_FUNC2(set80211bssaifs, ac, val)
+{
+ set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val),
+ getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
+}
+
+static
+DECL_CMD_FUNC2(set80211bsstxoplimit, ac, val)
+{
+ set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val),
+ getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211dtimperiod, val, d)
+{
+ set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211bintval, val, d)
+{
+ set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL);
+}
+
+static void
+set80211macmac(int s, int op, const char *val)
+{
+ char *temp;
+ struct sockaddr_dl sdl;
+
+ temp = malloc(strlen(val) + 1);
+ if (temp == NULL)
+ errx(1, "malloc failed");
+ temp[0] = ':';
+ strcpy(temp + 1, val);
+ sdl.sdl_len = sizeof(sdl);
+ link_addr(temp, &sdl);
+ free(temp);
+ if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
+ errx(1, "malformed link-level address");
+ set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl));
+}
+
+static
+DECL_CMD_FUNC(set80211addmac, val, d)
+{
+ set80211macmac(s, IEEE80211_IOC_ADDMAC, val);
+}
+
+static
+DECL_CMD_FUNC(set80211delmac, val, d)
+{
+ set80211macmac(s, IEEE80211_IOC_DELMAC, val);
+}
+
+static
+DECL_CMD_FUNC(set80211kickmac, val, d)
+{
+ char *temp;
+ struct sockaddr_dl sdl;
+ struct ieee80211req_mlme mlme;
+
+ temp = malloc(strlen(val) + 1);
+ if (temp == NULL)
+ errx(1, "malloc failed");
+ temp[0] = ':';
+ strcpy(temp + 1, val);
+ sdl.sdl_len = sizeof(sdl);
+ link_addr(temp, &sdl);
+ free(temp);
+ if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
+ errx(1, "malformed link-level address");
+ memset(&mlme, 0, sizeof(mlme));
+ mlme.im_op = IEEE80211_MLME_DEAUTH;
+ mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE;
+ memcpy(mlme.im_macaddr, LLADDR(&sdl), IEEE80211_ADDR_LEN);
+ set80211(s, IEEE80211_IOC_MLME, 0, sizeof(mlme), (u_int8_t *) &mlme);
+}
+
+static
+DECL_CMD_FUNC(set80211maccmd, val, d)
+{
+ set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL);
+}
+
+static void
+set80211pureg(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL);
+}
+
+static void
+set80211burst(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ set80211(s, IEEE80211_IOC_BURST, d, 0, NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211mcastrate, val, d)
+{
+ set80211(s, IEEE80211_IOC_MCAST_RATE, (int) 2*atof(val), 0, NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211fragthreshold, val, d)
+{
+ set80211(s, IEEE80211_IOC_FRAGTHRESHOLD,
+ isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL);
+}
+
+static int
+getmaxrate(uint8_t rates[15], uint8_t nrates)
+{
+ int i, maxrate = -1;
+
+ for (i = 0; i < nrates; i++) {
+ int rate = rates[i] & IEEE80211_RATE_VAL;
+ if (rate > maxrate)
+ maxrate = rate;
+ }
+ return maxrate / 2;
+}
+
+static const char *
+getcaps(int capinfo)
+{
+ static char capstring[32];
+ char *cp = capstring;
+
+ if (capinfo & IEEE80211_CAPINFO_ESS)
+ *cp++ = 'E';
+ if (capinfo & IEEE80211_CAPINFO_IBSS)
+ *cp++ = 'I';
+ if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
+ *cp++ = 'c';
+ if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
+ *cp++ = 'C';
+ if (capinfo & IEEE80211_CAPINFO_PRIVACY)
+ *cp++ = 'P';
+ if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
+ *cp++ = 'S';
+ if (capinfo & IEEE80211_CAPINFO_PBCC)
+ *cp++ = 'B';
+ if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
+ *cp++ = 'A';
+ if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
+ *cp++ = 's';
+ if (capinfo & IEEE80211_CAPINFO_RSN)
+ *cp++ = 'R';
+ if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
+ *cp++ = 'D';
+ *cp = '\0';
+ return capstring;
+}
+
+static void
+printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
+{
+ printf("%s", tag);
+ if (verbose) {
+ maxlen -= strlen(tag)+2;
+ if (2*ielen > maxlen)
+ maxlen--;
+ printf("<");
+ for (; ielen > 0; ie++, ielen--) {
+ if (maxlen-- <= 0)
+ break;
+ printf("%02x", *ie);
+ }
+ if (ielen != 0)
+ printf("-");
+ printf(">");
+ }
+}
+
+/*
+ * Copy the ssid string contents into buf, truncating to fit. If the
+ * ssid is entirely printable then just copy intact. Otherwise convert
+ * to hexadecimal. If the result is truncated then replace the last
+ * three characters with "...".
+ */
+static int
+copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
+{
+ const u_int8_t *p;
+ size_t maxlen;
+ int i;
+
+ if (essid_len > bufsize)
+ maxlen = bufsize;
+ else
+ maxlen = essid_len;
+ /* determine printable or not */
+ for (i = 0, p = essid; i < maxlen; i++, p++) {
+ if (*p < ' ' || *p > 0x7e)
+ break;
+ }
+ if (i != maxlen) { /* not printable, print as hex */
+ if (bufsize < 3)
+ return 0;
+ strlcpy(buf, "0x", bufsize);
+ bufsize -= 2;
+ p = essid;
+ for (i = 0; i < maxlen && bufsize >= 2; i++) {
+ sprintf(&buf[2+2*i], "%02x", p[i]);
+ bufsize -= 2;
+ }
+ if (i != essid_len)
+ memcpy(&buf[2+2*i-3], "...", 3);
+ } else { /* printable, truncate as needed */
+ memcpy(buf, essid, maxlen);
+ if (maxlen != essid_len)
+ memcpy(&buf[maxlen-3], "...", 3);
+ }
+ return maxlen;
+}
+
+/* unaligned little endian access */
+#define LE_READ_4(p) \
+ ((u_int32_t) \
+ ((((const u_int8_t *)(p))[0] ) | \
+ (((const u_int8_t *)(p))[1] << 8) | \
+ (((const u_int8_t *)(p))[2] << 16) | \
+ (((const u_int8_t *)(p))[3] << 24)))
+
+static int __inline
+iswpaoui(const u_int8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
+}
+
+static int __inline
+iswmeoui(const u_int8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
+}
+
+static int __inline
+isatherosoui(const u_int8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
+}
+
+static void
+printies(const u_int8_t *vp, int ielen, int maxcols)
+{
+ while (ielen > 0) {
+ switch (vp[0]) {
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswpaoui(vp))
+ printie(" WPA", vp, 2+vp[1], maxcols);
+ else if (iswmeoui(vp))
+ printie(" WME", vp, 2+vp[1], maxcols);
+ else if (isatherosoui(vp))
+ printie(" ATH", vp, 2+vp[1], maxcols);
+ else
+ printie(" VEN", vp, 2+vp[1], maxcols);
+ break;
+ case IEEE80211_ELEMID_RSN:
+ printie(" RSN", vp, 2+vp[1], maxcols);
+ break;
+ default:
+ printie(" ???", vp, 2+vp[1], maxcols);
+ break;
+ }
+ ielen -= 2+vp[1];
+ vp += 2+vp[1];
+ }
+}
+
+static void
+list_scan(int s)
+{
+ uint8_t buf[24*1024];
+ struct ieee80211req ireq;
+ char ssid[IEEE80211_NWID_LEN+1];
+ uint8_t *cp;
+ int len, ssidmax;
+
+ (void) memset(&ireq, 0, sizeof(ireq));
+ (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+ ireq.i_type = IEEE80211_IOC_SCAN_RESULTS;
+ ireq.i_data = buf;
+ ireq.i_len = sizeof(buf);
+ if (ioctl(s, SIOCG80211, &ireq) < 0)
+ errx(1, "unable to get scan results");
+ len = ireq.i_len;
+ if (len < sizeof(struct ieee80211req_scan_result))
+ return;
+
+ ssidmax = verbose ? IEEE80211_NWID_LEN : 14;
+ printf("%-*.*s %-17.17s %4s %4s %-5s %3s %4s\n"
+ , ssidmax, ssidmax, "SSID"
+ , "BSSID"
+ , "CHAN"
+ , "RATE"
+ , "S:N"
+ , "INT"
+ , "CAPS"
+ );
+ cp = buf;
+ do {
+ struct ieee80211req_scan_result *sr;
+ uint8_t *vp;
+
+ sr = (struct ieee80211req_scan_result *) cp;
+ vp = (u_int8_t *)(sr+1);
+ printf("%-*.*s %s %3d %3dM %2d:%-2d %3d %-4.4s"
+ , ssidmax
+ , copy_essid(ssid, sizeof(ssid), vp, sr->isr_ssid_len)
+ , ssid
+ , ether_ntoa((const struct ether_addr *) sr->isr_bssid)
+ , ieee80211_mhz2ieee(sr->isr_freq)
+ , getmaxrate(sr->isr_rates, sr->isr_nrates)
+ , sr->isr_rssi, sr->isr_noise
+ , sr->isr_intval
+ , getcaps(sr->isr_capinfo)
+ );
+ printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);;
+ printf("\n");
+ cp += sr->isr_len, len -= sr->isr_len;
+ } while (len >= sizeof(struct ieee80211req_scan_result));
+}
+
+#include <net80211/ieee80211_freebsd.h>
+
+static void
+scan_and_wait(int s)
+{
+ struct ieee80211req ireq;
+ int sroute;
+
+ sroute = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (sroute < 0) {
+ perror("socket(PF_ROUTE,SOCK_RAW)");
+ return;
+ }
+ (void) memset(&ireq, 0, sizeof(ireq));
+ (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+ ireq.i_type = IEEE80211_IOC_SCAN_REQ;
+ /* NB: only root can trigger a scan so ignore errors */
+ if (ioctl(s, SIOCS80211, &ireq) >= 0) {
+ char buf[2048];
+ struct if_announcemsghdr *ifan;
+ struct rt_msghdr *rtm;
+
+ do {
+ if (read(sroute, buf, sizeof(buf)) < 0) {
+ perror("read(PF_ROUTE)");
+ break;
+ }
+ rtm = (struct rt_msghdr *) buf;
+ if (rtm->rtm_version != RTM_VERSION)
+ break;
+ ifan = (struct if_announcemsghdr *) rtm;
+ } while (rtm->rtm_type != RTM_IEEE80211 ||
+ ifan->ifan_what != RTM_IEEE80211_SCAN);
+ }
+ close(sroute);
+}
+
+static
+DECL_CMD_FUNC(set80211scan, val, d)
+{
+ scan_and_wait(s);
+ list_scan(s);
+}
+
+static void
+list_stations(int s)
+{
+ uint8_t buf[24*1024];
+ struct ieee80211req ireq;
+ uint8_t *cp;
+ int len;
+
+ (void) memset(&ireq, 0, sizeof(ireq));
+ (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+ ireq.i_type = IEEE80211_IOC_STA_INFO;
+ ireq.i_data = buf;
+ ireq.i_len = sizeof(buf);
+ if (ioctl(s, SIOCG80211, &ireq) < 0)
+ errx(1, "unable to get station information");
+ len = ireq.i_len;
+ if (len < sizeof(struct ieee80211req_sta_info))
+ return;
+
+ printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %3s\n"
+ , "ADDR"
+ , "AID"
+ , "CHAN"
+ , "RATE"
+ , "RSSI"
+ , "IDLE"
+ , "TXSEQ"
+ , "RXSEQ"
+ , "CAPS"
+ , "ERP"
+ );
+ cp = buf;
+ do {
+ struct ieee80211req_sta_info *si;
+ uint8_t *vp;
+
+ si = (struct ieee80211req_sta_info *) cp;
+ vp = (u_int8_t *)(si+1);
+ printf("%s %4u %4d %3dM %4d %4d %6d %6d %-4.4s %3x"
+ , ether_ntoa((const struct ether_addr*) si->isi_macaddr)
+ , IEEE80211_AID(si->isi_associd)
+ , ieee80211_mhz2ieee(si->isi_freq)
+ , (si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL)/2
+ , si->isi_rssi
+ , si->isi_inact
+ , si->isi_txseqs[0]
+ , si->isi_rxseqs[0]
+ , getcaps(si->isi_capinfo)
+ , si->isi_erp
+ );
+ printies(vp, si->isi_ie_len, 24);
+ printf("\n");
+ cp += si->isi_len, len -= si->isi_len;
+ } while (len >= sizeof(struct ieee80211req_sta_info));
+}
+
+static void
+print_chaninfo(const struct ieee80211_channel *c)
+{
+#define IEEE80211_IS_CHAN_PASSIVE(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_PASSIVE))
+ char buf[14];
+
+ buf[0] = '\0';
+ if (IEEE80211_IS_CHAN_FHSS(c))
+ strlcat(buf, " FHSS", sizeof(buf));
+ if (IEEE80211_IS_CHAN_A(c))
+ strlcat(buf, " 11a", sizeof(buf));
+ /* XXX 11g schizophrenia */
+ if (IEEE80211_IS_CHAN_G(c) ||
+ IEEE80211_IS_CHAN_PUREG(c))
+ strlcat(buf, " 11g", sizeof(buf));
+ else if (IEEE80211_IS_CHAN_B(c))
+ strlcat(buf, " 11b", sizeof(buf));
+ if (IEEE80211_IS_CHAN_T(c))
+ strlcat(buf, " Turbo", sizeof(buf));
+ printf("Channel %3u : %u%c Mhz%-14.14s",
+ ieee80211_mhz2ieee(c->ic_freq), c->ic_freq,
+ IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ', buf);
+#undef IEEE80211_IS_CHAN_PASSIVE
+}
+
+static void
+list_channels(int s, int allchans)
+{
+ struct ieee80211req ireq;
+ struct ieee80211req_chaninfo chans;
+ struct ieee80211req_chaninfo achans;
+ const struct ieee80211_channel *c;
+ int i, half;
+
+ (void) memset(&ireq, 0, sizeof(ireq));
+ (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+ ireq.i_type = IEEE80211_IOC_CHANINFO;
+ ireq.i_data = &chans;
+ ireq.i_len = sizeof(chans);
+ if (ioctl(s, SIOCG80211, &ireq) < 0)
+ errx(1, "unable to get channel information");
+ if (!allchans) {
+ struct ieee80211req_chanlist active;
+
+ ireq.i_type = IEEE80211_IOC_CHANLIST;
+ ireq.i_data = &active;
+ ireq.i_len = sizeof(active);
+ if (ioctl(s, SIOCG80211, &ireq) < 0)
+ errx(1, "unable to get active channel list");
+ memset(&achans, 0, sizeof(achans));
+ for (i = 0; i < chans.ic_nchans; i++) {
+ c = &chans.ic_chans[i];
+ if (isset(active.ic_channels, ieee80211_mhz2ieee(c->ic_freq)) || allchans)
+ achans.ic_chans[achans.ic_nchans++] = *c;
+ }
+ } else
+ achans = chans;
+ half = achans.ic_nchans / 2;
+ if (achans.ic_nchans % 2)
+ half++;
+ for (i = 0; i < achans.ic_nchans / 2; i++) {
+ print_chaninfo(&achans.ic_chans[i]);
+ print_chaninfo(&achans.ic_chans[half+i]);
+ printf("\n");
+ }
+ if (achans.ic_nchans % 2) {
+ print_chaninfo(&achans.ic_chans[i]);
+ printf("\n");
+ }
+}
+
+static void
+list_keys(int s)
+{
+}
+
+#define IEEE80211_C_BITS \
+"\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \
+"\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \
+"\31WPA2\32BURST\33WME"
+
+static void
+list_capabilities(int s)
+{
+ struct ieee80211req ireq;
+ u_int32_t caps;
+
+ (void) memset(&ireq, 0, sizeof(ireq));
+ (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+ ireq.i_type = IEEE80211_IOC_DRIVER_CAPS;
+ if (ioctl(s, SIOCG80211, &ireq) < 0)
+ errx(1, "unable to get driver capabilities");
+ caps = (((u_int16_t) ireq.i_val) << 16) | ((u_int16_t) ireq.i_len);
+ printb(name, caps, IEEE80211_C_BITS);
+ putchar('\n');
+}
+
+static void
+list_wme(int s)
+{
+ static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
+ struct ieee80211req ireq;
+ int ac;
+
+ (void) memset(&ireq, 0, sizeof(ireq));
+ (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+ ireq.i_len = 0;
+ for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
+again:
+ if (ireq.i_len & IEEE80211_WMEPARAM_BSS)
+ printf("\t%s", " ");
+ else
+ printf("\t%s", acnames[ac]);
+
+ ireq.i_len = (ireq.i_len & IEEE80211_WMEPARAM_BSS) | ac;
+
+ /* show WME BSS parameters */
+ ireq.i_type = IEEE80211_IOC_WME_CWMIN;
+ if (ioctl(s, SIOCG80211, &ireq) != -1)
+ printf(" cwmin %2u", ireq.i_val);
+ ireq.i_type = IEEE80211_IOC_WME_CWMAX;
+ if (ioctl(s, SIOCG80211, &ireq) != -1)
+ printf(" cwmax %2u", ireq.i_val);
+ ireq.i_type = IEEE80211_IOC_WME_AIFS;
+ if (ioctl(s, SIOCG80211, &ireq) != -1)
+ printf(" aifs %2u", ireq.i_val);
+ ireq.i_type = IEEE80211_IOC_WME_TXOPLIMIT;
+ if (ioctl(s, SIOCG80211, &ireq) != -1)
+ printf(" txopLimit %3u", ireq.i_val);
+ ireq.i_type = IEEE80211_IOC_WME_ACM;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ if (ireq.i_val)
+ printf(" acm");
+ else if (verbose)
+ printf(" -acm");
+ }
+ /* !BSS only */
+ if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) {
+ ireq.i_type = IEEE80211_IOC_WME_ACKPOLICY;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ if (!ireq.i_val)
+ printf(" -ack");
+ else if (verbose)
+ printf(" ack");
+ }
+ }
+ printf("\n");
+ if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) {
+ ireq.i_len |= IEEE80211_WMEPARAM_BSS;
+ goto again;
+ } else
+ ireq.i_len &= ~IEEE80211_WMEPARAM_BSS;
+ }
+}
+
+static void
+list_mac(int s)
+{
+ struct ieee80211req ireq;
+ struct ieee80211req_maclist *acllist;
+ int i, nacls, policy;
+ char c;
+
+ (void) memset(&ireq, 0, sizeof(ireq));
+ (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
+ ireq.i_type = IEEE80211_IOC_MACCMD;
+ ireq.i_val = IEEE80211_MACCMD_POLICY;
+ if (ioctl(s, SIOCG80211, &ireq) < 0) {
+ if (errno == EINVAL) {
+ printf("No acl policy loaded\n");
+ return;
+ }
+ err(1, "unable to get mac policy");
+ }
+ policy = ireq.i_val;
+
+ ireq.i_val = IEEE80211_MACCMD_LIST;
+ ireq.i_len = 0;
+ if (ioctl(s, SIOCG80211, &ireq) < 0)
+ err(1, "unable to get mac acl list size");
+ if (ireq.i_len == 0) /* NB: no acls */
+ return;
+
+ ireq.i_data = malloc(ireq.i_len);
+ if (ireq.i_data == NULL)
+ err(1, "out of memory for acl list");
+
+ if (ioctl(s, SIOCG80211, &ireq) < 0)
+ err(1, "unable to get mac acl list");
+ if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
+ if (verbose)
+ printf("policy: open\n");
+ c = '*';
+ } else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
+ if (verbose)
+ printf("policy: allow\n");
+ c = '+';
+ } else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
+ if (verbose)
+ printf("policy: deny\n");
+ c = '-';
+ } else {
+ printf("policy: unknown (%u)\n", policy);
+ c = '?';
+ }
+ nacls = ireq.i_len / sizeof(*acllist);
+ acllist = (struct ieee80211req_maclist *) ireq.i_data;
+ for (i = 0; i < nacls; i++)
+ printf("%c%s\n", c, ether_ntoa(
+ (const struct ether_addr *) acllist[i].ml_macaddr));
+}
+
+static
+DECL_CMD_FUNC(set80211list, arg, d)
+{
+#define iseq(a,b) (strncasecmp(a,b,sizeof(b)-1) == 0)
+
+ if (iseq(arg, "sta"))
+ list_stations(s);
+ else if (iseq(arg, "scan") || iseq(arg, "ap"))
+ list_scan(s);
+ else if (iseq(arg, "chan") || iseq(arg, "freq"))
+ list_channels(s, 1);
+ else if (iseq(arg, "active"))
+ list_channels(s, 0);
+ else if (iseq(arg, "keys"))
+ list_keys(s);
+ else if (iseq(arg, "caps"))
+ list_capabilities(s);
+ else if (iseq(arg, "wme"))
+ list_wme(s);
+ else if (iseq(arg, "mac"))
+ list_mac(s);
+ else
+ errx(1, "Don't know how to list %s for %s", arg, name);
+#undef iseq
+}
+
+static enum ieee80211_opmode
+get80211opmode(int s)
+{
+ struct ifmediareq ifmr;
+
+ (void) memset(&ifmr, 0, sizeof(ifmr));
+ (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
+
+ if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
+ if (ifmr.ifm_current & IFM_IEEE80211_ADHOC)
+ return IEEE80211_M_IBSS; /* XXX ahdemo */
+ if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
+ return IEEE80211_M_HOSTAP;
+ if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
+ return IEEE80211_M_MONITOR;
+ }
+ return IEEE80211_M_STA;
+}
+
+static const struct ieee80211_channel *
+getchaninfo(int s, int chan)
+{
+ struct ieee80211req ireq;
+ static struct ieee80211req_chaninfo chans;
+ static struct ieee80211_channel undef;
+ const struct ieee80211_channel *c;
+ int i, freq;
+
+ (void) memset(&ireq, 0, sizeof(ireq));
+ (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+ ireq.i_type = IEEE80211_IOC_CHANINFO;
+ ireq.i_data = &chans;
+ ireq.i_len = sizeof(chans);
+ if (ioctl(s, SIOCG80211, &ireq) < 0)
+ errx(1, "unable to get channel information");
+ freq = ieee80211_ieee2mhz(chan);
+ for (i = 0; i < chans.ic_nchans; i++) {
+ c = &chans.ic_chans[i];
+ if (c->ic_freq == freq)
+ return c;
+ }
+ return &undef;
+}
+
+#if 0
+static void
+printcipher(int s, struct ieee80211req *ireq, int keylenop)
+{
+ switch (ireq->i_val) {
+ case IEEE80211_CIPHER_WEP:
+ ireq->i_type = keylenop;
+ if (ioctl(s, SIOCG80211, ireq) != -1)
+ printf("WEP-%s",
+ ireq->i_len <= 5 ? "40" :
+ ireq->i_len <= 13 ? "104" : "128");
+ else
+ printf("WEP");
+ break;
+ case IEEE80211_CIPHER_TKIP:
+ printf("TKIP");
+ break;
+ case IEEE80211_CIPHER_AES_OCB:
+ printf("AES-OCB");
+ break;
+ case IEEE80211_CIPHER_AES_CCM:
+ printf("AES-CCM");
+ break;
+ case IEEE80211_CIPHER_CKIP:
+ printf("CKIP");
+ break;
+ case IEEE80211_CIPHER_NONE:
+ printf("NONE");
+ break;
+ default:
+ printf("UNKNOWN (0x%x)", ireq->i_val);
+ break;
+ }
+}
+#endif
+
+#define MAXCOL 78
+int col;
+char spacer;
+
+#define LINE_BREAK() do { \
+ if (spacer != '\t') { \
+ printf("\n"); \
+ spacer = '\t'; \
+ } \
+ col = 8; /* 8-col tab */ \
+} while (0)
+#define LINE_CHECK(fmt, ...) do { \
+ col += sizeof(fmt)-2; \
+ if (col > MAXCOL) { \
+ LINE_BREAK(); \
+ col += sizeof(fmt)-2; \
+ } \
+ printf(fmt, __VA_ARGS__); \
+ spacer = ' '; \
+} while (0)
+
+static void
+printkey(const struct ieee80211req_key *ik)
+{
+ static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
+ int keylen = ik->ik_keylen;
+ int printcontents;
+
+ printcontents = printkeys &&
+ (memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
+ if (printcontents)
+ LINE_BREAK();
+ switch (ik->ik_type) {
+ case IEEE80211_CIPHER_WEP:
+ /* compatibility */
+ LINE_CHECK("%cwepkey %u:%s", spacer, ik->ik_keyix+1,
+ keylen <= 5 ? "40-bit" :
+ keylen <= 13 ? "104-bit" : "128-bit");
+ break;
+ case IEEE80211_CIPHER_TKIP:
+ if (keylen > 128/8)
+ keylen -= 128/8; /* ignore MIC for now */
+ LINE_CHECK("%cTKIP %u:%u-bit",
+ spacer, ik->ik_keyix+1, 8*keylen);
+ break;
+ case IEEE80211_CIPHER_AES_OCB:
+ LINE_CHECK("%cAES-OCB %u:%u-bit",
+ spacer, ik->ik_keyix+1, 8*keylen);
+ break;
+ case IEEE80211_CIPHER_AES_CCM:
+ LINE_CHECK("%cAES-CCM %u:%u-bit",
+ spacer, ik->ik_keyix+1, 8*keylen);
+ break;
+ case IEEE80211_CIPHER_CKIP:
+ LINE_CHECK("%cCKIP %u:%u-bit",
+ spacer, ik->ik_keyix+1, 8*keylen);
+ break;
+ case IEEE80211_CIPHER_NONE:
+ LINE_CHECK("%cNULL %u:%u-bit",
+ spacer, ik->ik_keyix+1, 8*keylen);
+ break;
+ default:
+ LINE_CHECK("%cUNKNOWN (0x%x) %u:%u-bit", spacer,
+ ik->ik_type, ik->ik_keyix+1, 8*keylen);
+ break;
+ }
+ if (printcontents) {
+ int i;
+
+ printf(" <");
+ for (i = 0; i < keylen; i++)
+ printf("%02x", ik->ik_keydata[i]);
+ printf(">");
+ if (ik->ik_type != IEEE80211_CIPHER_WEP &&
+ (ik->ik_keyrsc != 0 || verbose))
+ printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
+ if (ik->ik_type != IEEE80211_CIPHER_WEP &&
+ (ik->ik_keytsc != 0 || verbose))
+ printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
+ if (ik->ik_flags != 0 && verbose) {
+ const char *sep = " ";
+
+ if (ik->ik_flags & IEEE80211_KEY_XMIT)
+ printf("%stx", sep), sep = "+";
+ if (ik->ik_flags & IEEE80211_KEY_RECV)
+ printf("%srx", sep), sep = "+";
+ if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
+ printf("%sdef", sep), sep = "+";
+ }
+ LINE_BREAK();
+ }
+}
+
+static void
+ieee80211_status(int s)
+{
+ static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
+ enum ieee80211_opmode opmode = get80211opmode(s);
+ int i, num, wpa, wme;
+ struct ieee80211req ireq;
+ u_int8_t data[32];
+ const struct ieee80211_channel *c;
+
+ (void) memset(&ireq, 0, sizeof(ireq));
+ (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+ ireq.i_data = &data;
+
+ wpa = 0; /* unknown/not set */
+
+ ireq.i_type = IEEE80211_IOC_SSID;
+ ireq.i_val = -1;
+ if (ioctl(s, SIOCG80211, &ireq) < 0) {
+ /* If we can't get the SSID, this isn't an 802.11 device. */
+ return;
+ }
+ num = 0;
+ ireq.i_type = IEEE80211_IOC_NUMSSIDS;
+ if (ioctl(s, SIOCG80211, &ireq) >= 0)
+ num = ireq.i_val;
+ printf("\tssid ");
+ if (num > 1) {
+ ireq.i_type = IEEE80211_IOC_SSID;
+ for (ireq.i_val = 0; ireq.i_val < num; ireq.i_val++) {
+ if (ioctl(s, SIOCG80211, &ireq) >= 0 && ireq.i_len > 0) {
+ printf(" %d:", ireq.i_val + 1);
+ print_string(data, ireq.i_len);
+ }
+ }
+ } else
+ print_string(data, ireq.i_len);
+
+ ireq.i_type = IEEE80211_IOC_CHANNEL;
+ if (ioctl(s, SIOCG80211, &ireq) < 0)
+ goto end;
+ c = getchaninfo(s, ireq.i_val);
+ if (ireq.i_val != -1) {
+ printf(" channel %d", ireq.i_val);
+ if (verbose)
+ printf(" (%u)", c->ic_freq);
+ } else if (verbose)
+ printf(" channel UNDEF");
+
+ ireq.i_type = IEEE80211_IOC_BSSID;
+ ireq.i_len = IEEE80211_ADDR_LEN;
+ if (ioctl(s, SIOCG80211, &ireq) >= 0 &&
+ (memcmp(ireq.i_data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
+ printf(" bssid %s", ether_ntoa(ireq.i_data));
+
+ ireq.i_type = IEEE80211_IOC_STATIONNAME;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ printf("\n\tstationname ");
+ print_string(data, ireq.i_len);
+ }
+
+ spacer = ' '; /* force first break */
+ LINE_BREAK();
+
+ ireq.i_type = IEEE80211_IOC_AUTHMODE;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ switch (ireq.i_val) {
+ case IEEE80211_AUTH_NONE:
+ LINE_CHECK("%cauthmode NONE", spacer);
+ break;
+ case IEEE80211_AUTH_OPEN:
+ LINE_CHECK("%cauthmode OPEN", spacer);
+ break;
+ case IEEE80211_AUTH_SHARED:
+ LINE_CHECK("%cauthmode SHARED", spacer);
+ break;
+ case IEEE80211_AUTH_8021X:
+ LINE_CHECK("%cauthmode 802.1x", spacer);
+ break;
+ case IEEE80211_AUTH_WPA:
+ ireq.i_type = IEEE80211_IOC_WPA;
+ if (ioctl(s, SIOCG80211, &ireq) != -1)
+ wpa = ireq.i_val;
+ if (!wpa)
+ wpa = 1; /* default to WPA1 */
+ switch (wpa) {
+ case 2:
+ LINE_CHECK("%cauthmode WPA2/802.11i",
+ spacer);
+ break;
+ case 3:
+ LINE_CHECK("%cauthmode WPA1+WPA2/802.11i",
+ spacer);
+ break;
+ default:
+ LINE_CHECK("%cauthmode WPA", spacer);
+ break;
+ }
+ break;
+ case IEEE80211_AUTH_AUTO:
+ LINE_CHECK("%cauthmode AUTO", spacer);
+ break;
+ default:
+ LINE_CHECK("%cauthmode UNKNOWN (0x%x)",
+ spacer, ireq.i_val);
+ break;
+ }
+ }
+
+ ireq.i_type = IEEE80211_IOC_WEP;
+ if (ioctl(s, SIOCG80211, &ireq) != -1 &&
+ ireq.i_val != IEEE80211_WEP_NOSUP) {
+ int firstkey, wepmode;
+
+ wepmode = ireq.i_val;
+ switch (wepmode) {
+ case IEEE80211_WEP_OFF:
+ LINE_CHECK("%cprivacy OFF", spacer);
+ break;
+ case IEEE80211_WEP_ON:
+ LINE_CHECK("%cprivacy ON", spacer);
+ break;
+ case IEEE80211_WEP_MIXED:
+ LINE_CHECK("%cprivacy MIXED", spacer);
+ break;
+ default:
+ LINE_CHECK("%cprivacy UNKNOWN (0x%x)",
+ spacer, wepmode);
+ break;
+ }
+
+ /*
+ * If we get here then we've got WEP support so we need
+ * to print WEP status.
+ */
+
+ ireq.i_type = IEEE80211_IOC_WEPTXKEY;
+ if (ioctl(s, SIOCG80211, &ireq) < 0) {
+ warn("WEP support, but no tx key!");
+ goto end;
+ }
+ if (ireq.i_val != -1)
+ LINE_CHECK("%cdeftxkey %d", spacer, ireq.i_val+1);
+ else if (wepmode != IEEE80211_WEP_OFF || verbose)
+ LINE_CHECK("%cdeftxkey UNDEF", spacer);
+
+ ireq.i_type = IEEE80211_IOC_NUMWEPKEYS;
+ if (ioctl(s, SIOCG80211, &ireq) < 0) {
+ warn("WEP support, but no NUMWEPKEYS support!");
+ goto end;
+ }
+ num = ireq.i_val;
+
+ firstkey = 1;
+ for (i = 0; i < num; i++) {
+ struct ieee80211req_key ik;
+
+ memset(&ik, 0, sizeof(ik));
+ ik.ik_keyix = i;
+ ireq.i_type = IEEE80211_IOC_WPAKEY;
+ ireq.i_data = &ik;
+ ireq.i_len = sizeof(ik);
+ if (ioctl(s, SIOCG80211, &ireq) < 0) {
+ warn("WEP support, but can get keys!");
+ goto end;
+ }
+ if (ik.ik_keylen != 0) {
+ if (verbose)
+ LINE_BREAK();
+ printkey(&ik);
+ firstkey = 0;
+ }
+ }
+ }
+
+ ireq.i_type = IEEE80211_IOC_POWERSAVE;
+ if (ioctl(s, SIOCG80211, &ireq) != -1 &&
+ ireq.i_val != IEEE80211_POWERSAVE_NOSUP ) {
+ if (ireq.i_val != IEEE80211_POWERSAVE_OFF || verbose) {
+ switch (ireq.i_val) {
+ case IEEE80211_POWERSAVE_OFF:
+ LINE_CHECK("%cpowersavemode OFF",
+ spacer);
+ break;
+ case IEEE80211_POWERSAVE_CAM:
+ LINE_CHECK("%cpowersavemode CAM",
+ spacer);
+ break;
+ case IEEE80211_POWERSAVE_PSP:
+ LINE_CHECK("%cpowersavemode PSP",
+ spacer);
+ break;
+ case IEEE80211_POWERSAVE_PSP_CAM:
+ LINE_CHECK("%cpowersavemode PSP-CAM",
+ spacer);
+ break;
+ }
+ ireq.i_type = IEEE80211_IOC_POWERSAVESLEEP;
+ if (ioctl(s, SIOCG80211, &ireq) != -1)
+ LINE_CHECK("%cpowersavesleep %d",
+ spacer, ireq.i_val);
+ }
+ }
+
+ ireq.i_type = IEEE80211_IOC_TXPOWMAX;
+ if (ioctl(s, SIOCG80211, &ireq) != -1)
+ LINE_CHECK("%ctxpowmax %d", spacer, ireq.i_val);
+
+ if (verbose) {
+ ireq.i_type = IEEE80211_IOC_TXPOWER;
+ if (ioctl(s, SIOCG80211, &ireq) != -1)
+ LINE_CHECK("%ctxpower %d", spacer, ireq.i_val);
+ }
+
+ ireq.i_type = IEEE80211_IOC_RTSTHRESHOLD;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ if (ireq.i_val != IEEE80211_RTS_MAX || verbose)
+ LINE_CHECK("%crtsthreshold %d", spacer, ireq.i_val);
+ }
+
+ ireq.i_type = IEEE80211_IOC_MCAST_RATE;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ if (ireq.i_val != 2*1 || verbose) {
+ if (ireq.i_val == 11)
+ LINE_CHECK("%cmcastrate 5.5", spacer);
+ else
+ LINE_CHECK("%cmcastrate %d", spacer,
+ ireq.i_val/2);
+ }
+ }
+
+ ireq.i_type = IEEE80211_IOC_FRAGTHRESHOLD;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ if (ireq.i_val != IEEE80211_FRAG_MAX || verbose)
+ LINE_CHECK("%cfragthreshold %d", spacer, ireq.i_val);
+ }
+
+ if (IEEE80211_IS_CHAN_G(c) || IEEE80211_IS_CHAN_PUREG(c) || verbose) {
+ ireq.i_type = IEEE80211_IOC_PUREG;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ if (ireq.i_val)
+ LINE_CHECK("%cpureg", spacer);
+ else if (verbose)
+ LINE_CHECK("%c-pureg", spacer);
+ }
+ ireq.i_type = IEEE80211_IOC_PROTMODE;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ switch (ireq.i_val) {
+ case IEEE80211_PROTMODE_OFF:
+ LINE_CHECK("%cprotmode OFF", spacer);
+ break;
+ case IEEE80211_PROTMODE_CTS:
+ LINE_CHECK("%cprotmode CTS", spacer);
+ break;
+ case IEEE80211_PROTMODE_RTSCTS:
+ LINE_CHECK("%cprotmode RTSCTS", spacer);
+ break;
+ default:
+ LINE_CHECK("%cprotmode UNKNOWN (0x%x)",
+ spacer, ireq.i_val);
+ break;
+ }
+ }
+ }
+
+ ireq.i_type = IEEE80211_IOC_WME;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ wme = ireq.i_val;
+ if (wme)
+ LINE_CHECK("%cwme", spacer);
+ else if (verbose)
+ LINE_CHECK("%c-wme", spacer);
+ } else
+ wme = 0;
+
+ ireq.i_type = IEEE80211_IOC_BURST;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ if (ireq.i_val)
+ LINE_CHECK("%cburst", spacer);
+ else if (verbose)
+ LINE_CHECK("%c-burst", spacer);
+ }
+
+ if (opmode == IEEE80211_M_HOSTAP) {
+ ireq.i_type = IEEE80211_IOC_HIDESSID;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ if (ireq.i_val)
+ LINE_CHECK("%cssid HIDE", spacer);
+ else if (verbose)
+ LINE_CHECK("%cssid SHOW", spacer);
+ }
+
+ ireq.i_type = IEEE80211_IOC_APBRIDGE;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ if (!ireq.i_val)
+ LINE_CHECK("%c-apbridge", spacer);
+ else if (verbose)
+ LINE_CHECK("%capbridge", spacer);
+ }
+
+ ireq.i_type = IEEE80211_IOC_DTIM_PERIOD;
+ if (ioctl(s, SIOCG80211, &ireq) != -1)
+ LINE_CHECK("%cdtimperiod %u", spacer, ireq.i_val);
+ } else {
+ ireq.i_type = IEEE80211_IOC_ROAMING;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ if (ireq.i_val != IEEE80211_ROAMING_AUTO || verbose) {
+ switch (ireq.i_val) {
+ case IEEE80211_ROAMING_DEVICE:
+ LINE_CHECK("%croaming DEVICE", spacer);
+ break;
+ case IEEE80211_ROAMING_AUTO:
+ LINE_CHECK("%croaming AUTO", spacer);
+ break;
+ case IEEE80211_ROAMING_MANUAL:
+ LINE_CHECK("%croaming MANUAL", spacer);
+ break;
+ default:
+ LINE_CHECK("%croaming UNKNOWN (0x%x)",
+ spacer, ireq.i_val);
+ break;
+ }
+ }
+ }
+ }
+ ireq.i_type = IEEE80211_IOC_BEACON_INTERVAL;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ if (ireq.i_val)
+ LINE_CHECK("%cbintval %u", spacer, ireq.i_val);
+ else if (verbose)
+ LINE_CHECK("%cbintval %u", spacer, ireq.i_val);
+ }
+
+ if (wme && verbose) {
+ LINE_BREAK();
+ list_wme(s);
+ }
+
+ if (wpa) {
+ ireq.i_type = IEEE80211_IOC_COUNTERMEASURES;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ if (ireq.i_val)
+ LINE_CHECK("%ccountermeasures", spacer);
+ else if (verbose)
+ LINE_CHECK("%c-countermeasures", spacer);
+ }
+#if 0
+ /* XXX not interesting with WPA done in user space */
+ ireq.i_type = IEEE80211_IOC_KEYMGTALGS;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ }
+
+ ireq.i_type = IEEE80211_IOC_MCASTCIPHER;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ printf("%cmcastcipher ", spacer);
+ printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN);
+ spacer = ' ';
+ }
+
+ ireq.i_type = IEEE80211_IOC_UCASTCIPHER;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ printf("%cucastcipher ", spacer);
+ printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN);
+ }
+
+ if (wpa & 2) {
+ ireq.i_type = IEEE80211_IOC_RSNCAPS;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ printf("%cRSN caps 0x%x", spacer, ireq.i_val);
+ spacer = ' ';
+ }
+ }
+
+ ireq.i_type = IEEE80211_IOC_UCASTCIPHERS;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ }
+#endif
+ LINE_BREAK();
+ }
+ LINE_BREAK();
+
+end:
+ return;
+}
+
+static void
+set80211(int s, int type, int val, int len, u_int8_t *data)
+{
+ struct ieee80211req ireq;
+
+ (void) memset(&ireq, 0, sizeof(ireq));
+ (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+ ireq.i_type = type;
+ ireq.i_val = val;
+ ireq.i_len = len;
+ ireq.i_data = data;
+ if (ioctl(s, SIOCS80211, &ireq) < 0)
+ err(1, "SIOCS80211");
+}
+
+static const char *
+get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
+{
+ int len;
+ int hexstr;
+ u_int8_t *p;
+
+ len = *lenp;
+ p = buf;
+ hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
+ if (hexstr)
+ val += 2;
+ for (;;) {
+ if (*val == '\0')
+ break;
+ if (sep != NULL && strchr(sep, *val) != NULL) {
+ val++;
+ break;
+ }
+ if (hexstr) {
+ if (!isxdigit((u_char)val[0])) {
+ warnx("bad hexadecimal digits");
+ return NULL;
+ }
+ if (!isxdigit((u_char)val[1])) {
+ warnx("odd count hexadecimal digits");
+ return NULL;
+ }
+ }
+ if (p >= buf + len) {
+ if (hexstr)
+ warnx("hexadecimal digits too long");
+ else
+ warnx("string too long");
+ return NULL;
+ }
+ if (hexstr) {
+#define tohex(x) (isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
+ *p++ = (tohex((u_char)val[0]) << 4) |
+ tohex((u_char)val[1]);
+#undef tohex
+ val += 2;
+ } else
+ *p++ = *val++;
+ }
+ len = p - buf;
+ /* The string "-" is treated as the empty string. */
+ if (!hexstr && len == 1 && buf[0] == '-')
+ len = 0;
+ if (len < *lenp)
+ memset(p, 0, *lenp - len);
+ *lenp = len;
+ return val;
+}
+
+static void
+print_string(const u_int8_t *buf, int len)
+{
+ int i;
+ int hasspc;
+
+ i = 0;
+ hasspc = 0;
+ for (; i < len; i++) {
+ if (!isprint(buf[i]) && buf[i] != '\0')
+ break;
+ if (isspace(buf[i]))
+ hasspc++;
+ }
+ if (i == len) {
+ if (hasspc || len == 0 || buf[0] == '\0')
+ printf("\"%.*s\"", len, buf);
+ else
+ printf("%.*s", len, buf);
+ } else {
+ printf("0x");
+ for (i = 0; i < len; i++)
+ printf("%02x", buf[i]);
+ }
+}
+
+static struct cmd ieee80211_cmds[] = {
+ DEF_CMD_ARG("ssid", set80211ssid),
+ DEF_CMD_ARG("nwid", set80211ssid),
+ DEF_CMD_ARG("stationname", set80211stationname),
+ DEF_CMD_ARG("station", set80211stationname), /* BSD/OS */
+ DEF_CMD_ARG("channel", set80211channel),
+ DEF_CMD_ARG("authmode", set80211authmode),
+ DEF_CMD_ARG("powersavemode", set80211powersavemode),
+ DEF_CMD("powersave", 1, set80211powersave),
+ DEF_CMD("-powersave", 0, set80211powersave),
+ DEF_CMD_ARG("powersavesleep", set80211powersavesleep),
+ DEF_CMD_ARG("wepmode", set80211wepmode),
+ DEF_CMD("wep", 1, set80211wep),
+ DEF_CMD("-wep", 0, set80211wep),
+ DEF_CMD_ARG("deftxkey", set80211weptxkey),
+ DEF_CMD_ARG("weptxkey", set80211weptxkey),
+ DEF_CMD_ARG("wepkey", set80211wepkey),
+ DEF_CMD_ARG("nwkey", set80211nwkey), /* NetBSD */
+ DEF_CMD("-nwkey", 0, set80211wep), /* NetBSD */
+ DEF_CMD_ARG("rtsthreshold", set80211rtsthreshold),
+ DEF_CMD_ARG("protmode", set80211protmode),
+ DEF_CMD_ARG("txpower", set80211txpower),
+ DEF_CMD_ARG("roaming", set80211roaming),
+ DEF_CMD("wme", 1, set80211wme),
+ DEF_CMD("-wme", 0, set80211wme),
+ DEF_CMD("hidessid", 1, set80211hidessid),
+ DEF_CMD("-hidessid", 0, set80211hidessid),
+ DEF_CMD("apbridge", 1, set80211apbridge),
+ DEF_CMD("-apbridge", 0, set80211apbridge),
+ DEF_CMD_ARG("chanlist", set80211chanlist),
+ DEF_CMD_ARG("bssid", set80211bssid),
+ DEF_CMD_ARG("ap", set80211bssid),
+ DEF_CMD("scan", 0, set80211scan),
+ DEF_CMD_ARG("list", set80211list),
+ DEF_CMD_ARG2("cwmin", set80211cwmin),
+ DEF_CMD_ARG2("cwmax", set80211cwmax),
+ DEF_CMD_ARG2("aifs", set80211aifs),
+ DEF_CMD_ARG2("txoplimit", set80211txoplimit),
+ DEF_CMD_ARG("acm", set80211acm),
+ DEF_CMD_ARG("-acm", set80211noacm),
+ DEF_CMD_ARG("ack", set80211ackpolicy),
+ DEF_CMD_ARG("-ack", set80211noackpolicy),
+ DEF_CMD_ARG2("bss:cwmin", set80211bsscwmin),
+ DEF_CMD_ARG2("bss:cwmax", set80211bsscwmax),
+ DEF_CMD_ARG2("bss:aifs", set80211bssaifs),
+ DEF_CMD_ARG2("bss:txoplimit", set80211bsstxoplimit),
+ DEF_CMD_ARG("dtimperiod", set80211dtimperiod),
+ DEF_CMD_ARG("bintval", set80211bintval),
+ DEF_CMD("mac:open", IEEE80211_MACCMD_POLICY_OPEN, set80211maccmd),
+ DEF_CMD("mac:allow", IEEE80211_MACCMD_POLICY_ALLOW, set80211maccmd),
+ DEF_CMD("mac:deny", IEEE80211_MACCMD_POLICY_DENY, set80211maccmd),
+ DEF_CMD("mac:flush", IEEE80211_MACCMD_FLUSH, set80211maccmd),
+ DEF_CMD("mac:detach", IEEE80211_MACCMD_DETACH, set80211maccmd),
+ DEF_CMD_ARG("mac:add", set80211addmac),
+ DEF_CMD_ARG("mac:del", set80211delmac),
+ DEF_CMD_ARG("mac:kick", set80211kickmac),
+ DEF_CMD("pureg", 1, set80211pureg),
+ DEF_CMD("-pureg", 0, set80211pureg),
+ DEF_CMD_ARG("mcastrate", set80211mcastrate),
+ DEF_CMD_ARG("fragthreshold", set80211fragthreshold),
+ DEF_CMD("burst", 1, set80211burst),
+ DEF_CMD("-burst", 0, set80211burst),
+};
+static struct afswtch af_ieee80211 = {
+ .af_name = "af_ieee80211",
+ .af_af = AF_UNSPEC,
+ .af_other_status = ieee80211_status,
+};
+
+static __constructor void
+ieee80211_ctor(void)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ int i;
+
+ for (i = 0; i < N(ieee80211_cmds); i++)
+ cmd_register(&ieee80211_cmds[i]);
+ af_register(&af_ieee80211);
+#undef N
+}
diff --git a/sbin/ifconfig/ifmac.c b/sbin/ifconfig/ifmac.c
new file mode 100644
index 0000000..bb02106
--- /dev/null
+++ b/sbin/ifconfig/ifmac.c
@@ -0,0 +1,121 @@
+/*-
+ * Copyright (c) 2001 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by NAI Labs, the
+ * Security Research Division of Network Associates, Inc. under
+ * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA
+ * CHATS research program.
+ *
+ * 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/mac.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ifconfig.h"
+
+static void
+maclabel_status(int s)
+{
+ struct ifreq ifr;
+ mac_t label;
+ char *label_text;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+
+ if (mac_prepare_ifnet_label(&label) == -1)
+ return;
+ ifr.ifr_ifru.ifru_data = (void *)label;
+ if (ioctl(s, SIOCGIFMAC, &ifr) == -1)
+ goto mac_free;
+
+
+ if (mac_to_text(label, &label_text) == -1)
+ goto mac_free;
+
+ if (strlen(label_text) != 0)
+ printf("\tmaclabel %s\n", label_text);
+ free(label_text);
+
+mac_free:
+ mac_free(label);
+}
+
+static void
+setifmaclabel(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ struct ifreq ifr;
+ mac_t label;
+ int error;
+
+ if (mac_from_text(&label, val) == -1) {
+ perror(val);
+ return;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ ifr.ifr_ifru.ifru_data = (void *)label;
+
+ error = ioctl(s, SIOCSIFMAC, &ifr);
+ mac_free(label);
+ if (error == -1)
+ perror("setifmac");
+}
+
+static struct cmd mac_cmds[] = {
+ DEF_CMD_ARG("maclabel", setifmaclabel),
+};
+static struct afswtch af_mac = {
+ .af_name = "af_maclabel",
+ .af_af = AF_UNSPEC,
+ .af_other_status = maclabel_status,
+};
+
+static __constructor void
+mac_ctor(void)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ int i;
+
+ for (i = 0; i < N(mac_cmds); i++)
+ cmd_register(&mac_cmds[i]);
+ af_register(&af_mac);
+#undef N
+}
diff --git a/sbin/ifconfig/ifmedia.c b/sbin/ifconfig/ifmedia.c
new file mode 100644
index 0000000..4f49bde
--- /dev/null
+++ b/sbin/ifconfig/ifmedia.c
@@ -0,0 +1,806 @@
+/* $NetBSD: ifconfig.c,v 1.34 1997/04/21 01:17:58 lukem Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1997 Jason R. Thorpe.
+ * 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 for the NetBSD Project
+ * by Jason R. Thorpe.
+ * 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.
+ */
+
+/*
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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 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/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/if_media.h>
+#include <net/route.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ifconfig.h"
+
+static void domediaopt(const char *, int, int);
+static int get_media_subtype(int, const char *);
+static int get_media_mode(int, const char *);
+static int get_media_options(int, const char *);
+static int lookup_media_word(struct ifmedia_description *, const char *);
+static void print_media_word(int, int);
+static void print_media_word_ifconfig(int);
+
+static struct ifmedia_description *get_toptype_desc(int);
+static struct ifmedia_type_to_subtype *get_toptype_ttos(int);
+static struct ifmedia_description *get_subtype_desc(int,
+ struct ifmedia_type_to_subtype *ttos);
+
+static void
+media_status(int s)
+{
+ struct ifmediareq ifmr;
+ int *media_list, i;
+
+ (void) memset(&ifmr, 0, sizeof(ifmr));
+ (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
+
+ if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
+ /*
+ * Interface doesn't support SIOC{G,S}IFMEDIA.
+ */
+ return;
+ }
+
+ if (ifmr.ifm_count == 0) {
+ warnx("%s: no media types?", name);
+ return;
+ }
+
+ media_list = (int *)malloc(ifmr.ifm_count * sizeof(int));
+ if (media_list == NULL)
+ err(1, "malloc");
+ ifmr.ifm_ulist = media_list;
+
+ if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
+ err(1, "SIOCGIFMEDIA");
+
+ printf("\tmedia: ");
+ print_media_word(ifmr.ifm_current, 1);
+ if (ifmr.ifm_active != ifmr.ifm_current) {
+ putchar(' ');
+ putchar('(');
+ print_media_word(ifmr.ifm_active, 0);
+ putchar(')');
+ }
+
+ putchar('\n');
+
+ if (ifmr.ifm_status & IFM_AVALID) {
+ printf("\tstatus: ");
+ switch (IFM_TYPE(ifmr.ifm_active)) {
+ case IFM_ETHER:
+ if (ifmr.ifm_status & IFM_ACTIVE)
+ printf("active");
+ else
+ printf("no carrier");
+ break;
+
+ case IFM_FDDI:
+ case IFM_TOKEN:
+ if (ifmr.ifm_status & IFM_ACTIVE)
+ printf("inserted");
+ else
+ printf("no ring");
+ break;
+
+ case IFM_ATM:
+ if (ifmr.ifm_status & IFM_ACTIVE)
+ printf("active");
+ else
+ printf("no carrier");
+ break;
+
+ case IFM_IEEE80211:
+ /* XXX: Different value for adhoc? */
+ if (ifmr.ifm_status & IFM_ACTIVE)
+ printf("associated");
+ else
+ printf("no carrier");
+ break;
+ }
+ putchar('\n');
+ }
+
+ if (ifmr.ifm_count > 0 && supmedia) {
+ printf("\tsupported media:\n");
+ for (i = 0; i < ifmr.ifm_count; i++) {
+ printf("\t\t");
+ print_media_word_ifconfig(media_list[i]);
+ putchar('\n');
+ }
+ }
+
+ free(media_list);
+}
+
+static struct ifmediareq *
+getifmediastate(int s)
+{
+ static struct ifmediareq *ifmr = NULL;
+ int *mwords;
+
+ if (ifmr == NULL) {
+ ifmr = (struct ifmediareq *)malloc(sizeof(struct ifmediareq));
+ if (ifmr == NULL)
+ err(1, "malloc");
+
+ (void) memset(ifmr, 0, sizeof(struct ifmediareq));
+ (void) strncpy(ifmr->ifm_name, name,
+ sizeof(ifmr->ifm_name));
+
+ ifmr->ifm_count = 0;
+ ifmr->ifm_ulist = NULL;
+
+ /*
+ * We must go through the motions of reading all
+ * supported media because we need to know both
+ * the current media type and the top-level type.
+ */
+
+ if (ioctl(s, SIOCGIFMEDIA, (caddr_t)ifmr) < 0) {
+ err(1, "SIOCGIFMEDIA");
+ }
+
+ if (ifmr->ifm_count == 0)
+ errx(1, "%s: no media types?", name);
+
+ mwords = (int *)malloc(ifmr->ifm_count * sizeof(int));
+ if (mwords == NULL)
+ err(1, "malloc");
+
+ ifmr->ifm_ulist = mwords;
+ if (ioctl(s, SIOCGIFMEDIA, (caddr_t)ifmr) < 0)
+ err(1, "SIOCGIFMEDIA");
+ }
+
+ return ifmr;
+}
+
+static void
+setifmediacallback(int s, void *arg)
+{
+ struct ifmediareq *ifmr = (struct ifmediareq *)arg;
+ static int did_it = 0;
+
+ if (!did_it) {
+ ifr.ifr_media = ifmr->ifm_current;
+ if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0)
+ err(1, "SIOCSIFMEDIA (media)");
+ free(ifmr->ifm_ulist);
+ free(ifmr);
+ did_it = 1;
+ }
+}
+
+static void
+setmedia(const char *val, int d, int s, const struct afswtch *afp)
+{
+ struct ifmediareq *ifmr;
+ int subtype;
+
+
+ ifmr = getifmediastate(s);
+
+ /*
+ * We are primarily concerned with the top-level type.
+ * However, "current" may be only IFM_NONE, so we just look
+ * for the top-level type in the first "supported type"
+ * entry.
+ *
+ * (I'm assuming that all supported media types for a given
+ * interface will be the same top-level type..)
+ */
+ subtype = get_media_subtype(IFM_TYPE(ifmr->ifm_ulist[0]), val);
+
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ ifr.ifr_media = (ifmr->ifm_current & ~(IFM_NMASK|IFM_TMASK)) |
+ IFM_TYPE(ifmr->ifm_ulist[0]) | subtype;
+
+ if ((ifr.ifr_media & IFM_TMASK) == 0) {
+ ifr.ifr_media &= ~IFM_GMASK;
+ }
+
+ ifmr->ifm_current = ifr.ifr_media;
+ callback_register(setifmediacallback, (void *)ifmr);
+}
+
+static void
+setmediaopt(const char *val, int d, int s, const struct afswtch *afp)
+{
+
+ domediaopt(val, 0, s);
+}
+
+static void
+unsetmediaopt(const char *val, int d, int s, const struct afswtch *afp)
+{
+
+ domediaopt(val, 1, s);
+}
+
+static void
+domediaopt(const char *val, int clear, int s)
+{
+ struct ifmediareq *ifmr;
+ int options;
+
+ ifmr = getifmediastate(s);
+
+ options = get_media_options(IFM_TYPE(ifmr->ifm_ulist[0]), val);
+
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ ifr.ifr_media = ifmr->ifm_current;
+ if (clear)
+ ifr.ifr_media &= ~options;
+ else
+ ifr.ifr_media |= options;
+
+ ifmr->ifm_current = ifr.ifr_media;
+ callback_register(setifmediacallback, (void *)ifmr);
+}
+
+
+static void
+setmediamode(const char *val, int d, int s, const struct afswtch *afp)
+{
+ struct ifmediareq *ifmr;
+ int mode;
+
+ ifmr = getifmediastate(s);
+
+ mode = get_media_mode(IFM_TYPE(ifmr->ifm_ulist[0]), val);
+
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ ifr.ifr_media = (ifmr->ifm_current & ~IFM_MMASK) | mode;
+
+ ifmr->ifm_current = ifr.ifr_media;
+ callback_register(setifmediacallback, (void *)ifmr);
+}
+
+/**********************************************************************
+ * A good chunk of this is duplicated from sys/net/ifmedia.c
+ **********************************************************************/
+
+static struct ifmedia_description ifm_type_descriptions[] =
+ IFM_TYPE_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_ethernet_descriptions[] =
+ IFM_SUBTYPE_ETHERNET_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_ethernet_aliases[] =
+ IFM_SUBTYPE_ETHERNET_ALIASES;
+
+static struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] =
+ IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_tokenring_descriptions[] =
+ IFM_SUBTYPE_TOKENRING_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_tokenring_aliases[] =
+ IFM_SUBTYPE_TOKENRING_ALIASES;
+
+static struct ifmedia_description ifm_subtype_tokenring_option_descriptions[] =
+ IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_fddi_descriptions[] =
+ IFM_SUBTYPE_FDDI_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_fddi_aliases[] =
+ IFM_SUBTYPE_FDDI_ALIASES;
+
+static struct ifmedia_description ifm_subtype_fddi_option_descriptions[] =
+ IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_ieee80211_descriptions[] =
+ IFM_SUBTYPE_IEEE80211_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_ieee80211_aliases[] =
+ IFM_SUBTYPE_IEEE80211_ALIASES;
+
+static struct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] =
+ IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS;
+
+struct ifmedia_description ifm_subtype_ieee80211_mode_descriptions[] =
+ IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS;
+
+struct ifmedia_description ifm_subtype_ieee80211_mode_aliases[] =
+ IFM_SUBTYPE_IEEE80211_MODE_ALIASES;
+
+static struct ifmedia_description ifm_subtype_atm_descriptions[] =
+ IFM_SUBTYPE_ATM_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_atm_aliases[] =
+ IFM_SUBTYPE_ATM_ALIASES;
+
+static struct ifmedia_description ifm_subtype_atm_option_descriptions[] =
+ IFM_SUBTYPE_ATM_OPTION_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_shared_descriptions[] =
+ IFM_SUBTYPE_SHARED_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_shared_aliases[] =
+ IFM_SUBTYPE_SHARED_ALIASES;
+
+static struct ifmedia_description ifm_shared_option_descriptions[] =
+ IFM_SHARED_OPTION_DESCRIPTIONS;
+
+struct ifmedia_type_to_subtype {
+ struct {
+ struct ifmedia_description *desc;
+ int alias;
+ } subtypes[5];
+ struct {
+ struct ifmedia_description *desc;
+ int alias;
+ } options[3];
+ struct {
+ struct ifmedia_description *desc;
+ int alias;
+ } modes[3];
+};
+
+/* must be in the same order as IFM_TYPE_DESCRIPTIONS */
+static struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = {
+ {
+ {
+ { &ifm_subtype_shared_descriptions[0], 0 },
+ { &ifm_subtype_shared_aliases[0], 1 },
+ { &ifm_subtype_ethernet_descriptions[0], 0 },
+ { &ifm_subtype_ethernet_aliases[0], 1 },
+ { NULL, 0 },
+ },
+ {
+ { &ifm_shared_option_descriptions[0], 0 },
+ { &ifm_subtype_ethernet_option_descriptions[0], 0 },
+ { NULL, 0 },
+ },
+ {
+ { NULL, 0 },
+ },
+ },
+ {
+ {
+ { &ifm_subtype_shared_descriptions[0], 0 },
+ { &ifm_subtype_shared_aliases[0], 1 },
+ { &ifm_subtype_tokenring_descriptions[0], 0 },
+ { &ifm_subtype_tokenring_aliases[0], 1 },
+ { NULL, 0 },
+ },
+ {
+ { &ifm_shared_option_descriptions[0], 0 },
+ { &ifm_subtype_tokenring_option_descriptions[0], 0 },
+ { NULL, 0 },
+ },
+ {
+ { NULL, 0 },
+ },
+ },
+ {
+ {
+ { &ifm_subtype_shared_descriptions[0], 0 },
+ { &ifm_subtype_shared_aliases[0], 1 },
+ { &ifm_subtype_fddi_descriptions[0], 0 },
+ { &ifm_subtype_fddi_aliases[0], 1 },
+ { NULL, 0 },
+ },
+ {
+ { &ifm_shared_option_descriptions[0], 0 },
+ { &ifm_subtype_fddi_option_descriptions[0], 0 },
+ { NULL, 0 },
+ },
+ {
+ { NULL, 0 },
+ },
+ },
+ {
+ {
+ { &ifm_subtype_shared_descriptions[0], 0 },
+ { &ifm_subtype_shared_aliases[0], 1 },
+ { &ifm_subtype_ieee80211_descriptions[0], 0 },
+ { &ifm_subtype_ieee80211_aliases[0], 1 },
+ { NULL, 0 },
+ },
+ {
+ { &ifm_shared_option_descriptions[0], 0 },
+ { &ifm_subtype_ieee80211_option_descriptions[0], 0 },
+ { NULL, 0 },
+ },
+ {
+ { &ifm_subtype_ieee80211_mode_descriptions[0], 0 },
+ { &ifm_subtype_ieee80211_mode_aliases[0], 0 },
+ { NULL, 0 },
+ },
+ },
+ {
+ {
+ { &ifm_subtype_shared_descriptions[0], 0 },
+ { &ifm_subtype_shared_aliases[0], 1 },
+ { &ifm_subtype_atm_descriptions[0], 0 },
+ { &ifm_subtype_atm_aliases[0], 1 },
+ { NULL, 0 },
+ },
+ {
+ { &ifm_shared_option_descriptions[0], 0 },
+ { &ifm_subtype_atm_option_descriptions[0], 0 },
+ { NULL, 0 },
+ },
+ {
+ { NULL, 0 },
+ },
+ },
+};
+
+static int
+get_media_subtype(int type, const char *val)
+{
+ struct ifmedia_description *desc;
+ struct ifmedia_type_to_subtype *ttos;
+ int rval, i;
+
+ /* Find the top-level interface type. */
+ for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
+ desc->ifmt_string != NULL; desc++, ttos++)
+ if (type == desc->ifmt_word)
+ break;
+ if (desc->ifmt_string == NULL)
+ errx(1, "unknown media type 0x%x", type);
+
+ for (i = 0; ttos->subtypes[i].desc != NULL; i++) {
+ rval = lookup_media_word(ttos->subtypes[i].desc, val);
+ if (rval != -1)
+ return (rval);
+ }
+ errx(1, "unknown media subtype: %s", val);
+ /*NOTREACHED*/
+}
+
+static int
+get_media_mode(int type, const char *val)
+{
+ struct ifmedia_description *desc;
+ struct ifmedia_type_to_subtype *ttos;
+ int rval, i;
+
+ /* Find the top-level interface type. */
+ for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
+ desc->ifmt_string != NULL; desc++, ttos++)
+ if (type == desc->ifmt_word)
+ break;
+ if (desc->ifmt_string == NULL)
+ errx(1, "unknown media mode 0x%x", type);
+
+ for (i = 0; ttos->modes[i].desc != NULL; i++) {
+ rval = lookup_media_word(ttos->modes[i].desc, val);
+ if (rval != -1)
+ return (rval);
+ }
+ return -1;
+}
+
+static int
+get_media_options(int type, const char *val)
+{
+ struct ifmedia_description *desc;
+ struct ifmedia_type_to_subtype *ttos;
+ char *optlist, *optptr;
+ int option = 0, i, rval = 0;
+
+ /* We muck with the string, so copy it. */
+ optlist = strdup(val);
+ if (optlist == NULL)
+ err(1, "strdup");
+
+ /* Find the top-level interface type. */
+ for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
+ desc->ifmt_string != NULL; desc++, ttos++)
+ if (type == desc->ifmt_word)
+ break;
+ if (desc->ifmt_string == NULL)
+ errx(1, "unknown media type 0x%x", type);
+
+ /*
+ * Look up the options in the user-provided comma-separated
+ * list.
+ */
+ optptr = optlist;
+ for (; (optptr = strtok(optptr, ",")) != NULL; optptr = NULL) {
+ for (i = 0; ttos->options[i].desc != NULL; i++) {
+ option = lookup_media_word(ttos->options[i].desc, optptr);
+ if (option != -1)
+ break;
+ }
+ if (option == 0)
+ errx(1, "unknown option: %s", optptr);
+ rval |= option;
+ }
+
+ free(optlist);
+ return (rval);
+}
+
+static int
+lookup_media_word(struct ifmedia_description *desc, const char *val)
+{
+
+ for (; desc->ifmt_string != NULL; desc++)
+ if (strcasecmp(desc->ifmt_string, val) == 0)
+ return (desc->ifmt_word);
+
+ return (-1);
+}
+
+static struct ifmedia_description *get_toptype_desc(int ifmw)
+{
+ struct ifmedia_description *desc;
+
+ for (desc = ifm_type_descriptions; desc->ifmt_string != NULL; desc++)
+ if (IFM_TYPE(ifmw) == desc->ifmt_word)
+ break;
+
+ return desc;
+}
+
+static struct ifmedia_type_to_subtype *get_toptype_ttos(int ifmw)
+{
+ struct ifmedia_description *desc;
+ struct ifmedia_type_to_subtype *ttos;
+
+ for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
+ desc->ifmt_string != NULL; desc++, ttos++)
+ if (IFM_TYPE(ifmw) == desc->ifmt_word)
+ break;
+
+ return ttos;
+}
+
+static struct ifmedia_description *get_subtype_desc(int ifmw,
+ struct ifmedia_type_to_subtype *ttos)
+{
+ int i;
+ struct ifmedia_description *desc;
+
+ for (i = 0; ttos->subtypes[i].desc != NULL; i++) {
+ if (ttos->subtypes[i].alias)
+ continue;
+ for (desc = ttos->subtypes[i].desc;
+ desc->ifmt_string != NULL; desc++) {
+ if (IFM_SUBTYPE(ifmw) == desc->ifmt_word)
+ return desc;
+ }
+ }
+
+ return NULL;
+}
+
+static struct ifmedia_description *get_mode_desc(int ifmw,
+ struct ifmedia_type_to_subtype *ttos)
+{
+ int i;
+ struct ifmedia_description *desc;
+
+ for (i = 0; ttos->modes[i].desc != NULL; i++) {
+ if (ttos->modes[i].alias)
+ continue;
+ for (desc = ttos->modes[i].desc;
+ desc->ifmt_string != NULL; desc++) {
+ if (IFM_MODE(ifmw) == desc->ifmt_word)
+ return desc;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+print_media_word(int ifmw, int print_toptype)
+{
+ struct ifmedia_description *desc;
+ struct ifmedia_type_to_subtype *ttos;
+ int seen_option = 0, i;
+
+ /* Find the top-level interface type. */
+ desc = get_toptype_desc(ifmw);
+ ttos = get_toptype_ttos(ifmw);
+ if (desc->ifmt_string == NULL) {
+ printf("<unknown type>");
+ return;
+ } else if (print_toptype) {
+ printf("%s", desc->ifmt_string);
+ }
+
+ /*
+ * Don't print the top-level type; it's not like we can
+ * change it, or anything.
+ */
+
+ /* Find subtype. */
+ desc = get_subtype_desc(ifmw, ttos);
+ if (desc != NULL)
+ goto got_subtype;
+
+ /* Falling to here means unknown subtype. */
+ printf("<unknown subtype>");
+ return;
+
+ got_subtype:
+ if (print_toptype)
+ putchar(' ');
+
+ printf("%s", desc->ifmt_string);
+
+ if (print_toptype) {
+ desc = get_mode_desc(ifmw, ttos);
+ if (desc != NULL && strcasecmp("autoselect", desc->ifmt_string))
+ printf(" mode %s", desc->ifmt_string);
+ }
+
+ /* Find options. */
+ for (i = 0; ttos->options[i].desc != NULL; i++) {
+ if (ttos->options[i].alias)
+ continue;
+ for (desc = ttos->options[i].desc;
+ desc->ifmt_string != NULL; desc++) {
+ if (ifmw & desc->ifmt_word) {
+ if (seen_option == 0)
+ printf(" <");
+ printf("%s%s", seen_option++ ? "," : "",
+ desc->ifmt_string);
+ }
+ }
+ }
+ printf("%s", seen_option ? ">" : "");
+}
+
+static void
+print_media_word_ifconfig(int ifmw)
+{
+ struct ifmedia_description *desc;
+ struct ifmedia_type_to_subtype *ttos;
+ int i;
+
+ /* Find the top-level interface type. */
+ desc = get_toptype_desc(ifmw);
+ ttos = get_toptype_ttos(ifmw);
+ if (desc->ifmt_string == NULL) {
+ printf("<unknown type>");
+ return;
+ }
+
+ /*
+ * Don't print the top-level type; it's not like we can
+ * change it, or anything.
+ */
+
+ /* Find subtype. */
+ desc = get_subtype_desc(ifmw, ttos);
+ if (desc != NULL)
+ goto got_subtype;
+
+ /* Falling to here means unknown subtype. */
+ printf("<unknown subtype>");
+ return;
+
+ got_subtype:
+ printf("media %s", desc->ifmt_string);
+
+ desc = get_mode_desc(ifmw, ttos);
+ if (desc != NULL)
+ printf(" mode %s", desc->ifmt_string);
+
+ /* Find options. */
+ for (i = 0; ttos->options[i].desc != NULL; i++) {
+ if (ttos->options[i].alias)
+ continue;
+ for (desc = ttos->options[i].desc;
+ desc->ifmt_string != NULL; desc++) {
+ if (ifmw & desc->ifmt_word) {
+ printf(" mediaopt %s", desc->ifmt_string);
+ }
+ }
+ }
+}
+
+/**********************************************************************
+ * ...until here.
+ **********************************************************************/
+
+static struct cmd media_cmds[] = {
+ DEF_CMD_ARG("media", setmedia),
+ DEF_CMD_ARG("mode", setmediamode),
+ DEF_CMD_ARG("mediaopt", setmediaopt),
+ DEF_CMD_ARG("-mediaopt",unsetmediaopt),
+};
+static struct afswtch af_media = {
+ .af_name = "af_media",
+ .af_af = AF_UNSPEC,
+ .af_other_status = media_status,
+};
+
+static __constructor void
+ifmedia_ctor(void)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ int i;
+
+ for (i = 0; i < N(media_cmds); i++)
+ cmd_register(&media_cmds[i]);
+ af_register(&af_media);
+#undef N
+}
diff --git a/sbin/ifconfig/ifpfsync.c b/sbin/ifconfig/ifpfsync.c
new file mode 100644
index 0000000..413cd74
--- /dev/null
+++ b/sbin/ifconfig/ifpfsync.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2003 Ryan McBride. All rights reserved.
+ * Copyright (c) 2004 Max Laier. 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <net/pfvar.h>
+#include <net/if_pfsync.h>
+#include <net/route.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ifconfig.h"
+
+void setpfsync_syncdev(const char *, int, int, const struct afswtch *);
+void unsetpfsync_syncdev(const char *, int, int, const struct afswtch *);
+void setpfsync_syncpeer(const char *, int, int, const struct afswtch *);
+void unsetpfsync_syncpeer(const char *, int, int, const struct afswtch *);
+void setpfsync_syncpeer(const char *, int, int, const struct afswtch *);
+void setpfsync_maxupd(const char *, int, int, const struct afswtch *);
+void pfsync_status(int);
+
+void
+setpfsync_syncdev(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ struct pfsyncreq preq;
+
+ bzero((char *)&preq, sizeof(struct pfsyncreq));
+ ifr.ifr_data = (caddr_t)&preq;
+
+ if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
+ err(1, "SIOCGETPFSYNC");
+
+ strlcpy(preq.pfsyncr_syncdev, val, sizeof(preq.pfsyncr_syncdev));
+
+ if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSETPFSYNC");
+}
+
+/* ARGSUSED */
+void
+unsetpfsync_syncdev(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ struct pfsyncreq preq;
+
+ bzero((char *)&preq, sizeof(struct pfsyncreq));
+ ifr.ifr_data = (caddr_t)&preq;
+
+ if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
+ err(1, "SIOCGETPFSYNC");
+
+ bzero((char *)&preq.pfsyncr_syncdev, sizeof(preq.pfsyncr_syncdev));
+
+ if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSETPFSYNC");
+}
+
+/* ARGSUSED */
+void
+setpfsync_syncpeer(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ struct pfsyncreq preq;
+ struct addrinfo hints, *peerres;
+ int ecode;
+
+ bzero((char *)&preq, sizeof(struct pfsyncreq));
+ ifr.ifr_data = (caddr_t)&preq;
+
+ if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
+ err(1, "SIOCGETPFSYNC");
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+
+ if ((ecode = getaddrinfo(val, NULL, &hints, &peerres)) != 0)
+ errx(1, "error in parsing address string: %s",
+ gai_strerror(ecode));
+
+ if (peerres->ai_addr->sa_family != AF_INET)
+ errx(1, "only IPv4 addresses supported for the syncpeer");
+
+ preq.pfsyncr_syncpeer.s_addr = ((struct sockaddr_in *)
+ peerres->ai_addr)->sin_addr.s_addr;
+
+ if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSETPFSYNC");
+}
+
+/* ARGSUSED */
+void
+unsetpfsync_syncpeer(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ struct pfsyncreq preq;
+
+ bzero((char *)&preq, sizeof(struct pfsyncreq));
+ ifr.ifr_data = (caddr_t)&preq;
+
+ if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
+ err(1, "SIOCGETPFSYNC");
+
+ preq.pfsyncr_syncpeer.s_addr = 0;
+
+ if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSETPFSYNC");
+}
+
+/* ARGSUSED */
+void
+setpfsync_maxupd(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ struct pfsyncreq preq;
+ int maxupdates;
+
+ maxupdates = atoi(val);
+ if ((maxupdates < 0) || (maxupdates > 255))
+ errx(1, "maxupd %s: out of range", val);
+
+ memset((char *)&preq, 0, sizeof(struct pfsyncreq));
+ ifr.ifr_data = (caddr_t)&preq;
+
+ if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
+ err(1, "SIOCGETPFSYNC");
+
+ preq.pfsyncr_maxupdates = maxupdates;
+
+ if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSETPFSYNC");
+}
+
+void
+pfsync_status(int s)
+{
+ struct pfsyncreq preq;
+
+ bzero((char *)&preq, sizeof(struct pfsyncreq));
+ ifr.ifr_data = (caddr_t)&preq;
+
+ if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
+ return;
+
+ if (preq.pfsyncr_syncdev[0] != '\0') {
+ printf("\tpfsync: syncdev: %s ", preq.pfsyncr_syncdev);
+ if (preq.pfsyncr_syncpeer.s_addr != INADDR_PFSYNC_GROUP)
+ printf("syncpeer: %s ",
+ inet_ntoa(preq.pfsyncr_syncpeer));
+ printf("maxupd: %d\n", preq.pfsyncr_maxupdates);
+ }
+}
+
+static struct cmd pfsync_cmds[] = {
+ DEF_CMD_ARG("syncdev", setpfsync_syncdev),
+ DEF_CMD("-syncdev", 1, unsetpfsync_syncdev),
+ DEF_CMD_ARG("syncif", setpfsync_syncdev),
+ DEF_CMD("-syncif", 1, unsetpfsync_syncdev),
+ DEF_CMD_ARG("syncpeer", setpfsync_syncpeer),
+ DEF_CMD("-syncpeer", 1, unsetpfsync_syncpeer),
+ DEF_CMD_ARG("maxupd", setpfsync_maxupd)
+};
+static struct afswtch af_pfsync = {
+ .af_name = "af_pfsync",
+ .af_af = AF_UNSPEC,
+ .af_other_status = pfsync_status,
+};
+
+static __constructor void
+pfsync_ctor(void)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ int i;
+
+ for (i = 0; i < N(pfsync_cmds); i++)
+ cmd_register(&pfsync_cmds[i]);
+ af_register(&af_pfsync);
+#undef N
+}
diff --git a/sbin/ifconfig/ifvlan.c b/sbin/ifconfig/ifvlan.c
new file mode 100644
index 0000000..8ec39e2
--- /dev/null
+++ b/sbin/ifconfig/ifvlan.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1999
+ * 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 THE VOICES IN HIS HEAD
+ * 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/sockio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_vlan_var.h>
+#include <net/route.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+
+#include "ifconfig.h"
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+static struct vlanreq __vreq;
+static int __have_dev = 0;
+static int __have_tag = 0;
+
+static void
+vlan_status(int s)
+{
+ struct vlanreq vreq;
+
+ bzero((char *)&vreq, sizeof(vreq));
+ ifr.ifr_data = (caddr_t)&vreq;
+
+ if (ioctl(s, SIOCGETVLAN, (caddr_t)&ifr) == -1)
+ return;
+
+ printf("\tvlan: %d parent interface: %s\n",
+ vreq.vlr_tag, vreq.vlr_parent[0] == '\0' ?
+ "<none>" : vreq.vlr_parent);
+}
+
+static void
+setvlantag(const char *val, int d, int s, const struct afswtch *afp)
+{
+ char *endp;
+ u_long ul;
+
+ ul = strtoul(val, &endp, 0);
+ if (*endp != '\0')
+ errx(1, "invalid value for vlan");
+ __vreq.vlr_tag = ul;
+ /* check if the value can be represented in vlr_tag */
+ if (__vreq.vlr_tag != ul)
+ errx(1, "value for vlan out of range");
+ /* the kernel will do more specific checks on vlr_tag */
+ __have_tag = 1;
+}
+
+static void
+setvlandev(const char *val, int d, int s, const struct afswtch *afp)
+{
+
+ strncpy(__vreq.vlr_parent, val, sizeof(__vreq.vlr_parent));
+ __have_dev = 1;
+}
+
+static void
+unsetvlandev(const char *val, int d, int s, const struct afswtch *afp)
+{
+
+ if (val != NULL)
+ warnx("argument to -vlandev is useless and hence deprecated");
+
+ bzero((char *)&__vreq, sizeof(__vreq));
+ ifr.ifr_data = (caddr_t)&__vreq;
+#if 0 /* this code will be of use when we can alter vlan or vlandev only */
+ if (ioctl(s, SIOCGETVLAN, (caddr_t)&ifr) == -1)
+ err(1, "SIOCGETVLAN");
+
+ bzero((char *)&__vreq.vlr_parent, sizeof(__vreq.vlr_parent));
+ __vreq.vlr_tag = 0; /* XXX clear parent only (no kernel support now) */
+#endif
+ if (ioctl(s, SIOCSETVLAN, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSETVLAN");
+ __have_dev = __have_tag = 0;
+}
+
+static void
+vlan_cb(int s, void *arg)
+{
+
+ if (__have_tag ^ __have_dev)
+ errx(1, "both vlan and vlandev must be specified");
+
+ if (__have_tag && __have_dev) {
+ ifr.ifr_data = (caddr_t)&__vreq;
+ if (ioctl(s, SIOCSETVLAN, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSETVLAN");
+ }
+}
+
+static struct cmd vlan_cmds[] = {
+ DEF_CMD_ARG("vlan", setvlantag),
+ DEF_CMD_ARG("vlandev", setvlandev),
+ /* XXX For compatibility. Should become DEF_CMD() some day. */
+ DEF_CMD_OPTARG("-vlandev", unsetvlandev),
+ DEF_CMD("vlanmtu", IFCAP_VLAN_MTU, setifcap),
+ DEF_CMD("-vlanmtu", -IFCAP_VLAN_MTU, setifcap),
+ DEF_CMD("vlanhwtag", IFCAP_VLAN_HWTAGGING, setifcap),
+ DEF_CMD("-vlanhwtag", -IFCAP_VLAN_HWTAGGING, setifcap),
+};
+static struct afswtch af_vlan = {
+ .af_name = "af_vlan",
+ .af_af = AF_UNSPEC,
+ .af_other_status = vlan_status,
+};
+
+static __constructor void
+vlan_ctor(void)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ int i;
+
+ for (i = 0; i < N(vlan_cmds); i++)
+ cmd_register(&vlan_cmds[i]);
+ af_register(&af_vlan);
+ callback_register(vlan_cb, NULL);
+#undef N
+}
diff --git a/sbin/init/Makefile b/sbin/init/Makefile
new file mode 100644
index 0000000..1d7c39f
--- /dev/null
+++ b/sbin/init/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.1 (Berkeley) 7/19/93
+# $FreeBSD$
+
+PROG= init
+MAN= init.8
+MLINKS= init.8 securelevel.8
+BINMODE=500
+PRECIOUSPROG=
+INSTALLFLAGS=-b -B.bak
+WARNS?= 6
+CFLAGS+=-DDEBUGSHELL -DSECURE -DLOGIN_CAP -DCOMPAT_SYSV_INIT
+DPADD= ${LIBUTIL} ${LIBCRYPT}
+LDADD= -lutil -lcrypt
+
+NO_SHARED?= YES
+
+.include <bsd.prog.mk>
diff --git a/sbin/init/NOTES b/sbin/init/NOTES
new file mode 100644
index 0000000..bf75101
--- /dev/null
+++ b/sbin/init/NOTES
@@ -0,0 +1,112 @@
+POSIX and init:
+--------------
+
+POSIX.1 does not define 'init' but it mentions it in a few places.
+
+B.2.2.2, p205 line 873:
+
+ This is part of the extensive 'job control' glossary entry.
+ This specific reference says that 'init' must by default provide
+ protection from job control signals to jobs it starts --
+ it sets SIGTSTP, SIGTTIN and SIGTTOU to SIG_IGN.
+
+B.2.2.2, p206 line 889:
+
+ Here is a reference to 'vhangup'. It says, 'POSIX.1 does
+ not specify how controlling terminal access is affected by
+ a user logging out (that is, by a controlling process
+ terminating).' vhangup() is recognized as one way to handle
+ the problem. I'm not clear what happens in Reno; I have
+ the impression that when the controlling process terminates,
+ references to the controlling terminal are converted to
+ references to a 'dead' vnode. I don't know whether vhangup()
+ is required.
+
+B.2.2.2, p206 line 921:
+
+ Orphaned process groups bear indirectly on this issue. A
+ session leader's process group is considered to be orphaned;
+ that is, it's immune to job control signals from the terminal.
+
+B.2.2.2, p233 line 2055:
+
+ 'Historically, the implementation-dependent process that
+ inherits children whose parents have terminated without
+ waiting on them is called "init" and has a process ID of 1.'
+
+ It goes on to note that it used to be the case that 'init'
+ was responsible for sending SIGHUP to the foreground process
+ group of a tty whose controlling process has exited, using
+ vhangup(). It is now the responsibility of the kernel to
+ do this when the controlling process calls _exit(). The
+ kernel is also responsible for sending SIGCONT to stopped
+ process groups that become orphaned. This is like old BSD
+ but entire process groups are signaled instead of individual
+ processes.
+
+ In general it appears that the kernel now automatically
+ takes care of orphans, relieving 'init' of any responsibility.
+ Specifics are listed on the _exit() page (p50).
+
+On setsid():
+-----------
+
+It appears that neither getty nor login call setsid(), so init must
+do this -- seems reasonable. B.4.3.2 p 248 implies that this is the
+way that 'init' should work; it says that setsid() should be called
+after forking.
+
+Process group leaders cannot call setsid() -- another reason to
+fork! Of course setsid() causes the current process to become a
+process group leader, so we can only call setsid() once. Note that
+the controlling terminal acquires the session leader's process
+group when opened.
+
+Controlling terminals:
+---------------------
+
+B.7.1.1.3 p276: 'POSIX.1 does not specify a mechanism by which to
+allocate a controlling terminal. This is normally done by a system
+utility (such as 'getty') and is considered ... outside the scope
+of POSIX.1.' It goes on to say that historically the first open()
+of a tty in a session sets the controlling terminal. P130 has the
+full details; nothing particularly surprising.
+
+The glossary p12 describes a 'controlling process' as the first
+process in a session that acquires a controlling terminal. Access
+to the terminal from the session is revoked if the controlling
+process exits (see p50, in the discussion of process termination).
+
+Design notes:
+------------
+
+your generic finite state machine
+we are fascist about which signals we elect to receive,
+ even signals purportedly generated by hardware
+handle fatal errors gracefully if possible (we reboot if we goof!!)
+ if we get a segmentation fault etc., print a message on the console
+ and spin for a while before rebooting
+ (this at least decreases the amount of paper consumed :-)
+apply hysteresis to rapidly exiting gettys
+check wait status of children we reap
+ don't wait for stopped children
+don't use SIGCHILD, it's too expensive
+ but it may close windows and avoid races, sigh
+look for EINTR in case we need to change state
+init is responsible for utmp and wtmp maintenance (ick)
+ maybe now we can consider replacements? maintain them in parallel
+ init only removes utmp and closes out wtmp entries...
+
+necessary states and state transitions (gleaned from the man page):
+ 1: single user shell (with password checking?); on exit, go to 2
+ 2: rc script: on exit 0, go to 3; on exit N (error), go to 1
+ 3: read ttys file: on completion, go to 4
+ 4: multi-user operation: on SIGTERM, go to 7; on SIGHUP, go to 5;
+ on SIGTSTP, go to 6
+ 5: clean up mode (re-read ttys file, killing off controlling processes
+ on lines that are now 'off', starting them on lines newly 'on')
+ on completion, go to 4
+ 6: boring mode (no new sessions); signals as in 4
+ 7: death: send SIGHUP to all controlling processes, reap for 30 seconds,
+ then go to 1 (warn if not all processes died, i.e. wait blocks)
+Given the -s flag, we start at state 1; otherwise state 2
diff --git a/sbin/init/init.8 b/sbin/init/init.8
new file mode 100644
index 0000000..cff2b34
--- /dev/null
+++ b/sbin/init/init.8
@@ -0,0 +1,377 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Donn Seeley at Berkeley Software Design, 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.
+.\" 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 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.
+.\"
+.\" @(#)init.8 8.3 (Berkeley) 4/18/94
+.\" $FreeBSD$
+.\"
+.Dd September 15, 2005
+.Dt INIT 8
+.Os
+.Sh NAME
+.Nm init
+.Nd process control initialization
+.Sh SYNOPSIS
+.Nm
+.Nm
+.Oo
+.Cm 0 | 1 | 6 |
+.Cm c | q
+.Oc
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is the last stage of the boot process.
+It normally runs the automatic reboot sequence as described in
+.Xr rc 8 ,
+and if this succeeds, begins multi-user operation.
+If the reboot scripts fail,
+.Nm
+commences single-user operation by giving
+the super-user a shell on the console.
+The
+.Nm
+utility may be passed parameters
+from the boot program to
+prevent the system from going multi-user and to instead execute
+a single-user shell without starting the normal daemons.
+The system is then quiescent for maintenance work and may
+later be made to go to multi-user by exiting the
+single-user shell (with ^D).
+This
+causes
+.Nm
+to run the
+.Pa /etc/rc
+start up command file in fastboot mode (skipping disk checks).
+.Pp
+If the
+.Em console
+entry in the
+.Xr ttys 5
+file is marked
+.Dq insecure ,
+then
+.Nm
+will require that the super-user password be
+entered before the system will start a single-user shell.
+The password check is skipped if the
+.Em console
+is marked as
+.Dq secure .
+.Pp
+If the system security level (see
+.Xr security 7 )
+is initially nonzero, then
+.Nm
+leaves it unchanged.
+Otherwise,
+.Nm
+raises the level to 1 before going multi-user for the first time.
+Since the level cannot be reduced, it will be at least 1 for
+subsequent operation, even on return to single-user.
+If a level higher than 1 is desired while running multi-user,
+it can be set before going multi-user, e.g., by the startup script
+.Xr rc 8 ,
+using
+.Xr sysctl 8
+to set the
+.Va kern.securelevel
+variable to the required security level.
+.Pp
+If
+.Nm
+is run in a jail, the security level of the
+.Dq host system
+will not be effected.
+Part of the information set up in the kernel to support a jail
+is a per-jail security level.
+This allows running a higher security level inside of a jail
+than that of the host system.
+See
+.Xr jail 8
+for more information about jails.
+.Pp
+In multi-user operation,
+.Nm
+maintains
+processes for the terminal ports found in the file
+.Xr ttys 5 .
+The
+.Nm
+utility reads this file and executes the command found in the second field,
+unless the first field refers to a device in
+.Pa /dev
+which is not configured.
+The first field is supplied as the final argument to the command.
+This command is usually
+.Xr getty 8 ;
+.Nm getty
+opens and initializes the tty line
+and
+executes the
+.Xr login 1
+program.
+The
+.Nm login
+program, when a valid user logs in,
+executes a shell for that user.
+When this shell
+dies, either because the user logged out
+or an abnormal termination occurred (a signal),
+the
+.Nm
+utility wakes up, deletes the user
+from the
+.Xr utmp 5
+file of current users and records the logout in the
+.Xr wtmp 5
+file.
+The cycle is
+then restarted by
+.Nm
+executing a new
+.Nm getty
+for the line.
+.Pp
+The
+.Nm
+utility can also be used to keep arbitrary daemons running,
+automatically restarting them if they die.
+In this case, the first field in the
+.Xr ttys 5
+file must not reference the path to a configured device node
+and will be passed to the daemon
+as the final argument on its command line.
+This is similar to the facility offered in the
+.At V
+.Pa /etc/inittab .
+.Pp
+Line status (on, off, secure, getty, or window information)
+may be changed in the
+.Xr ttys 5
+file without a reboot by sending the signal
+.Dv SIGHUP
+to
+.Nm
+with the command
+.Dq Li "kill -HUP 1" .
+On receipt of this signal,
+.Nm
+re-reads the
+.Xr ttys 5
+file.
+When a line is turned off in
+.Xr ttys 5 ,
+.Nm
+will send a SIGHUP signal to the controlling process
+for the session associated with the line.
+For any lines that were previously turned off in the
+.Xr ttys 5
+file and are now on,
+.Nm
+executes the command specified in the second field.
+If the command or window field for a line is changed,
+the change takes effect at the end of the current
+login session (e.g., the next time
+.Nm
+starts a process on the line).
+If a line is commented out or deleted from
+.Xr ttys 5 ,
+.Nm
+will not do anything at all to that line.
+However, it will complain that the relationship between lines
+in the
+.Xr ttys 5
+file and records in the
+.Xr utmp 5
+file is out of sync,
+so this practice is not recommended.
+.Pp
+The
+.Nm
+utility will terminate multi-user operations and resume single-user mode
+if sent a terminate
+.Pq Dv TERM
+signal, for example,
+.Dq Li "kill \-TERM 1" .
+If there are processes outstanding that are deadlocked (because of
+hardware or software failure),
+.Nm
+will not wait for them all to die (which might take forever), but
+will time out after 30 seconds and print a warning message.
+.Pp
+The
+.Nm
+utility will cease creating new processes
+and allow the system to slowly die away, if it is sent a terminal stop
+.Pq Dv TSTP
+signal, i.e.\&
+.Dq Li "kill \-TSTP 1" .
+A later hangup will resume full
+multi-user operations, or a terminate will start a single-user shell.
+This hook is used by
+.Xr reboot 8
+and
+.Xr halt 8 .
+.Pp
+The
+.Nm
+utility will terminate all possible processes (again, it will not wait
+for deadlocked processes) and reboot the machine if sent the interrupt
+.Pq Dv INT
+signal, i.e.\&
+.Dq Li "kill \-INT 1".
+This is useful for shutting the machine down cleanly from inside the kernel
+or from X when the machine appears to be hung.
+.Pp
+The
+.Nm
+utility will do the same, except it will halt the machine if sent
+the user defined signal 1
+.Pq Dv USR1 ,
+or will halt and turn the power off (if hardware permits) if sent
+the user defined signal 2
+.Pq Dv USR2 .
+.Pp
+When shutting down the machine,
+.Nm
+will try to run the
+.Pa /etc/rc.shutdown
+script.
+This script can be used to cleanly terminate specific programs such
+as
+.Nm innd
+(the InterNetNews server).
+If this script does not terminate within 120 seconds,
+.Nm
+will terminate it.
+The timeout can be configured via the
+.Xr sysctl 8
+variable
+.Va kern.init_shutdown_timeout .
+.Pp
+The role of
+.Nm
+is so critical that if it dies, the system will reboot itself
+automatically.
+If, at bootstrap time, the
+.Nm
+process cannot be located, the system will panic with the message
+.Dq "panic: init died (signal %d, exit %d)" .
+.Pp
+If run as a user process as shown in the second synopsis line,
+.Nm
+will emulate
+.At V
+behavior, i.e., super-user can specify the desired
+.Em run-level
+on a command line, and
+.Nm
+will signal the original
+(PID 1)
+.Nm
+as follows:
+.Bl -column Run-level SIGTERM
+.It Sy "Run-level Signal Action
+.It Cm 0 Ta Dv SIGUSR2 Ta "Halt and turn the power off"
+.It Cm 1 Ta Dv SIGTERM Ta "Go to single-user mode"
+.It Cm 6 Ta Dv SIGINT Ta "Reboot the machine"
+.It Cm c Ta Dv SIGTSTP Ta "Block further logins"
+.It Cm q Ta Dv SIGHUP Ta Rescan the
+.Xr ttys 5
+file
+.El
+.Sh FILES
+.Bl -tag -width /etc/rc.shutdown -compact
+.It Pa /dev/console
+system console device
+.It Pa /dev/tty*
+terminal ports found in
+.Xr ttys 5
+.It Pa /var/run/utmp
+record of current users on the system
+.It Pa /var/log/wtmp
+record of all logins and logouts
+.It Pa /etc/ttys
+the terminal initialization information file
+.It Pa /etc/rc
+system startup commands
+.It Pa /etc/rc.shutdown
+system shutdown commands
+.El
+.Sh DIAGNOSTICS
+.Bl -diag
+.It "getty repeating too quickly on port %s, sleeping."
+A process being started to service a line is exiting quickly
+each time it is started.
+This is often caused by a ringing or noisy terminal line.
+.Bf -emphasis
+Init will sleep for 30 seconds,
+then continue trying to start the process.
+.Ef
+.It "some processes would not die; ps axl advised."
+A process
+is hung and could not be killed when the system was shutting down.
+This condition is usually caused by a process
+that is stuck in a device driver because of
+a persistent device error condition.
+.El
+.Sh SEE ALSO
+.Xr kill 1 ,
+.Xr login 1 ,
+.Xr sh 1 ,
+.Xr ttys 5 ,
+.Xr security 7 ,
+.Xr getty 8 ,
+.Xr halt 8 ,
+.Xr jail 8 ,
+.Xr rc 8 ,
+.Xr reboot 8 ,
+.Xr shutdown 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+An
+.Nm
+utility appeared in
+.At v6 .
+.Sh CAVEATS
+Systems without
+.Xr sysctl 8
+behave as though they have security level \-1.
+.Pp
+Setting the security level above 1 too early in the boot sequence can
+prevent
+.Xr fsck 8
+from repairing inconsistent file systems.
+The
+preferred location to set the security level is at the end of
+.Pa /etc/rc
+after all multi-user startup actions are complete.
diff --git a/sbin/init/init.c b/sbin/init/init.c
new file mode 100644
index 0000000..393955f
--- /dev/null
+++ b/sbin/init/init.c
@@ -0,0 +1,1635 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Donn Seeley at Berkeley Software Design, 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+
+#include <db.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <ttyent.h>
+#include <unistd.h>
+#include <sys/reboot.h>
+#include <err.h>
+
+#include <stdarg.h>
+
+#ifdef SECURE
+#include <pwd.h>
+#endif
+
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+#endif
+
+#include "pathnames.h"
+
+/*
+ * Sleep times; used to prevent thrashing.
+ */
+#define GETTY_SPACING 5 /* N secs minimum getty spacing */
+#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */
+#define GETTY_NSPACE 3 /* max. spacing count to bring reaction */
+#define WINDOW_WAIT 3 /* wait N secs after starting window */
+#define STALL_TIMEOUT 30 /* wait N secs after warning */
+#define DEATH_WATCH 10 /* wait N secs for procs to die */
+#define DEATH_SCRIPT 120 /* wait for 2min for /etc/rc.shutdown */
+#define RESOURCE_RC "daemon"
+#define RESOURCE_WINDOW "default"
+#define RESOURCE_GETTY "default"
+
+void handle(sig_t, ...);
+void delset(sigset_t *, ...);
+
+void stall(const char *, ...) __printflike(1, 2);
+void warning(const char *, ...) __printflike(1, 2);
+void emergency(const char *, ...) __printflike(1, 2);
+void disaster(int);
+void badsys(int);
+int runshutdown(void);
+static char *strk(char *);
+
+/*
+ * We really need a recursive typedef...
+ * The following at least guarantees that the return type of (*state_t)()
+ * is sufficiently wide to hold a function pointer.
+ */
+typedef long (*state_func_t)(void);
+typedef state_func_t (*state_t)(void);
+
+state_func_t single_user(void);
+state_func_t runcom(void);
+state_func_t read_ttys(void);
+state_func_t multi_user(void);
+state_func_t clean_ttys(void);
+state_func_t catatonia(void);
+state_func_t death(void);
+
+enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
+#define FALSE 0
+#define TRUE 1
+
+int Reboot = FALSE;
+int howto = RB_AUTOBOOT;
+
+int devfs;
+
+void transition(state_t);
+state_t requested_transition = runcom;
+
+void setctty(const char *);
+
+typedef struct init_session {
+ int se_index; /* index of entry in ttys file */
+ pid_t se_process; /* controlling process */
+ time_t se_started; /* used to avoid thrashing */
+ int se_flags; /* status of session */
+#define SE_SHUTDOWN 0x1 /* session won't be restarted */
+#define SE_PRESENT 0x2 /* session is in /etc/ttys */
+ int se_nspace; /* spacing count */
+ char *se_device; /* filename of port */
+ char *se_getty; /* what to run on that port */
+ char *se_getty_argv_space; /* pre-parsed argument array space */
+ char **se_getty_argv; /* pre-parsed argument array */
+ char *se_window; /* window system (started only once) */
+ char *se_window_argv_space; /* pre-parsed argument array space */
+ char **se_window_argv; /* pre-parsed argument array */
+ char *se_type; /* default terminal type */
+ struct init_session *se_prev;
+ struct init_session *se_next;
+} session_t;
+
+void free_session(session_t *);
+session_t *new_session(session_t *, int, struct ttyent *);
+session_t *sessions;
+
+char **construct_argv(char *);
+void start_window_system(session_t *);
+void collect_child(pid_t);
+pid_t start_getty(session_t *);
+void transition_handler(int);
+void alrm_handler(int);
+void setsecuritylevel(int);
+int getsecuritylevel(void);
+int setupargv(session_t *, struct ttyent *);
+#ifdef LOGIN_CAP
+void setprocresources(const char *);
+#endif
+int clang;
+
+void clear_session_logs(session_t *);
+
+int start_session_db(void);
+void add_session(session_t *);
+void del_session(session_t *);
+session_t *find_session(pid_t);
+DB *session_db;
+
+/*
+ * The mother of all processes.
+ */
+int
+main(int argc, char *argv[])
+{
+ int c;
+ struct sigaction sa;
+ sigset_t mask;
+
+
+ /* Dispose of random users. */
+ if (getuid() != 0)
+ errx(1, "%s", strerror(EPERM));
+
+ /* System V users like to reexec init. */
+ if (getpid() != 1) {
+#ifdef COMPAT_SYSV_INIT
+ /* So give them what they want */
+ if (argc > 1) {
+ if (strlen(argv[1]) == 1) {
+ char runlevel = *argv[1];
+ int sig;
+
+ switch (runlevel) {
+ case '0': /* halt + poweroff */
+ sig = SIGUSR2;
+ break;
+ case '1': /* single-user */
+ sig = SIGTERM;
+ break;
+ case '6': /* reboot */
+ sig = SIGINT;
+ break;
+ case 'c': /* block further logins */
+ sig = SIGTSTP;
+ break;
+ case 'q': /* rescan /etc/ttys */
+ sig = SIGHUP;
+ break;
+ default:
+ goto invalid;
+ }
+ kill(1, sig);
+ _exit(0);
+ } else
+invalid:
+ errx(1, "invalid run-level ``%s''", argv[1]);
+ } else
+#endif
+ errx(1, "already running");
+ }
+ /*
+ * Note that this does NOT open a file...
+ * Does 'init' deserve its own facility number?
+ */
+ openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);
+
+ /*
+ * Create an initial session.
+ */
+ if (setsid() < 0)
+ warning("initial setsid() failed: %m");
+
+ /*
+ * Establish an initial user so that programs running
+ * single user do not freak out and die (like passwd).
+ */
+ if (setlogin("root") < 0)
+ warning("setlogin() failed: %m");
+
+ /*
+ * This code assumes that we always get arguments through flags,
+ * never through bits set in some random machine register.
+ */
+ while ((c = getopt(argc, argv, "dsf")) != -1)
+ switch (c) {
+ case 'd':
+ devfs = 1;
+ break;
+ case 's':
+ requested_transition = single_user;
+ break;
+ case 'f':
+ runcom_mode = FASTBOOT;
+ break;
+ default:
+ warning("unrecognized flag '-%c'", c);
+ break;
+ }
+
+ if (optind != argc)
+ warning("ignoring excess arguments");
+
+ if (devfs) {
+ struct iovec iov[4];
+ char *s;
+ int i;
+
+ char _fstype[] = "fstype";
+ char _devfs[] = "devfs";
+ char _fspath[] = "fspath";
+ char _path_dev[]= _PATH_DEV;
+
+ iov[0].iov_base = _fstype;
+ iov[0].iov_len = sizeof(_fstype);
+ iov[1].iov_base = _devfs;
+ iov[1].iov_len = sizeof(_devfs);
+ iov[2].iov_base = _fspath;
+ iov[2].iov_len = sizeof(_fspath);
+ /*
+ * Try to avoid the trailing slash in _PATH_DEV.
+ * Be *very* defensive.
+ */
+ s = strdup(_PATH_DEV);
+ if (s != NULL) {
+ i = strlen(s);
+ if (i > 0 && s[i - 1] == '/')
+ s[i - 1] = '\0';
+ iov[3].iov_base = s;
+ iov[3].iov_len = strlen(s) + 1;
+ } else {
+ iov[3].iov_base = _path_dev;
+ iov[3].iov_len = sizeof(_path_dev);
+ }
+ nmount(iov, 4, 0);
+ if (s != NULL)
+ free(s);
+ }
+
+ /*
+ * We catch or block signals rather than ignore them,
+ * so that they get reset on exec.
+ */
+ handle(badsys, SIGSYS, 0);
+ handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV,
+ SIGBUS, SIGXCPU, SIGXFSZ, 0);
+ handle(transition_handler, SIGHUP, SIGINT, SIGTERM, SIGTSTP,
+ SIGUSR1, SIGUSR2, 0);
+ handle(alrm_handler, SIGALRM, 0);
+ sigfillset(&mask);
+ delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
+ SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGALRM,
+ SIGUSR1, SIGUSR2, 0);
+ sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = SIG_IGN;
+ (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0);
+ (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0);
+
+ /*
+ * Paranoia.
+ */
+ close(0);
+ close(1);
+ close(2);
+
+ /*
+ * Start the state machine.
+ */
+ transition(requested_transition);
+
+ /*
+ * Should never reach here.
+ */
+ return 1;
+}
+
+/*
+ * Associate a function with a signal handler.
+ */
+void
+handle(sig_t handler, ...)
+{
+ int sig;
+ struct sigaction sa;
+ sigset_t mask_everything;
+ va_list ap;
+ va_start(ap, handler);
+
+ sa.sa_handler = handler;
+ sigfillset(&mask_everything);
+
+ while ((sig = va_arg(ap, int)) != 0) {
+ sa.sa_mask = mask_everything;
+ /* XXX SA_RESTART? */
+ sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0;
+ sigaction(sig, &sa, (struct sigaction *) 0);
+ }
+ va_end(ap);
+}
+
+/*
+ * Delete a set of signals from a mask.
+ */
+void
+delset(sigset_t *maskp, ...)
+{
+ int sig;
+ va_list ap;
+ va_start(ap, maskp);
+
+ while ((sig = va_arg(ap, int)) != 0)
+ sigdelset(maskp, sig);
+ va_end(ap);
+}
+
+/*
+ * Log a message and sleep for a while (to give someone an opportunity
+ * to read it and to save log or hardcopy output if the problem is chronic).
+ * NB: should send a message to the session logger to avoid blocking.
+ */
+void
+stall(const char *message, ...)
+{
+ va_list ap;
+ va_start(ap, message);
+
+ vsyslog(LOG_ALERT, message, ap);
+ va_end(ap);
+ sleep(STALL_TIMEOUT);
+}
+
+/*
+ * Like stall(), but doesn't sleep.
+ * If cpp had variadic macros, the two functions could be #defines for another.
+ * NB: should send a message to the session logger to avoid blocking.
+ */
+void
+warning(const char *message, ...)
+{
+ va_list ap;
+ va_start(ap, message);
+
+ vsyslog(LOG_ALERT, message, ap);
+ va_end(ap);
+}
+
+/*
+ * Log an emergency message.
+ * NB: should send a message to the session logger to avoid blocking.
+ */
+void
+emergency(const char *message, ...)
+{
+ va_list ap;
+ va_start(ap, message);
+
+ vsyslog(LOG_EMERG, message, ap);
+ va_end(ap);
+}
+
+/*
+ * Catch a SIGSYS signal.
+ *
+ * These may arise if a system does not support sysctl.
+ * We tolerate up to 25 of these, then throw in the towel.
+ */
+void
+badsys(int sig)
+{
+ static int badcount = 0;
+
+ if (badcount++ < 25)
+ return;
+ disaster(sig);
+}
+
+/*
+ * Catch an unexpected signal.
+ */
+void
+disaster(int sig)
+{
+ emergency("fatal signal: %s",
+ (unsigned)sig < NSIG ? sys_siglist[sig] : "unknown signal");
+
+ sleep(STALL_TIMEOUT);
+ _exit(sig); /* reboot */
+}
+
+/*
+ * Get the security level of the kernel.
+ */
+int
+getsecuritylevel(void)
+{
+#ifdef KERN_SECURELVL
+ int name[2], curlevel;
+ size_t len;
+
+ name[0] = CTL_KERN;
+ name[1] = KERN_SECURELVL;
+ len = sizeof curlevel;
+ if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) {
+ emergency("cannot get kernel security level: %s",
+ strerror(errno));
+ return (-1);
+ }
+ return (curlevel);
+#else
+ return (-1);
+#endif
+}
+
+/*
+ * Set the security level of the kernel.
+ */
+void
+setsecuritylevel(int newlevel)
+{
+#ifdef KERN_SECURELVL
+ int name[2], curlevel;
+
+ curlevel = getsecuritylevel();
+ if (newlevel == curlevel)
+ return;
+ name[0] = CTL_KERN;
+ name[1] = KERN_SECURELVL;
+ if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) {
+ emergency(
+ "cannot change kernel security level from %d to %d: %s",
+ curlevel, newlevel, strerror(errno));
+ return;
+ }
+#ifdef SECURE
+ warning("kernel security level changed from %d to %d",
+ curlevel, newlevel);
+#endif
+#endif
+}
+
+/*
+ * Change states in the finite state machine.
+ * The initial state is passed as an argument.
+ */
+void
+transition(state_t s)
+{
+ for (;;)
+ s = (state_t) (*s)();
+}
+
+/*
+ * Close out the accounting files for a login session.
+ * NB: should send a message to the session logger to avoid blocking.
+ */
+void
+clear_session_logs(session_t *sp)
+{
+ char *line = sp->se_device + sizeof(_PATH_DEV) - 1;
+
+ if (logout(line))
+ logwtmp(line, "", "");
+}
+
+/*
+ * Start a session and allocate a controlling terminal.
+ * Only called by children of init after forking.
+ */
+void
+setctty(const char *name)
+{
+ int fd;
+
+ (void) revoke(name);
+ if ((fd = open(name, O_RDWR)) == -1) {
+ stall("can't open %s: %m", name);
+ _exit(1);
+ }
+ if (login_tty(fd) == -1) {
+ stall("can't get %s for controlling terminal: %m", name);
+ _exit(1);
+ }
+}
+
+/*
+ * Bring the system up single user.
+ */
+state_func_t
+single_user(void)
+{
+ pid_t pid, wpid;
+ int status;
+ sigset_t mask;
+ const char *shell = _PATH_BSHELL;
+ char *argv[2];
+#ifdef SECURE
+ struct ttyent *typ;
+ struct passwd *pp;
+ static const char banner[] =
+ "Enter root password, or ^D to go multi-user\n";
+ char *clear, *password;
+#endif
+#ifdef DEBUGSHELL
+ char altshell[128];
+#endif
+
+ if (Reboot) {
+ /* Instead of going single user, let's reboot the machine */
+ sync();
+ alarm(2);
+ pause();
+ reboot(howto);
+ _exit(0);
+ }
+
+ if ((pid = fork()) == 0) {
+ /*
+ * Start the single user session.
+ */
+ setctty(_PATH_CONSOLE);
+
+#ifdef SECURE
+ /*
+ * Check the root password.
+ * We don't care if the console is 'on' by default;
+ * it's the only tty that can be 'off' and 'secure'.
+ */
+ typ = getttynam("console");
+ pp = getpwnam("root");
+ if (typ && (typ->ty_status & TTY_SECURE) == 0 &&
+ pp && *pp->pw_passwd) {
+ write(STDERR_FILENO, banner, sizeof banner - 1);
+ for (;;) {
+ clear = getpass("Password:");
+ if (clear == 0 || *clear == '\0')
+ _exit(0);
+ password = crypt(clear, pp->pw_passwd);
+ bzero(clear, _PASSWORD_LEN);
+ if (strcmp(password, pp->pw_passwd) == 0)
+ break;
+ warning("single-user login failed\n");
+ }
+ }
+ endttyent();
+ endpwent();
+#endif /* SECURE */
+
+#ifdef DEBUGSHELL
+ {
+ char *cp = altshell;
+ int num;
+
+#define SHREQUEST \
+ "Enter full pathname of shell or RETURN for " _PATH_BSHELL ": "
+ (void)write(STDERR_FILENO,
+ SHREQUEST, sizeof(SHREQUEST) - 1);
+ while ((num = read(STDIN_FILENO, cp, 1)) != -1 &&
+ num != 0 && *cp != '\n' && cp < &altshell[127])
+ cp++;
+ *cp = '\0';
+ if (altshell[0] != '\0')
+ shell = altshell;
+ }
+#endif /* DEBUGSHELL */
+
+ /*
+ * Unblock signals.
+ * We catch all the interesting ones,
+ * and those are reset to SIG_DFL on exec.
+ */
+ sigemptyset(&mask);
+ sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
+
+ /*
+ * Fire off a shell.
+ * If the default one doesn't work, try the Bourne shell.
+ */
+
+ char name[] = "-sh";
+
+ argv[0] = name;
+ argv[1] = 0;
+ execv(shell, argv);
+ emergency("can't exec %s for single user: %m", shell);
+ execv(_PATH_BSHELL, argv);
+ emergency("can't exec %s for single user: %m", _PATH_BSHELL);
+ sleep(STALL_TIMEOUT);
+ _exit(1);
+ }
+
+ if (pid == -1) {
+ /*
+ * We are seriously hosed. Do our best.
+ */
+ emergency("can't fork single-user shell, trying again");
+ while (waitpid(-1, (int *) 0, WNOHANG) > 0)
+ continue;
+ return (state_func_t) single_user;
+ }
+
+ requested_transition = 0;
+ do {
+ if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
+ collect_child(wpid);
+ if (wpid == -1) {
+ if (errno == EINTR)
+ continue;
+ warning("wait for single-user shell failed: %m; restarting");
+ return (state_func_t) single_user;
+ }
+ if (wpid == pid && WIFSTOPPED(status)) {
+ warning("init: shell stopped, restarting\n");
+ kill(pid, SIGCONT);
+ wpid = -1;
+ }
+ } while (wpid != pid && !requested_transition);
+
+ if (requested_transition)
+ return (state_func_t) requested_transition;
+
+ if (!WIFEXITED(status)) {
+ if (WTERMSIG(status) == SIGKILL) {
+ /*
+ * reboot(8) killed shell?
+ */
+ warning("single user shell terminated.");
+ sleep(STALL_TIMEOUT);
+ _exit(0);
+ } else {
+ warning("single user shell terminated, restarting");
+ return (state_func_t) single_user;
+ }
+ }
+
+ runcom_mode = FASTBOOT;
+ return (state_func_t) runcom;
+}
+
+/*
+ * Run the system startup script.
+ */
+state_func_t
+runcom(void)
+{
+ pid_t pid, wpid;
+ int status;
+ char *argv[4];
+ struct sigaction sa;
+
+ if ((pid = fork()) == 0) {
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = SIG_IGN;
+ (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0);
+ (void) sigaction(SIGHUP, &sa, (struct sigaction *)0);
+
+ setctty(_PATH_CONSOLE);
+
+ char _sh[] = "sh";
+ char _path_runcom[] = _PATH_RUNCOM;
+ char _autoboot[] = "autoboot";
+
+ argv[0] = _sh;
+ argv[1] = _path_runcom;
+ argv[2] = runcom_mode == AUTOBOOT ? _autoboot : 0;
+ argv[3] = 0;
+
+ sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
+
+#ifdef LOGIN_CAP
+ setprocresources(RESOURCE_RC);
+#endif
+ execv(_PATH_BSHELL, argv);
+ stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM);
+ _exit(1); /* force single user mode */
+ }
+
+ if (pid == -1) {
+ emergency("can't fork for %s on %s: %m",
+ _PATH_BSHELL, _PATH_RUNCOM);
+ while (waitpid(-1, (int *) 0, WNOHANG) > 0)
+ continue;
+ sleep(STALL_TIMEOUT);
+ return (state_func_t) single_user;
+ }
+
+ /*
+ * Copied from single_user(). This is a bit paranoid.
+ */
+ requested_transition = 0;
+ do {
+ if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
+ collect_child(wpid);
+ if (wpid == -1) {
+ if (requested_transition == death)
+ return (state_func_t) death;
+ if (errno == EINTR)
+ continue;
+ warning("wait for %s on %s failed: %m; going to single user mode",
+ _PATH_BSHELL, _PATH_RUNCOM);
+ return (state_func_t) single_user;
+ }
+ if (wpid == pid && WIFSTOPPED(status)) {
+ warning("init: %s on %s stopped, restarting\n",
+ _PATH_BSHELL, _PATH_RUNCOM);
+ kill(pid, SIGCONT);
+ wpid = -1;
+ }
+ } while (wpid != pid);
+
+ if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM &&
+ requested_transition == catatonia) {
+ /* /etc/rc executed /sbin/reboot; wait for the end quietly */
+ sigset_t s;
+
+ sigfillset(&s);
+ for (;;)
+ sigsuspend(&s);
+ }
+
+ if (!WIFEXITED(status)) {
+ warning("%s on %s terminated abnormally, going to single user mode",
+ _PATH_BSHELL, _PATH_RUNCOM);
+ return (state_func_t) single_user;
+ }
+
+ if (WEXITSTATUS(status))
+ return (state_func_t) single_user;
+
+ runcom_mode = AUTOBOOT; /* the default */
+ /* NB: should send a message to the session logger to avoid blocking. */
+ logwtmp("~", "reboot", "");
+ return (state_func_t) read_ttys;
+}
+
+/*
+ * Open the session database.
+ *
+ * NB: We could pass in the size here; is it necessary?
+ */
+int
+start_session_db(void)
+{
+ if (session_db && (*session_db->close)(session_db))
+ emergency("session database close: %s", strerror(errno));
+ if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) {
+ emergency("session database open: %s", strerror(errno));
+ return (1);
+ }
+ return (0);
+
+}
+
+/*
+ * Add a new login session.
+ */
+void
+add_session(session_t *sp)
+{
+ DBT key;
+ DBT data;
+
+ key.data = &sp->se_process;
+ key.size = sizeof sp->se_process;
+ data.data = &sp;
+ data.size = sizeof sp;
+
+ if ((*session_db->put)(session_db, &key, &data, 0))
+ emergency("insert %d: %s", sp->se_process, strerror(errno));
+}
+
+/*
+ * Delete an old login session.
+ */
+void
+del_session(session_t *sp)
+{
+ DBT key;
+
+ key.data = &sp->se_process;
+ key.size = sizeof sp->se_process;
+
+ if ((*session_db->del)(session_db, &key, 0))
+ emergency("delete %d: %s", sp->se_process, strerror(errno));
+}
+
+/*
+ * Look up a login session by pid.
+ */
+session_t *
+find_session(pid_t pid)
+{
+ DBT key;
+ DBT data;
+ session_t *ret;
+
+ key.data = &pid;
+ key.size = sizeof pid;
+ if ((*session_db->get)(session_db, &key, &data, 0) != 0)
+ return 0;
+ bcopy(data.data, (char *)&ret, sizeof(ret));
+ return ret;
+}
+
+/*
+ * Construct an argument vector from a command line.
+ */
+char **
+construct_argv(char *command)
+{
+ int argc = 0;
+ char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1)
+ * sizeof (char *));
+
+ if ((argv[argc++] = strk(command)) == 0) {
+ free(argv);
+ return (NULL);
+ }
+ while ((argv[argc++] = strk((char *) 0)) != NULL)
+ continue;
+ return argv;
+}
+
+/*
+ * Deallocate a session descriptor.
+ */
+void
+free_session(session_t *sp)
+{
+ free(sp->se_device);
+ if (sp->se_getty) {
+ free(sp->se_getty);
+ free(sp->se_getty_argv_space);
+ free(sp->se_getty_argv);
+ }
+ if (sp->se_window) {
+ free(sp->se_window);
+ free(sp->se_window_argv_space);
+ free(sp->se_window_argv);
+ }
+ if (sp->se_type)
+ free(sp->se_type);
+ free(sp);
+}
+
+/*
+ * Allocate a new session descriptor.
+ * Mark it SE_PRESENT.
+ */
+session_t *
+new_session(session_t *sprev, int session_index, struct ttyent *typ)
+{
+ session_t *sp;
+ int fd;
+
+ if ((typ->ty_status & TTY_ON) == 0 ||
+ typ->ty_name == 0 ||
+ typ->ty_getty == 0)
+ return 0;
+
+ sp = (session_t *) calloc(1, sizeof (session_t));
+
+ sp->se_index = session_index;
+ sp->se_flags |= SE_PRESENT;
+
+ sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name));
+ (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name);
+
+ /*
+ * Attempt to open the device, if we get "device not configured"
+ * then don't add the device to the session list.
+ */
+ if ((fd = open(sp->se_device, O_RDONLY | O_NONBLOCK, 0)) < 0) {
+ if (errno == ENXIO) {
+ free_session(sp);
+ return (0);
+ }
+ } else
+ close(fd);
+
+ if (setupargv(sp, typ) == 0) {
+ free_session(sp);
+ return (0);
+ }
+
+ sp->se_next = 0;
+ if (sprev == 0) {
+ sessions = sp;
+ sp->se_prev = 0;
+ } else {
+ sprev->se_next = sp;
+ sp->se_prev = sprev;
+ }
+
+ return sp;
+}
+
+/*
+ * Calculate getty and if useful window argv vectors.
+ */
+int
+setupargv(session_t *sp, struct ttyent *typ)
+{
+
+ if (sp->se_getty) {
+ free(sp->se_getty);
+ free(sp->se_getty_argv_space);
+ free(sp->se_getty_argv);
+ }
+ sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2);
+ (void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name);
+ sp->se_getty_argv_space = strdup(sp->se_getty);
+ sp->se_getty_argv = construct_argv(sp->se_getty_argv_space);
+ if (sp->se_getty_argv == 0) {
+ warning("can't parse getty for port %s", sp->se_device);
+ free(sp->se_getty);
+ free(sp->se_getty_argv_space);
+ sp->se_getty = sp->se_getty_argv_space = 0;
+ return (0);
+ }
+ if (sp->se_window) {
+ free(sp->se_window);
+ free(sp->se_window_argv_space);
+ free(sp->se_window_argv);
+ }
+ sp->se_window = sp->se_window_argv_space = 0;
+ sp->se_window_argv = 0;
+ if (typ->ty_window) {
+ sp->se_window = strdup(typ->ty_window);
+ sp->se_window_argv_space = strdup(sp->se_window);
+ sp->se_window_argv = construct_argv(sp->se_window_argv_space);
+ if (sp->se_window_argv == 0) {
+ warning("can't parse window for port %s",
+ sp->se_device);
+ free(sp->se_window_argv_space);
+ free(sp->se_window);
+ sp->se_window = sp->se_window_argv_space = 0;
+ return (0);
+ }
+ }
+ if (sp->se_type)
+ free(sp->se_type);
+ sp->se_type = typ->ty_type ? strdup(typ->ty_type) : 0;
+ return (1);
+}
+
+/*
+ * Walk the list of ttys and create sessions for each active line.
+ */
+state_func_t
+read_ttys(void)
+{
+ int session_index = 0;
+ session_t *sp, *snext;
+ struct ttyent *typ;
+
+ /*
+ * Destroy any previous session state.
+ * There shouldn't be any, but just in case...
+ */
+ for (sp = sessions; sp; sp = snext) {
+ if (sp->se_process)
+ clear_session_logs(sp);
+ snext = sp->se_next;
+ free_session(sp);
+ }
+ sessions = 0;
+ if (start_session_db())
+ return (state_func_t) single_user;
+
+ /*
+ * Allocate a session entry for each active port.
+ * Note that sp starts at 0.
+ */
+ while ((typ = getttyent()) != NULL)
+ if ((snext = new_session(sp, ++session_index, typ)) != NULL)
+ sp = snext;
+
+ endttyent();
+
+ return (state_func_t) multi_user;
+}
+
+/*
+ * Start a window system running.
+ */
+void
+start_window_system(session_t *sp)
+{
+ pid_t pid;
+ sigset_t mask;
+ char term[64], *env[2];
+
+ if ((pid = fork()) == -1) {
+ emergency("can't fork for window system on port %s: %m",
+ sp->se_device);
+ /* hope that getty fails and we can try again */
+ return;
+ }
+
+ if (pid)
+ return;
+
+ sigemptyset(&mask);
+ sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
+
+ if (setsid() < 0)
+ emergency("setsid failed (window) %m");
+
+#ifdef LOGIN_CAP
+ setprocresources(RESOURCE_WINDOW);
+#endif
+ if (sp->se_type) {
+ /* Don't use malloc after fork */
+ strcpy(term, "TERM=");
+ strncat(term, sp->se_type, sizeof(term) - 6);
+ env[0] = term;
+ env[1] = 0;
+ }
+ else
+ env[0] = 0;
+ execve(sp->se_window_argv[0], sp->se_window_argv, env);
+ stall("can't exec window system '%s' for port %s: %m",
+ sp->se_window_argv[0], sp->se_device);
+ _exit(1);
+}
+
+/*
+ * Start a login session running.
+ */
+pid_t
+start_getty(session_t *sp)
+{
+ pid_t pid;
+ sigset_t mask;
+ time_t current_time = time((time_t *) 0);
+ int too_quick = 0;
+ char term[64], *env[2];
+
+ if (current_time >= sp->se_started &&
+ current_time - sp->se_started < GETTY_SPACING) {
+ if (++sp->se_nspace > GETTY_NSPACE) {
+ sp->se_nspace = 0;
+ too_quick = 1;
+ }
+ } else
+ sp->se_nspace = 0;
+
+ /*
+ * fork(), not vfork() -- we can't afford to block.
+ */
+ if ((pid = fork()) == -1) {
+ emergency("can't fork for getty on port %s: %m", sp->se_device);
+ return -1;
+ }
+
+ if (pid)
+ return pid;
+
+ if (too_quick) {
+ warning("getty repeating too quickly on port %s, sleeping %d secs",
+ sp->se_device, GETTY_SLEEP);
+ sleep((unsigned) GETTY_SLEEP);
+ }
+
+ if (sp->se_window) {
+ start_window_system(sp);
+ sleep(WINDOW_WAIT);
+ }
+
+ sigemptyset(&mask);
+ sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
+
+#ifdef LOGIN_CAP
+ setprocresources(RESOURCE_GETTY);
+#endif
+ if (sp->se_type) {
+ /* Don't use malloc after fork */
+ strcpy(term, "TERM=");
+ strncat(term, sp->se_type, sizeof(term) - 6);
+ env[0] = term;
+ env[1] = 0;
+ }
+ else
+ env[0] = 0;
+ execve(sp->se_getty_argv[0], sp->se_getty_argv, env);
+ stall("can't exec getty '%s' for port %s: %m",
+ sp->se_getty_argv[0], sp->se_device);
+ _exit(1);
+}
+
+/*
+ * Collect exit status for a child.
+ * If an exiting login, start a new login running.
+ */
+void
+collect_child(pid_t pid)
+{
+ session_t *sp, *sprev, *snext;
+
+ if (! sessions)
+ return;
+
+ if (! (sp = find_session(pid)))
+ return;
+
+ clear_session_logs(sp);
+ del_session(sp);
+ sp->se_process = 0;
+
+ if (sp->se_flags & SE_SHUTDOWN) {
+ if ((sprev = sp->se_prev) != NULL)
+ sprev->se_next = sp->se_next;
+ else
+ sessions = sp->se_next;
+ if ((snext = sp->se_next) != NULL)
+ snext->se_prev = sp->se_prev;
+ free_session(sp);
+ return;
+ }
+
+ if ((pid = start_getty(sp)) == -1) {
+ /* serious trouble */
+ requested_transition = clean_ttys;
+ return;
+ }
+
+ sp->se_process = pid;
+ sp->se_started = time((time_t *) 0);
+ add_session(sp);
+}
+
+/*
+ * Catch a signal and request a state transition.
+ */
+void
+transition_handler(int sig)
+{
+
+ switch (sig) {
+ case SIGHUP:
+ requested_transition = clean_ttys;
+ break;
+ case SIGUSR2:
+ howto = RB_POWEROFF;
+ case SIGUSR1:
+ howto |= RB_HALT;
+ case SIGINT:
+ Reboot = TRUE;
+ case SIGTERM:
+ requested_transition = death;
+ break;
+ case SIGTSTP:
+ requested_transition = catatonia;
+ break;
+ default:
+ requested_transition = 0;
+ break;
+ }
+}
+
+/*
+ * Take the system multiuser.
+ */
+state_func_t
+multi_user(void)
+{
+ pid_t pid;
+ session_t *sp;
+
+ requested_transition = 0;
+
+ /*
+ * If the administrator has not set the security level to -1
+ * to indicate that the kernel should not run multiuser in secure
+ * mode, and the run script has not set a higher level of security
+ * than level 1, then put the kernel into secure mode.
+ */
+ if (getsecuritylevel() == 0)
+ setsecuritylevel(1);
+
+ for (sp = sessions; sp; sp = sp->se_next) {
+ if (sp->se_process)
+ continue;
+ if ((pid = start_getty(sp)) == -1) {
+ /* serious trouble */
+ requested_transition = clean_ttys;
+ break;
+ }
+ sp->se_process = pid;
+ sp->se_started = time((time_t *) 0);
+ add_session(sp);
+ }
+
+ while (!requested_transition)
+ if ((pid = waitpid(-1, (int *) 0, 0)) != -1)
+ collect_child(pid);
+
+ return (state_func_t) requested_transition;
+}
+
+/*
+ * This is an (n*2)+(n^2) algorithm. We hope it isn't run often...
+ */
+state_func_t
+clean_ttys(void)
+{
+ session_t *sp, *sprev;
+ struct ttyent *typ;
+ int session_index = 0;
+ int devlen;
+ char *old_getty, *old_window, *old_type;
+
+ /*
+ * mark all sessions for death, (!SE_PRESENT)
+ * as we find or create new ones they'll be marked as keepers,
+ * we'll later nuke all the ones not found in /etc/ttys
+ */
+ for (sp = sessions; sp != NULL; sp = sp->se_next)
+ sp->se_flags &= ~SE_PRESENT;
+
+ devlen = sizeof(_PATH_DEV) - 1;
+ while ((typ = getttyent()) != NULL) {
+ ++session_index;
+
+ for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next)
+ if (strcmp(typ->ty_name, sp->se_device + devlen) == 0)
+ break;
+
+ if (sp) {
+ /* we want this one to live */
+ sp->se_flags |= SE_PRESENT;
+ if (sp->se_index != session_index) {
+ warning("port %s changed utmp index from %d to %d",
+ sp->se_device, sp->se_index,
+ session_index);
+ sp->se_index = session_index;
+ }
+ if ((typ->ty_status & TTY_ON) == 0 ||
+ typ->ty_getty == 0) {
+ sp->se_flags |= SE_SHUTDOWN;
+ kill(sp->se_process, SIGHUP);
+ continue;
+ }
+ sp->se_flags &= ~SE_SHUTDOWN;
+ old_getty = sp->se_getty ? strdup(sp->se_getty) : 0;
+ old_window = sp->se_window ? strdup(sp->se_window) : 0;
+ old_type = sp->se_type ? strdup(sp->se_type) : 0;
+ if (setupargv(sp, typ) == 0) {
+ warning("can't parse getty for port %s",
+ sp->se_device);
+ sp->se_flags |= SE_SHUTDOWN;
+ kill(sp->se_process, SIGHUP);
+ }
+ else if ( !old_getty
+ || (!old_type && sp->se_type)
+ || (old_type && !sp->se_type)
+ || (!old_window && sp->se_window)
+ || (old_window && !sp->se_window)
+ || (strcmp(old_getty, sp->se_getty) != 0)
+ || (old_window && strcmp(old_window, sp->se_window) != 0)
+ || (old_type && strcmp(old_type, sp->se_type) != 0)
+ ) {
+ /* Don't set SE_SHUTDOWN here */
+ sp->se_nspace = 0;
+ sp->se_started = 0;
+ kill(sp->se_process, SIGHUP);
+ }
+ if (old_getty)
+ free(old_getty);
+ if (old_window)
+ free(old_window);
+ if (old_type)
+ free(old_type);
+ continue;
+ }
+
+ new_session(sprev, session_index, typ);
+ }
+
+ endttyent();
+
+ /*
+ * sweep through and kill all deleted sessions
+ * ones who's /etc/ttys line was deleted (SE_PRESENT unset)
+ */
+ for (sp = sessions; sp != NULL; sp = sp->se_next) {
+ if ((sp->se_flags & SE_PRESENT) == 0) {
+ sp->se_flags |= SE_SHUTDOWN;
+ kill(sp->se_process, SIGHUP);
+ }
+ }
+
+ return (state_func_t) multi_user;
+}
+
+/*
+ * Block further logins.
+ */
+state_func_t
+catatonia(void)
+{
+ session_t *sp;
+
+ for (sp = sessions; sp; sp = sp->se_next)
+ sp->se_flags |= SE_SHUTDOWN;
+
+ return (state_func_t) multi_user;
+}
+
+/*
+ * Note SIGALRM.
+ */
+void
+alrm_handler(int sig)
+{
+ (void)sig;
+ clang = 1;
+}
+
+/*
+ * Bring the system down to single user.
+ */
+state_func_t
+death(void)
+{
+ session_t *sp;
+ int i;
+ pid_t pid;
+ static const int death_sigs[2] = { SIGTERM, SIGKILL };
+
+ /* NB: should send a message to the session logger to avoid blocking. */
+ logwtmp("~", "shutdown", "");
+
+ for (sp = sessions; sp; sp = sp->se_next) {
+ sp->se_flags |= SE_SHUTDOWN;
+ kill(sp->se_process, SIGHUP);
+ }
+
+ /* Try to run the rc.shutdown script within a period of time */
+ (void) runshutdown();
+
+ for (i = 0; i < 2; ++i) {
+ if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
+ return (state_func_t) single_user;
+
+ clang = 0;
+ alarm(DEATH_WATCH);
+ do
+ if ((pid = waitpid(-1, (int *)0, 0)) != -1)
+ collect_child(pid);
+ while (clang == 0 && errno != ECHILD);
+
+ if (errno == ECHILD)
+ return (state_func_t) single_user;
+ }
+
+ warning("some processes would not die; ps axl advised");
+
+ return (state_func_t) single_user;
+}
+
+/*
+ * Run the system shutdown script.
+ *
+ * Exit codes: XXX I should document more
+ * -2 shutdown script terminated abnormally
+ * -1 fatal error - can't run script
+ * 0 good.
+ * >0 some error (exit code)
+ */
+int
+runshutdown(void)
+{
+ pid_t pid, wpid;
+ int status;
+ int shutdowntimeout;
+ size_t len;
+ char *argv[4];
+ struct sigaction sa;
+ struct stat sb;
+
+ /*
+ * rc.shutdown is optional, so to prevent any unnecessary
+ * complaints from the shell we simply don't run it if the
+ * file does not exist. If the stat() here fails for other
+ * reasons, we'll let the shell complain.
+ */
+ if (stat(_PATH_RUNDOWN, &sb) == -1 && errno == ENOENT)
+ return 0;
+
+ if ((pid = fork()) == 0) {
+ int fd;
+
+ /* Assume that init already grab console as ctty before */
+
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = SIG_IGN;
+ (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0);
+ (void) sigaction(SIGHUP, &sa, (struct sigaction *)0);
+
+ if ((fd = open(_PATH_CONSOLE, O_RDWR)) == -1)
+ warning("can't open %s: %m", _PATH_CONSOLE);
+ else {
+ (void) dup2(fd, 0);
+ (void) dup2(fd, 1);
+ (void) dup2(fd, 2);
+ if (fd > 2)
+ close(fd);
+ }
+
+ /*
+ * Run the shutdown script.
+ */
+
+ char _sh[] = "sh";
+ char _reboot[] = "reboot";
+ char _single[] = "single";
+ char _path_rundown[] = _PATH_RUNDOWN;
+
+ argv[0] = _sh;
+ argv[1] = _path_rundown;
+ argv[2] = Reboot ? _reboot : _single;
+ argv[3] = 0;
+
+ sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
+
+#ifdef LOGIN_CAP
+ setprocresources(RESOURCE_RC);
+#endif
+ execv(_PATH_BSHELL, argv);
+ warning("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNDOWN);
+ _exit(1); /* force single user mode */
+ }
+
+ if (pid == -1) {
+ emergency("can't fork for %s on %s: %m",
+ _PATH_BSHELL, _PATH_RUNDOWN);
+ while (waitpid(-1, (int *) 0, WNOHANG) > 0)
+ continue;
+ sleep(STALL_TIMEOUT);
+ return -1;
+ }
+
+ len = sizeof(shutdowntimeout);
+ if (sysctlbyname("kern.init_shutdown_timeout",
+ &shutdowntimeout,
+ &len, NULL, 0) == -1 || shutdowntimeout < 2)
+ shutdowntimeout = DEATH_SCRIPT;
+ alarm(shutdowntimeout);
+ clang = 0;
+ /*
+ * Copied from single_user(). This is a bit paranoid.
+ * Use the same ALRM handler.
+ */
+ do {
+ if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
+ collect_child(wpid);
+ if (clang == 1) {
+ /* we were waiting for the sub-shell */
+ kill(wpid, SIGTERM);
+ warning("timeout expired for %s on %s: %m; going to single user mode",
+ _PATH_BSHELL, _PATH_RUNDOWN);
+ return -1;
+ }
+ if (wpid == -1) {
+ if (errno == EINTR)
+ continue;
+ warning("wait for %s on %s failed: %m; going to single user mode",
+ _PATH_BSHELL, _PATH_RUNDOWN);
+ return -1;
+ }
+ if (wpid == pid && WIFSTOPPED(status)) {
+ warning("init: %s on %s stopped, restarting\n",
+ _PATH_BSHELL, _PATH_RUNDOWN);
+ kill(pid, SIGCONT);
+ wpid = -1;
+ }
+ } while (wpid != pid && !clang);
+
+ /* Turn off the alarm */
+ alarm(0);
+
+ if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM &&
+ requested_transition == catatonia) {
+ /*
+ * /etc/rc.shutdown executed /sbin/reboot;
+ * wait for the end quietly
+ */
+ sigset_t s;
+
+ sigfillset(&s);
+ for (;;)
+ sigsuspend(&s);
+ }
+
+ if (!WIFEXITED(status)) {
+ warning("%s on %s terminated abnormally, going to single user mode",
+ _PATH_BSHELL, _PATH_RUNDOWN);
+ return -2;
+ }
+
+ if ((status = WEXITSTATUS(status)) != 0)
+ warning("%s returned status %d", _PATH_RUNDOWN, status);
+
+ return status;
+}
+
+static char *
+strk(char *p)
+{
+ static char *t;
+ char *q;
+ int c;
+
+ if (p)
+ t = p;
+ if (!t)
+ return 0;
+
+ c = *t;
+ while (c == ' ' || c == '\t' )
+ c = *++t;
+ if (!c) {
+ t = 0;
+ return 0;
+ }
+ q = t;
+ if (c == '\'') {
+ c = *++t;
+ q = t;
+ while (c && c != '\'')
+ c = *++t;
+ if (!c) /* unterminated string */
+ q = t = 0;
+ else
+ *t++ = 0;
+ } else {
+ while (c && c != ' ' && c != '\t' )
+ c = *++t;
+ *t++ = 0;
+ if (!c)
+ t = 0;
+ }
+ return q;
+}
+
+#ifdef LOGIN_CAP
+void
+setprocresources(const char *cname)
+{
+ login_cap_t *lc;
+ if ((lc = login_getclassbyname(cname, NULL)) != NULL) {
+ setusercontext(lc, (struct passwd*)NULL, 0, LOGIN_SETPRIORITY|LOGIN_SETRESOURCES);
+ login_close(lc);
+ }
+}
+#endif
diff --git a/sbin/init/pathnames.h b/sbin/init/pathnames.h
new file mode 100644
index 0000000..335445b
--- /dev/null
+++ b/sbin/init/pathnames.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Donn Seeley at Berkeley Software Design, 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.
+ * 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 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.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/5/93
+ * $FreeBSD$
+ */
+
+#include <paths.h>
+
+#define _PATH_SLOGGER "/sbin/session_logger"
+#define _PATH_RUNCOM "/etc/rc"
+#define _PATH_RUNDOWN "/etc/rc.shutdown"
diff --git a/sbin/ip6fw/Makefile b/sbin/ip6fw/Makefile
new file mode 100644
index 0000000..17f330d
--- /dev/null
+++ b/sbin/ip6fw/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= ip6fw
+WARNS?= 2
+MAN= ip6fw.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/ip6fw/ip6fw.8 b/sbin/ip6fw/ip6fw.8
new file mode 100644
index 0000000..9d296cf
--- /dev/null
+++ b/sbin/ip6fw/ip6fw.8
@@ -0,0 +1,582 @@
+.\"
+.\" $FreeBSD$
+.\"
+.\" $KAME$
+.\"
+.\" Copyright (C) 1998, 1999, 2000 and 2001 WIDE 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.
+.\" 3. Neither the name of the project 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 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.
+.\"
+.Dd March 13, 2000
+.Dt IP6FW 8
+.Os
+.Sh NAME
+.Nm ip6fw
+.Nd controlling utility for IPv6 firewall
+.Sh SYNOPSIS
+.Nm
+.Op Fl nq
+.Oo
+.Fl p Ar preproc
+.Oo Fl D
+.Ar macro Ns Op = Ns Ar value
+.Oc
+.Op Fl U Ar macro
+.Oc
+.Ar pathname
+.Nm
+.Op Fl n
+.Op Fl f | Fl q
+flush
+.Nm
+.Op Fl nq
+zero
+.Op Ar number ...
+.Nm
+.Op Fl n
+delete
+.Ar number ...
+.Nm
+.Op Fl aftN
+list
+.Op Ar number ...
+.Nm
+.Op Fl ftN
+show
+.Op Ar number ...
+.Nm
+.Op Fl nq
+add
+.Op Ar number
+.Ar action
+.Op log
+.Ar proto
+from
+.Ar src
+to
+.Ar dst
+.Op via Ar name | ipv6no
+.Op Ar options
+.Sh DESCRIPTION
+To ease configuration, rules can be put into a file which is
+processed using
+.Nm
+as shown in the first synopsis line.
+An absolute
+.Ar pathname
+must be used.
+The file
+will be read line by line and applied as arguments to the
+.Nm
+utility.
+.Pp
+Optionally, a preprocessor can be specified using
+.Fl p Ar preproc
+where
+.Ar pathname
+is to be piped through.
+Useful preprocessors include
+.Xr cpp 1
+and
+.Xr m4 1 .
+If
+.Ar preproc
+does not start with a slash
+.Pq Ql /
+as its first character, the usual
+.Ev PATH
+name search is performed.
+Care should be taken with this in environments where not all
+file systems are mounted (yet) by the time
+.Nm
+is being run (e.g.\& when they are mounted over NFS).
+Once
+.Fl p
+has been specified, optional
+.Fl D
+and
+.Fl U
+specifications can follow and will be passed on to the preprocessor.
+This allows for flexible configuration files (like conditionalizing
+them on the local hostname) and the use of macros to centralize
+frequently required arguments like IP addresses.
+.Pp
+The
+.Nm
+code works by going through the rule-list for each packet,
+until a match is found.
+All rules have two associated counters, a packet count and
+a byte count.
+These counters are updated when a packet matches the rule.
+.Pp
+The rules are ordered by a
+.Dq line-number
+from 1 to 65534 that is used
+to order and delete rules.
+Rules are tried in increasing order, and the
+first rule that matches a packet applies.
+Multiple rules may share the same number and apply in
+the order in which they were added.
+.Pp
+If a rule is added without a number, it is numbered 100 higher
+than the previous rule.
+If the highest defined rule number is
+greater than 65434, new rules are appended to the last rule.
+.Pp
+The delete operation deletes the first rule with number
+.Ar number ,
+if any.
+.Pp
+The list command prints out the current rule set.
+.Pp
+The show command is equivalent to `ip6fw -a list'.
+.Pp
+The zero operation zeroes the counters associated with rule number
+.Ar number .
+.Pp
+The flush operation removes all rules.
+.Pp
+Any command beginning with a
+.Sq # ,
+or being all blank, is ignored.
+.Pp
+One rule is always present:
+.Bd -literal -offset center
+65535 deny all from any to any
+.Ed
+.Pp
+This rule is the default policy, i.e., do not allow anything at all.
+Your job in setting up rules is to modify this policy to match your
+needs.
+.Pp
+The following options are available:
+.Bl -tag -width flag
+.It Fl a
+While listing, show counter values.
+See also
+.Dq show
+command.
+.It Fl f
+Do not ask for confirmation for commands that can cause problems if misused
+(ie; flush).
+.Ar Note ,
+if there is no tty associated with the process, this is implied.
+.It Fl n
+Only check syntax of the command strings,
+without actually passing them into the kernel.
+.It Fl q
+While adding, zeroing or flushing, be quiet about actions (implies '-f').
+This is useful for adjusting rules by executing multiple ip6fw commands in a
+script (e.g.\& sh /etc/rc.firewall), or by processing a file of many ip6fw rules,
+across a remote login session.
+If a flush is performed in normal
+(verbose) mode, it prints a message.
+Because all rules are flushed, the
+message cannot be delivered to the login session, the login session is
+closed and the remainder of the ruleset is not processed.
+Access to the
+console is required to recover.
+.It Fl t
+While listing, show last match timestamp.
+.It Fl N
+Try to resolve addresses and service names in output.
+.El
+.Pp
+.Ar action :
+.Bl -hang -offset flag -width 16n
+.It Ar allow
+Allow packets that match rule.
+The search terminates.
+Aliases are
+.Ar pass ,
+.Ar permit ,
+and
+.Ar accept .
+.It Ar deny
+Discard packets that match this rule.
+The search terminates.
+.Ar Drop
+is an alias for
+.Ar deny .
+.It Ar reject
+(Deprecated.)
+Discard packets that match this rule, and try to send an ICMPv6
+host unreachable notice.
+The search terminates.
+.It Ar unreach code
+Discard packets that match this rule, and try to send an ICMPv6
+unreachable notice with code
+.Ar code ,
+where
+.Ar code
+is a number from zero to 255, or one of these aliases:
+.Ar noroute ,
+.Ar admin ,
+.Ar notneighbor ,
+.Ar addr ,
+or
+.Ar noport ,
+The search terminates.
+.It Ar reset
+TCP packets only.
+Discard packets that match this rule,
+and try to send a TCP reset (RST) notice.
+The search terminates
+.It Ar count
+Update counters for all packets that match rule.
+The search continues with the next rule.
+.It Ar skipto number
+Skip all subsequent rules numbered less than
+.Ar number .
+The search continues with the first rule numbered
+.Ar number
+or higher.
+.El
+.Pp
+If the kernel was compiled with
+.Dv IPV6FIREWALL_VERBOSE ,
+then when a packet matches a rule with the
+.Dq log
+keyword or a clear/resetlog is performed, a message will be logged to
+.Xr syslogd 8 ,
+or, if that fails, to the console.
+If the kernel was compiled with the
+.Dv IPV6FIREWALL_VERBOSE_LIMIT
+option, then logging will cease after the number of packets
+specified by the option are received for that particular
+chain entry.
+When this limit is reached, the limit and rule number will be logged.
+Logging may then be re-enabled by clearing
+the packet counter for that entry.
+.Pp
+The
+.Xr syslogd 8
+logging and the default log limit are adjustable dynamically through the
+.Xr sysctl 8
+interface.
+.Pp
+.Ar proto :
+.Bl -hang -offset flag -width 16n
+.It Ar ipv6
+All packets match.
+The alias
+.Ar all
+has the same effect.
+.It Ar tcp
+Only TCP packets match.
+.It Ar udp
+Only UDP packets match.
+.It Ar ipv6-icmp
+Only ICMPv6 packets match.
+.It Ar <number|name>
+Only packets for the specified protocol matches (see
+.Pa /etc/protocols
+for a complete list).
+.El
+.Pp
+.Ar src
+and
+.Ar dst :
+.Bl -hang -offset flag
+.It Ar <address/prefixlen>
+.Op Ar ports
+.El
+.Pp
+The
+.Em <address/prefixlen>
+may be specified as:
+.Bl -hang -offset flag -width 16n
+.It Ar ipv6no
+An ipv6number of the form
+.Li fec0::1:2:3:4 .
+.It Ar ipv6no/prefixlen
+An ipv6number with a prefix length of the form
+.Li fec0::1:2:3:4/112 .
+.El
+.Pp
+The sense of the match can be inverted by preceding an address with the
+.Dq not
+modifier, causing all other addresses to be matched instead.
+This
+does not affect the selection of port numbers.
+.Pp
+With the TCP and UDP protocols, optional
+.Em ports
+may be specified as:
+.Pp
+.Bl -hang -offset flag
+.It Ns {port|port-port} Ns Op ,port Ns Op ,...
+.El
+.Pp
+Service names (from
+.Pa /etc/services )
+may be used instead of numeric port values.
+A range may only be specified as the first value,
+and the length of the port list is limited to
+.Dv IPV6_FW_MAX_PORTS
+(as defined in
+.In netinet6/ip6_fw.h )
+ports.
+.Pp
+Fragmented packets which have a non-zero offset (i.e., not the first
+fragment) will never match a rule which has one or more port
+specifications.
+See the
+.Ar frag
+option for details on matching fragmented packets.
+.Pp
+Rules can apply to packets when they are incoming, or outgoing, or both.
+The
+.Ar in
+keyword indicates the rule should only match incoming packets.
+The
+.Ar out
+keyword indicates the rule should only match outgoing packets.
+.Pp
+To match packets going through a certain interface, specify
+the interface using
+.Ar via :
+.Bl -hang -offset flag -width 16n
+.It Ar via ifX
+Packet must be going through interface
+.Ar ifX .
+.It Ar via if*
+Packet must be going through interface
+.Ar ifX ,
+where X is any unit number.
+.It Ar via any
+Packet must be going through
+.Em some
+interface.
+.It Ar via ipv6no
+Packet must be going through the interface having IPv6 address
+.Ar ipv6no .
+.El
+.Pp
+The
+.Ar via
+keyword causes the interface to always be checked.
+If
+.Ar recv
+or
+.Ar xmit
+is used instead of
+.Ar via ,
+then the only receive or transmit interface (respectively) is checked.
+By specifying both, it is possible to match packets based on both receive
+and transmit interface, e.g.:
+.Pp
+.Dl "ip6fw add 100 deny ip from any to any out recv ed0 xmit ed1"
+.Pp
+The
+.Ar recv
+interface can be tested on either incoming or outgoing packets, while the
+.Ar xmit
+interface can only be tested on outgoing packets.
+So
+.Ar out
+is required (and
+.Ar in
+invalid) whenever
+.Ar xmit
+is used.
+Specifying
+.Ar via
+together with
+.Ar xmit
+or
+.Ar recv
+is invalid.
+.Pp
+A packet may not have a receive or transmit interface: packets originating
+from the local host have no receive interface, while packets destined for
+the local host have no transmit interface.
+.Pp
+Additional
+.Ar options :
+.Bl -hang -offset flag -width 16n
+.It frag
+Matches if the packet is a fragment and this is not the first fragment
+of the datagram.
+.Ar frag
+may not be used in conjunction with either
+.Ar tcpflags
+or TCP/UDP port specifications.
+.It in
+Matches if this packet was on the way in.
+.It out
+Matches if this packet was on the way out.
+.It ipv6options Ar spec
+Matches if the IPv6 header contains the comma separated list of
+options specified in
+.Ar spec .
+The supported IPv6 options are:
+.Ar hopopt
+(hop-by-hop options header),
+.Ar route
+(routing header),
+.Ar frag
+(fragment header),
+.Ar esp
+(encapsulating security payload),
+.Ar ah
+(authentication header),
+.Ar nonxt
+(no next header), and
+.Ar opts
+(destination options header).
+The absence of a particular option may be denoted
+with a
+.Dq \&!
+.Em ( "not working yet" ) .
+.It established
+Matches packets that have the RST or ACK bits set.
+TCP packets only.
+.It setup
+Matches packets that have the SYN bit set but no ACK bit.
+TCP packets only.
+.It tcpflags Ar spec
+Matches if the TCP header contains the comma separated list of
+flags specified in
+.Ar spec .
+The supported TCP flags are:
+.Ar fin ,
+.Ar syn ,
+.Ar rst ,
+.Ar psh ,
+.Ar ack ,
+and
+.Ar urg .
+The absence of a particular flag may be denoted
+with a
+.Dq \&! .
+A rule which contains a
+.Ar tcpflags
+specification can never match a fragmented packet which has
+a non-zero offset.
+See the
+.Ar frag
+option for details on matching fragmented packets.
+.It icmptypes Ar types
+Matches if the ICMPv6 type is in the list
+.Ar types .
+The list may be specified as any combination of ranges
+or individual types separated by commas.
+.El
+.Sh CHECKLIST
+Here are some important points to consider when designing your
+rules:
+.Bl -bullet -offset flag
+.It
+Remember that you filter both packets going in and out.
+Most connections need packets going in both directions.
+.It
+Remember to test very carefully.
+It is a good idea to be near the console when doing this.
+.It
+Do not forget the loopback interface.
+.El
+.Sh FINE POINTS
+There is one kind of packet that the firewall will always discard,
+that is an IPv6 fragment with a fragment offset of one.
+This is a valid packet, but it only has one use, to try to circumvent
+firewalls.
+.Pp
+If you are logged in over a network, loading the KLD version of
+.Nm
+is probably not as straightforward as you would think
+.Em ( "not supported" ) .
+I recommend this command line:
+.Bd -literal -offset center
+kldload ip6fw && \e
+ip6fw add 32000 allow all from any to any
+.Ed
+.Pp
+Along the same lines, doing an
+.Bd -literal -offset center
+ip6fw flush
+.Ed
+.Pp
+in similar surroundings is also a bad idea.
+.Sh PACKET DIVERSION
+not supported.
+.Sh EXAMPLES
+This command adds an entry which denies all tcp packets from
+.Em hacker.evil.org
+to the telnet port of
+.Em wolf.tambov.su
+from being forwarded by the host:
+.Pp
+.Dl ip6fw add deny tcp from hacker.evil.org to wolf.tambov.su 23
+.Pp
+This one disallows any connection from the entire hackers network to
+my host:
+.Pp
+.Dl ip6fw add deny all from fec0::123:45:67:0/112 to my.host.org
+.Pp
+Here is a good usage of the list command to see accounting records
+and timestamp information:
+.Pp
+.Dl ip6fw -at l
+.Pp
+or in short form without timestamps:
+.Pp
+.Dl ip6fw -a l
+.Sh SEE ALSO
+.Xr ip 4 ,
+.Xr ipfirewall 4 ,
+.Xr protocols 5 ,
+.Xr services 5 ,
+.Xr reboot 8 ,
+.Xr sysctl 8 ,
+.Xr syslogd 8
+.Sh HISTORY
+A
+.Nm
+utility first appeared in
+.Fx 4.0 .
+.Sh AUTHORS
+.An Ugen J. S. Antsilevich ,
+.An Poul-Henning Kamp ,
+.An Alex Nash ,
+.An Archie Cobbs .
+.Pp
+.An -nosplit
+API based upon code written by
+.An Daniel Boulet
+for BSDI.
+.Sh BUGS
+.Em WARNING!!WARNING!!WARNING!!WARNING!!WARNING!!WARNING!!WARNING!!
+.Pp
+This program can put your computer in rather unusable state.
+When
+using it for the first time, work on the console of the computer, and
+do
+.Em NOT
+do anything you do not understand.
+.Pp
+When manipulating/adding chain entries, service and protocol names are
+not accepted.
diff --git a/sbin/ip6fw/ip6fw.c b/sbin/ip6fw/ip6fw.c
new file mode 100644
index 0000000..9ceabbf
--- /dev/null
+++ b/sbin/ip6fw/ip6fw.c
@@ -0,0 +1,1453 @@
+/* $KAME: ip6fw.c,v 1.14 2003/10/02 19:36:25 itojun Exp $ */
+
+/*
+ * Copyright (C) 1998, 1999, 2000 and 2001 WIDE 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.
+ * 3. Neither the name of the project 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 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.
+ */
+
+/*
+ * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Idea and grammar partially left from:
+ * Copyright (c) 1993 Daniel Boulet
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ *
+ * NEW command line interface for IP firewall facility
+ *
+ * $Id: ip6fw.c,v 1.1.2.2.2.2 1999/05/14 05:13:50 shin Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <timeconv.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <sysexits.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet6/ip6_fw.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+int lineno = -1;
+
+int s; /* main RAW socket */
+int do_resolv=0; /* Would try to resolv all */
+int do_acct=0; /* Show packet/byte count */
+int do_time=0; /* Show time stamps */
+int do_quiet=0; /* Be quiet in add and flush */
+int do_force=0; /* Don't ask for confirmation */
+int do_test=0; /* Don't load into Kernel */
+
+struct icmpcode {
+ int code;
+ char *str;
+};
+
+static struct icmpcode icmp6codes[] = {
+ { ICMP6_DST_UNREACH_NOROUTE, "noroute" },
+ { ICMP6_DST_UNREACH_ADMIN, "admin" },
+ { ICMP6_DST_UNREACH_NOTNEIGHBOR, "notneighbor" },
+ { ICMP6_DST_UNREACH_ADDR, "addr" },
+ { ICMP6_DST_UNREACH_NOPORT, "noport" },
+ { 0, NULL }
+};
+
+static char ntop_buf[INET6_ADDRSTRLEN];
+
+static void show_usage(const char *fmt, ...);
+
+static int
+mask_bits(u_char *m_ad, int m_len)
+{
+ int h_num = 0,i;
+
+ for (i = 0; i < m_len; i++, m_ad++) {
+ if (*m_ad != 0xff)
+ break;
+ h_num += 8;
+ }
+ if (i < m_len) {
+ switch (*m_ad) {
+#define MASKLEN(m, l) case m: h_num += l; break
+ MASKLEN(0xfe, 7);
+ MASKLEN(0xfc, 6);
+ MASKLEN(0xf8, 5);
+ MASKLEN(0xf0, 4);
+ MASKLEN(0xe0, 3);
+ MASKLEN(0xc0, 2);
+ MASKLEN(0x80, 1);
+#undef MASKLEN
+ }
+ }
+ return h_num;
+}
+
+static int pl2m[9] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
+
+struct in6_addr *plen2mask(int n)
+{
+ static struct in6_addr ia;
+ u_char *p;
+ int i;
+
+ memset(&ia, 0, sizeof(struct in6_addr));
+ p = (u_char *)&ia;
+ for (i = 0; i < 16; i++, p++, n -= 8) {
+ if (n >= 8) {
+ *p = 0xff;
+ continue;
+ }
+ *p = pl2m[n];
+ break;
+ }
+ return &ia;
+}
+
+void
+print_port(prot, port, comma)
+ u_char prot;
+ u_short port;
+ const char *comma;
+{
+ struct servent *se;
+ struct protoent *pe;
+ const char *protocol;
+ int printed = 0;
+
+ if (do_resolv) {
+ pe = getprotobynumber(prot);
+ if (pe)
+ protocol = pe->p_name;
+ else
+ protocol = NULL;
+
+ se = getservbyport(htons(port), protocol);
+ if (se) {
+ printf("%s%s", comma, se->s_name);
+ printed = 1;
+ }
+ }
+ if (!printed)
+ printf("%s%d",comma,port);
+}
+
+static void
+print_iface(char *key, union ip6_fw_if *un, int byname)
+{
+
+ if (byname) {
+ printf(" %s %s", key, un->fu_via_if.name);
+ } else if (!IN6_IS_ADDR_UNSPECIFIED(&un->fu_via_ip6)) {
+ printf(" %s %s", key, inet_ntop(AF_INET6,&un->fu_via_ip6,ntop_buf,sizeof(ntop_buf)));
+ } else
+ printf(" %s any", key);
+}
+
+static void
+print_reject_code(int code)
+{
+ struct icmpcode *ic;
+
+ for (ic = icmp6codes; ic->str; ic++)
+ if (ic->code == code) {
+ printf("%s", ic->str);
+ return;
+ }
+ printf("%u", code);
+}
+
+static void
+show_ip6fw(struct ip6_fw *chain)
+{
+ char *comma;
+ struct hostent *he;
+ struct protoent *pe;
+ int i, mb;
+ int nsp = IPV6_FW_GETNSRCP(chain);
+ int ndp = IPV6_FW_GETNDSTP(chain);
+
+ if (do_resolv)
+ setservent(1/*stayopen*/);
+
+ printf("%05u ", chain->fw_number);
+
+ if (do_acct)
+ printf("%10lu %10lu ",chain->fw_pcnt,chain->fw_bcnt);
+
+ if (do_time)
+ {
+ if (chain->timestamp)
+ {
+ char timestr[30];
+ time_t t = _long_to_time(chain->timestamp);
+
+ strcpy(timestr, ctime(&t));
+ *strchr(timestr, '\n') = '\0';
+ printf("%s ", timestr);
+ }
+ else
+ printf(" ");
+ }
+
+ switch (chain->fw_flg & IPV6_FW_F_COMMAND)
+ {
+ case IPV6_FW_F_ACCEPT:
+ printf("allow");
+ break;
+ case IPV6_FW_F_DENY:
+ printf("deny");
+ break;
+ case IPV6_FW_F_COUNT:
+ printf("count");
+ break;
+ case IPV6_FW_F_DIVERT:
+ printf("divert %u", chain->fw_divert_port);
+ break;
+ case IPV6_FW_F_TEE:
+ printf("tee %u", chain->fw_divert_port);
+ break;
+ case IPV6_FW_F_SKIPTO:
+ printf("skipto %u", chain->fw_skipto_rule);
+ break;
+ case IPV6_FW_F_REJECT:
+ if (chain->fw_reject_code == IPV6_FW_REJECT_RST)
+ printf("reset");
+ else {
+ printf("unreach ");
+ print_reject_code(chain->fw_reject_code);
+ }
+ break;
+ default:
+ errx(EX_OSERR, "impossible");
+ }
+
+ if (chain->fw_flg & IPV6_FW_F_PRN)
+ printf(" log");
+
+ pe = getprotobynumber(chain->fw_prot);
+ if (pe)
+ printf(" %s", pe->p_name);
+ else
+ printf(" %u", chain->fw_prot);
+
+ printf(" from %s", chain->fw_flg & IPV6_FW_F_INVSRC ? "not " : "");
+
+ mb=mask_bits((u_char *)&chain->fw_smsk,sizeof(chain->fw_smsk));
+ if (mb==128 && do_resolv) {
+ he=gethostbyaddr((char *)&(chain->fw_src),sizeof(chain->fw_src),AF_INET6);
+ if (he==NULL) {
+ printf(inet_ntop(AF_INET6,&chain->fw_src,ntop_buf,sizeof(ntop_buf)));
+ } else
+ printf("%s",he->h_name);
+ } else {
+ if (mb!=128) {
+ if (mb == 0) {
+ printf("any");
+ } else {
+ printf(inet_ntop(AF_INET6,&chain->fw_src,ntop_buf,sizeof(ntop_buf)));
+ printf("/%d",mb);
+ }
+ } else
+ printf(inet_ntop(AF_INET6,&chain->fw_src,ntop_buf,sizeof(ntop_buf)));
+ }
+
+ if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) {
+ comma = " ";
+ for (i = 0; i < nsp; i++) {
+ print_port(chain->fw_prot, chain->fw_pts[i], comma);
+ if (i==0 && (chain->fw_flg & IPV6_FW_F_SRNG))
+ comma = "-";
+ else
+ comma = ",";
+ }
+ }
+
+ printf(" to %s", chain->fw_flg & IPV6_FW_F_INVDST ? "not " : "");
+
+ mb=mask_bits((u_char *)&chain->fw_dmsk,sizeof(chain->fw_dmsk));
+ if (mb==128 && do_resolv) {
+ he=gethostbyaddr((char *)&(chain->fw_dst),sizeof(chain->fw_dst),AF_INET6);
+ if (he==NULL) {
+ printf(inet_ntop(AF_INET6,&chain->fw_dst,ntop_buf,sizeof(ntop_buf)));
+ } else
+ printf("%s",he->h_name);
+ } else {
+ if (mb!=128) {
+ if (mb == 0) {
+ printf("any");
+ } else {
+ printf(inet_ntop(AF_INET6,&chain->fw_dst,ntop_buf,sizeof(ntop_buf)));
+ printf("/%d",mb);
+ }
+ } else
+ printf(inet_ntop(AF_INET6,&chain->fw_dst,ntop_buf,sizeof(ntop_buf)));
+ }
+
+ if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) {
+ comma = " ";
+ for (i = 0; i < ndp; i++) {
+ print_port(chain->fw_prot, chain->fw_pts[nsp+i], comma);
+ if (i==0 && (chain->fw_flg & IPV6_FW_F_DRNG))
+ comma = "-";
+ else
+ comma = ",";
+ }
+ }
+
+ /* Direction */
+ if ((chain->fw_flg & IPV6_FW_F_IN) && !(chain->fw_flg & IPV6_FW_F_OUT))
+ printf(" in");
+ if (!(chain->fw_flg & IPV6_FW_F_IN) && (chain->fw_flg & IPV6_FW_F_OUT))
+ printf(" out");
+
+ /* Handle hack for "via" backwards compatibility */
+ if ((chain->fw_flg & IF6_FW_F_VIAHACK) == IF6_FW_F_VIAHACK) {
+ print_iface("via",
+ &chain->fw_in_if, chain->fw_flg & IPV6_FW_F_IIFNAME);
+ } else {
+ /* Receive interface specified */
+ if (chain->fw_flg & IPV6_FW_F_IIFACE)
+ print_iface("recv", &chain->fw_in_if,
+ chain->fw_flg & IPV6_FW_F_IIFNAME);
+ /* Transmit interface specified */
+ if (chain->fw_flg & IPV6_FW_F_OIFACE)
+ print_iface("xmit", &chain->fw_out_if,
+ chain->fw_flg & IPV6_FW_F_OIFNAME);
+ }
+
+ if (chain->fw_flg & IPV6_FW_F_FRAG)
+ printf(" frag");
+
+ if (chain->fw_ip6opt || chain->fw_ip6nopt) {
+ int _opt_printed = 0;
+#define PRINTOPT(x) {if (_opt_printed) printf(",");\
+ printf(x); _opt_printed = 1;}
+
+ printf(" ip6opt ");
+ if (chain->fw_ip6opt & IPV6_FW_IP6OPT_HOPOPT) PRINTOPT("hopopt");
+ if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_HOPOPT) PRINTOPT("!hopopt");
+ if (chain->fw_ip6opt & IPV6_FW_IP6OPT_ROUTE) PRINTOPT("route");
+ if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_ROUTE) PRINTOPT("!route");
+ if (chain->fw_ip6opt & IPV6_FW_IP6OPT_FRAG) PRINTOPT("frag");
+ if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_FRAG) PRINTOPT("!frag");
+ if (chain->fw_ip6opt & IPV6_FW_IP6OPT_ESP) PRINTOPT("esp");
+ if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_ESP) PRINTOPT("!esp");
+ if (chain->fw_ip6opt & IPV6_FW_IP6OPT_AH) PRINTOPT("ah");
+ if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_AH) PRINTOPT("!ah");
+ if (chain->fw_ip6opt & IPV6_FW_IP6OPT_NONXT) PRINTOPT("nonxt");
+ if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_NONXT) PRINTOPT("!nonxt");
+ if (chain->fw_ip6opt & IPV6_FW_IP6OPT_OPTS) PRINTOPT("opts");
+ if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_OPTS) PRINTOPT("!opts");
+ }
+
+ if (chain->fw_ipflg & IPV6_FW_IF_TCPEST)
+ printf(" established");
+ else if (chain->fw_tcpf == IPV6_FW_TCPF_SYN &&
+ chain->fw_tcpnf == IPV6_FW_TCPF_ACK)
+ printf(" setup");
+ else if (chain->fw_tcpf || chain->fw_tcpnf) {
+ int _flg_printed = 0;
+#define PRINTFLG(x) {if (_flg_printed) printf(",");\
+ printf(x); _flg_printed = 1;}
+
+ printf(" tcpflg ");
+ if (chain->fw_tcpf & IPV6_FW_TCPF_FIN) PRINTFLG("fin");
+ if (chain->fw_tcpnf & IPV6_FW_TCPF_FIN) PRINTFLG("!fin");
+ if (chain->fw_tcpf & IPV6_FW_TCPF_SYN) PRINTFLG("syn");
+ if (chain->fw_tcpnf & IPV6_FW_TCPF_SYN) PRINTFLG("!syn");
+ if (chain->fw_tcpf & IPV6_FW_TCPF_RST) PRINTFLG("rst");
+ if (chain->fw_tcpnf & IPV6_FW_TCPF_RST) PRINTFLG("!rst");
+ if (chain->fw_tcpf & IPV6_FW_TCPF_PSH) PRINTFLG("psh");
+ if (chain->fw_tcpnf & IPV6_FW_TCPF_PSH) PRINTFLG("!psh");
+ if (chain->fw_tcpf & IPV6_FW_TCPF_ACK) PRINTFLG("ack");
+ if (chain->fw_tcpnf & IPV6_FW_TCPF_ACK) PRINTFLG("!ack");
+ if (chain->fw_tcpf & IPV6_FW_TCPF_URG) PRINTFLG("urg");
+ if (chain->fw_tcpnf & IPV6_FW_TCPF_URG) PRINTFLG("!urg");
+ }
+ if (chain->fw_flg & IPV6_FW_F_ICMPBIT) {
+ int type_index;
+ int first = 1;
+
+ printf(" icmptype");
+
+ for (type_index = 0; type_index < IPV6_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8; ++type_index)
+ if (chain->fw_icmp6types[type_index / (sizeof(unsigned) * 8)] &
+ (1U << (type_index % (sizeof(unsigned) * 8)))) {
+ printf("%c%d", first == 1 ? ' ' : ',', type_index);
+ first = 0;
+ }
+ }
+ printf("\n");
+ if (do_resolv)
+ endservent();
+}
+
+void
+list(ac, av)
+ int ac;
+ char **av;
+{
+ struct ip6_fw *r, *rules, *n;
+ int l,i;
+ unsigned long rulenum;
+ int nalloc, bytes, maxbytes;
+
+ /* extract rules from kernel, resizing array as necessary */
+ rules = NULL;
+ nalloc = sizeof *rules;
+ bytes = nalloc;
+ maxbytes = 65536 * sizeof *rules;
+ while (bytes >= nalloc) {
+ if ((n = realloc(rules, nalloc * 2 + 200)) == NULL)
+ err(EX_OSERR, "realloc");
+ bytes = nalloc = nalloc * 2 + 200;
+ rules = n;
+ i = getsockopt(s, IPPROTO_IPV6, IPV6_FW_GET, rules, &bytes);
+ if ((i < 0 && errno != EINVAL) || nalloc > maxbytes)
+ err(EX_OSERR, "getsockopt(IPV6_FW_GET)");
+ }
+ if (!ac) {
+ /* display all rules */
+ for (r = rules, l = bytes; l >= sizeof rules[0];
+ r++, l-=sizeof rules[0])
+ show_ip6fw(r);
+ }
+ else {
+ /* display specific rules requested on command line */
+ int exitval = 0;
+
+ while (ac--) {
+ char *endptr;
+ int seen;
+
+ /* convert command line rule # */
+ rulenum = strtoul(*av++, &endptr, 10);
+ if (*endptr) {
+ exitval = 1;
+ warn("invalid rule number: %s", av[-1]);
+ continue;
+ }
+ seen = 0;
+ for (r = rules, l = bytes;
+ l >= sizeof rules[0] && r->fw_number <= rulenum;
+ r++, l-=sizeof rules[0])
+ if (rulenum == r->fw_number) {
+ show_ip6fw(r);
+ seen = 1;
+ }
+ if (!seen) {
+ exitval = 1;
+ warnx("rule %lu does not exist", rulenum);
+ }
+ }
+ if (exitval != 0)
+ exit(exitval);
+ }
+}
+
+static void
+show_usage(const char *fmt, ...)
+{
+ if (fmt) {
+ char buf[100];
+ va_list args;
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ warnx("error: %s", buf);
+ }
+ fprintf(stderr, "usage: ip6fw [options]\n"
+" flush\n"
+" add [number] rule\n"
+" delete number ...\n"
+" list [number ...]\n"
+" show [number ...]\n"
+" zero [number ...]\n"
+" rule: action proto src dst extras...\n"
+" action:\n"
+" {allow|permit|accept|pass|deny|drop|reject|unreach code|\n"
+" reset|count|skipto num} [log]\n"
+" proto: {ipv6|tcp|udp|ipv6-icmp|<number>}\n"
+" src: from [not] {any|ipv6[/prefixlen]} [{port|port-port},[port],...]\n"
+" dst: to [not] {any|ipv6[/prefixlen]} [{port|port-port},[port],...]\n"
+" extras:\n"
+" fragment (may not be used with ports or tcpflags)\n"
+" in\n"
+" out\n"
+" {xmit|recv|via} {iface|ipv6|any}\n"
+" {established|setup}\n"
+" tcpflags [!]{syn|fin|rst|ack|psh|urg},...\n"
+" ipv6options [!]{hopopt|route|frag|esp|ah|nonxt|opts},...\n"
+" icmptypes {type[,type]}...\n");
+
+ exit(1);
+}
+
+static int
+lookup_host (host, addr, family)
+ char *host;
+ u_char *addr;
+ int family;
+{
+ struct hostent *he;
+
+ if (inet_pton(family, host, addr) != 1) {
+ if ((he = gethostbyname2(host, family)) == NULL)
+ return(-1);
+ memcpy(addr, he->h_addr_list[0], he->h_length);
+ }
+ return(0);
+}
+
+void
+fill_ip6(ipno, mask, acp, avp)
+ struct in6_addr *ipno, *mask;
+ int *acp;
+ char ***avp;
+{
+ int ac = *acp;
+ char **av = *avp;
+ char *p = 0, md = 0;
+ int i;
+
+ if (ac && !strncmp(*av,"any",strlen(*av))) {
+ *ipno = *mask = in6addr_any; av++; ac--;
+ } else {
+ p = strchr(*av, '/');
+ if (p) {
+ md = *p;
+ *p++ = '\0';
+ }
+
+ if (lookup_host(*av, (u_char *)ipno, AF_INET6) != 0)
+ show_usage("hostname ``%s'' unknown", *av);
+ switch (md) {
+ case '/':
+ if (atoi(p) == 0) {
+ *mask = in6addr_any;
+ } else if (atoi(p) > 128) {
+ show_usage("bad width ``%s''", p);
+ } else {
+ *mask = *(plen2mask(atoi(p)));
+ }
+ break;
+ default:
+ *mask = *(plen2mask(128));
+ break;
+ }
+ for (i = 0; i < sizeof(*ipno); i++)
+ ipno->s6_addr[i] &= mask->s6_addr[i];
+ av++;
+ ac--;
+ }
+ *acp = ac;
+ *avp = av;
+}
+
+static void
+fill_reject_code6(u_short *codep, char *str)
+{
+ struct icmpcode *ic;
+ u_long val;
+ char *s;
+
+ val = strtoul(str, &s, 0);
+ if (s != str && *s == '\0' && val < 0x100) {
+ *codep = val;
+ return;
+ }
+ for (ic = icmp6codes; ic->str; ic++)
+ if (!strcasecmp(str, ic->str)) {
+ *codep = ic->code;
+ return;
+ }
+ show_usage("unknown ICMP6 unreachable code ``%s''", str);
+}
+
+static void
+add_port(cnt, ptr, off, port)
+ u_short *cnt, *ptr, off, port;
+{
+ if (off + *cnt >= IPV6_FW_MAX_PORTS)
+ errx(1, "too many ports (max is %d)", IPV6_FW_MAX_PORTS);
+ ptr[off+*cnt] = port;
+ (*cnt)++;
+}
+
+static int
+lookup_port(const char *arg, int test, int nodash)
+{
+ int val;
+ char *earg, buf[32];
+ struct servent *s;
+
+ snprintf(buf, sizeof(buf), "%s", arg);
+ buf[strcspn(arg, nodash ? "-," : ",")] = 0;
+ val = (int) strtoul(buf, &earg, 0);
+ if (!*buf || *earg) {
+ setservent(1);
+ if ((s = getservbyname(buf, NULL))) {
+ val = htons(s->s_port);
+ } else {
+ if (!test) {
+ errx(1, "unknown port ``%s''", arg);
+ }
+ val = -1;
+ }
+ } else {
+ if (val < 0 || val > 0xffff) {
+ if (!test) {
+ errx(1, "port ``%s'' out of range", arg);
+ }
+ val = -1;
+ }
+ }
+ return(val);
+}
+
+int
+fill_port(cnt, ptr, off, arg)
+ u_short *cnt, *ptr, off;
+ char *arg;
+{
+ char *s;
+ int initial_range = 0;
+
+ s = arg + strcspn(arg, "-,"); /* first port name can't have a dash */
+ if (*s == '-') {
+ *s++ = '\0';
+ if (strchr(arg, ','))
+ errx(1, "port range must be first in list");
+ add_port(cnt, ptr, off, *arg ? lookup_port(arg, 0, 0) : 0x0000);
+ arg = s;
+ s = strchr(arg,',');
+ if (s)
+ *s++ = '\0';
+ add_port(cnt, ptr, off, *arg ? lookup_port(arg, 0, 0) : 0xffff);
+ arg = s;
+ initial_range = 1;
+ }
+ while (arg != NULL) {
+ s = strchr(arg,',');
+ if (s)
+ *s++ = '\0';
+ add_port(cnt, ptr, off, lookup_port(arg, 0, 0));
+ arg = s;
+ }
+ return initial_range;
+}
+
+void
+fill_tcpflag(set, reset, vp)
+ u_char *set, *reset;
+ char **vp;
+{
+ char *p = *vp,*q;
+ u_char *d;
+
+ while (p && *p) {
+ struct tpcflags {
+ char * name;
+ u_char value;
+ } flags[] = {
+ { "syn", IPV6_FW_TCPF_SYN },
+ { "fin", IPV6_FW_TCPF_FIN },
+ { "ack", IPV6_FW_TCPF_ACK },
+ { "psh", IPV6_FW_TCPF_PSH },
+ { "rst", IPV6_FW_TCPF_RST },
+ { "urg", IPV6_FW_TCPF_URG }
+ };
+ int i;
+
+ if (*p == '!') {
+ p++;
+ d = reset;
+ } else {
+ d = set;
+ }
+ q = strchr(p, ',');
+ if (q)
+ *q++ = '\0';
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i)
+ if (!strncmp(p, flags[i].name, strlen(p))) {
+ *d |= flags[i].value;
+ break;
+ }
+ if (i == sizeof(flags) / sizeof(flags[0]))
+ show_usage("invalid tcp flag ``%s''", p);
+ p = q;
+ }
+}
+
+static void
+fill_ip6opt(u_char *set, u_char *reset, char **vp)
+{
+ char *p = *vp,*q;
+ u_char *d;
+
+ while (p && *p) {
+ if (*p == '!') {
+ p++;
+ d = reset;
+ } else {
+ d = set;
+ }
+ q = strchr(p, ',');
+ if (q)
+ *q++ = '\0';
+ if (!strncmp(p,"hopopt",strlen(p))) *d |= IPV6_FW_IP6OPT_HOPOPT;
+ if (!strncmp(p,"route",strlen(p))) *d |= IPV6_FW_IP6OPT_ROUTE;
+ if (!strncmp(p,"frag",strlen(p))) *d |= IPV6_FW_IP6OPT_FRAG;
+ if (!strncmp(p,"esp",strlen(p))) *d |= IPV6_FW_IP6OPT_ESP;
+ if (!strncmp(p,"ah",strlen(p))) *d |= IPV6_FW_IP6OPT_AH;
+ if (!strncmp(p,"nonxt",strlen(p))) *d |= IPV6_FW_IP6OPT_NONXT;
+ if (!strncmp(p,"opts",strlen(p))) *d |= IPV6_FW_IP6OPT_OPTS;
+ p = q;
+ }
+}
+
+void
+fill_icmptypes(types, vp, fw_flg)
+ u_long *types;
+ char **vp;
+ u_short *fw_flg;
+{
+ char *c = *vp;
+
+ while (*c)
+ {
+ unsigned long icmptype;
+
+ if ( *c == ',' )
+ ++c;
+
+ icmptype = strtoul(c, &c, 0);
+
+ if ( *c != ',' && *c != '\0' )
+ show_usage("invalid ICMP6 type");
+
+ if (icmptype >= IPV6_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8)
+ show_usage("ICMP6 type out of range");
+
+ types[icmptype / (sizeof(unsigned) * 8)] |=
+ 1 << (icmptype % (sizeof(unsigned) * 8));
+ *fw_flg |= IPV6_FW_F_ICMPBIT;
+ }
+}
+
+void
+delete(ac,av)
+ int ac;
+ char **av;
+{
+ struct ip6_fw rule;
+ int i;
+ int exitval = 0;
+
+ memset(&rule, 0, sizeof rule);
+
+ av++; ac--;
+
+ /* Rule number */
+ while (ac && isdigit(**av)) {
+ rule.fw_number = atoi(*av); av++; ac--;
+ if (!do_test) {
+ i = setsockopt(s, IPPROTO_IPV6, IPV6_FW_DEL, &rule, sizeof rule);
+ if (i) {
+ exitval = 1;
+ warn("rule %u: setsockopt(%s)", rule.fw_number, "IPV6_FW_DEL");
+ }
+ }
+ }
+ if (exitval != 0)
+ exit(exitval);
+}
+
+static void
+verify_interface(union ip6_fw_if *ifu)
+{
+ struct ifreq ifr;
+
+ strlcpy(ifr.ifr_name, ifu->fu_via_if.name, sizeof(ifr.ifr_name));
+
+ if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
+ warnx("warning: interface ``%s'' does not exist", ifr.ifr_name);
+}
+
+static void
+fill_iface(char *which, union ip6_fw_if *ifu, int *byname, int ac, char *arg)
+{
+ if (!ac)
+ show_usage("missing argument for ``%s''", which);
+
+ /* Parse the interface or address */
+ if (!strcmp(arg, "any")) {
+ ifu->fu_via_ip6 = in6addr_any;
+ *byname = 0;
+ } else if (!isdigit(*arg)) {
+ *byname = 1;
+ strlcpy(ifu->fu_via_if.name, arg, sizeof(ifu->fu_via_if.name));
+ /*
+ * We assume that strings containing '*', '?', or '['
+ * are ment to be shell pattern.
+ */
+ if (strpbrk(arg, "*?[") != NULL) {
+ ifu->fu_via_if.glob = 1;
+ } else {
+ ifu->fu_via_if.glob = 0;
+ verify_interface(ifu);
+ }
+ } else if (inet_pton(AF_INET6, arg, &ifu->fu_via_ip6) != 1) {
+ show_usage("bad ip6 address ``%s''", arg);
+ } else
+ *byname = 0;
+}
+
+static void
+add(ac,av)
+ int ac;
+ char **av;
+{
+ struct ip6_fw rule;
+ int i;
+ u_char proto;
+ struct protoent *pe;
+ int saw_xmrc = 0, saw_via = 0;
+
+ memset(&rule, 0, sizeof rule);
+
+ av++; ac--;
+
+ /* Rule number */
+ if (ac && isdigit(**av)) {
+ rule.fw_number = atoi(*av); av++; ac--;
+ }
+
+ /* Action */
+ if (ac == 0)
+ show_usage("missing action");
+ if (!strncmp(*av,"accept",strlen(*av))
+ || !strncmp(*av,"pass",strlen(*av))
+ || !strncmp(*av,"allow",strlen(*av))
+ || !strncmp(*av,"permit",strlen(*av))) {
+ rule.fw_flg |= IPV6_FW_F_ACCEPT; av++; ac--;
+ } else if (!strncmp(*av,"count",strlen(*av))) {
+ rule.fw_flg |= IPV6_FW_F_COUNT; av++; ac--;
+ }
+#if 0
+ else if (!strncmp(*av,"divert",strlen(*av))) {
+ rule.fw_flg |= IPV6_FW_F_DIVERT; av++; ac--;
+ if (!ac)
+ show_usage("missing divert port");
+ rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
+ if (rule.fw_divert_port == 0) {
+ struct servent *s;
+ setservent(1);
+ s = getservbyname(av[-1], "divert");
+ if (s != NULL)
+ rule.fw_divert_port = ntohs(s->s_port);
+ else
+ show_usage("illegal divert port");
+ }
+ } else if (!strncmp(*av,"tee",strlen(*av))) {
+ rule.fw_flg |= IPV6_FW_F_TEE; av++; ac--;
+ if (!ac)
+ show_usage("missing divert port");
+ rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
+ if (rule.fw_divert_port == 0) {
+ struct servent *s;
+ setservent(1);
+ s = getservbyname(av[-1], "divert");
+ if (s != NULL)
+ rule.fw_divert_port = ntohs(s->s_port);
+ else
+ show_usage("illegal divert port");
+ }
+ }
+#endif
+ else if (!strncmp(*av,"skipto",strlen(*av))) {
+ rule.fw_flg |= IPV6_FW_F_SKIPTO; av++; ac--;
+ if (!ac)
+ show_usage("missing skipto rule number");
+ rule.fw_skipto_rule = strtoul(*av, NULL, 0); av++; ac--;
+ } else if ((!strncmp(*av,"deny",strlen(*av))
+ || !strncmp(*av,"drop",strlen(*av)))) {
+ rule.fw_flg |= IPV6_FW_F_DENY; av++; ac--;
+ } else if (!strncmp(*av,"reject",strlen(*av))) {
+ rule.fw_flg |= IPV6_FW_F_REJECT; av++; ac--;
+ rule.fw_reject_code = ICMP6_DST_UNREACH_NOROUTE;
+ } else if (!strncmp(*av,"reset",strlen(*av))) {
+ rule.fw_flg |= IPV6_FW_F_REJECT; av++; ac--;
+ rule.fw_reject_code = IPV6_FW_REJECT_RST; /* check TCP later */
+ } else if (!strncmp(*av,"unreach",strlen(*av))) {
+ rule.fw_flg |= IPV6_FW_F_REJECT; av++; ac--;
+ fill_reject_code6(&rule.fw_reject_code, *av); av++; ac--;
+ } else {
+ show_usage("invalid action ``%s''", *av);
+ }
+
+ /* [log] */
+ if (ac && !strncmp(*av,"log",strlen(*av))) {
+ rule.fw_flg |= IPV6_FW_F_PRN; av++; ac--;
+ }
+
+ /* protocol */
+ if (ac == 0)
+ show_usage("missing protocol");
+ if ((proto = atoi(*av)) > 0) {
+ rule.fw_prot = proto; av++; ac--;
+ } else if (!strncmp(*av,"all",strlen(*av))) {
+ rule.fw_prot = IPPROTO_IPV6; av++; ac--;
+ } else if ((pe = getprotobyname(*av)) != NULL) {
+ rule.fw_prot = pe->p_proto; av++; ac--;
+ } else {
+ show_usage("invalid protocol ``%s''", *av);
+ }
+
+ if (rule.fw_prot != IPPROTO_TCP
+ && (rule.fw_flg & IPV6_FW_F_COMMAND) == IPV6_FW_F_REJECT
+ && rule.fw_reject_code == IPV6_FW_REJECT_RST)
+ show_usage("``reset'' is only valid for tcp packets");
+
+ /* from */
+ if (ac && !strncmp(*av,"from",strlen(*av))) { av++; ac--; }
+ else
+ show_usage("missing ``from''");
+
+ if (ac && !strncmp(*av,"not",strlen(*av))) {
+ rule.fw_flg |= IPV6_FW_F_INVSRC;
+ av++; ac--;
+ }
+ if (!ac)
+ show_usage("missing arguments");
+
+ fill_ip6(&rule.fw_src, &rule.fw_smsk, &ac, &av);
+
+ if (ac && (isdigit(**av) || lookup_port(*av, 1, 1) >= 0)) {
+ u_short nports = 0;
+
+ if (fill_port(&nports, rule.fw_pts, 0, *av))
+ rule.fw_flg |= IPV6_FW_F_SRNG;
+ IPV6_FW_SETNSRCP(&rule, nports);
+ av++; ac--;
+ }
+
+ /* to */
+ if (ac && !strncmp(*av,"to",strlen(*av))) { av++; ac--; }
+ else
+ show_usage("missing ``to''");
+
+ if (ac && !strncmp(*av,"not",strlen(*av))) {
+ rule.fw_flg |= IPV6_FW_F_INVDST;
+ av++; ac--;
+ }
+ if (!ac)
+ show_usage("missing arguments");
+
+ fill_ip6(&rule.fw_dst, &rule.fw_dmsk, &ac, &av);
+
+ if (ac && (isdigit(**av) || lookup_port(*av, 1, 1) >= 0)) {
+ u_short nports = 0;
+
+ if (fill_port(&nports,
+ rule.fw_pts, IPV6_FW_GETNSRCP(&rule), *av))
+ rule.fw_flg |= IPV6_FW_F_DRNG;
+ IPV6_FW_SETNDSTP(&rule, nports);
+ av++; ac--;
+ }
+
+ if ((rule.fw_prot != IPPROTO_TCP) && (rule.fw_prot != IPPROTO_UDP)
+ && (IPV6_FW_GETNSRCP(&rule) || IPV6_FW_GETNDSTP(&rule))) {
+ show_usage("only TCP and UDP protocols are valid"
+ " with port specifications");
+ }
+
+ while (ac) {
+ if (!strncmp(*av,"in",strlen(*av))) {
+ rule.fw_flg |= IPV6_FW_F_IN;
+ av++; ac--; continue;
+ }
+ if (!strncmp(*av,"out",strlen(*av))) {
+ rule.fw_flg |= IPV6_FW_F_OUT;
+ av++; ac--; continue;
+ }
+ if (ac && !strncmp(*av,"xmit",strlen(*av))) {
+ union ip6_fw_if ifu;
+ int byname;
+
+ if (saw_via) {
+badviacombo:
+ show_usage("``via'' is incompatible"
+ " with ``xmit'' and ``recv''");
+ }
+ saw_xmrc = 1;
+ av++; ac--;
+ fill_iface("xmit", &ifu, &byname, ac, *av);
+ rule.fw_out_if = ifu;
+ rule.fw_flg |= IPV6_FW_F_OIFACE;
+ if (byname)
+ rule.fw_flg |= IPV6_FW_F_OIFNAME;
+ av++; ac--; continue;
+ }
+ if (ac && !strncmp(*av,"recv",strlen(*av))) {
+ union ip6_fw_if ifu;
+ int byname;
+
+ if (saw_via)
+ goto badviacombo;
+ saw_xmrc = 1;
+ av++; ac--;
+ fill_iface("recv", &ifu, &byname, ac, *av);
+ rule.fw_in_if = ifu;
+ rule.fw_flg |= IPV6_FW_F_IIFACE;
+ if (byname)
+ rule.fw_flg |= IPV6_FW_F_IIFNAME;
+ av++; ac--; continue;
+ }
+ if (ac && !strncmp(*av,"via",strlen(*av))) {
+ union ip6_fw_if ifu;
+ int byname = 0;
+
+ if (saw_xmrc)
+ goto badviacombo;
+ saw_via = 1;
+ av++; ac--;
+ fill_iface("via", &ifu, &byname, ac, *av);
+ rule.fw_out_if = rule.fw_in_if = ifu;
+ if (byname)
+ rule.fw_flg |=
+ (IPV6_FW_F_IIFNAME | IPV6_FW_F_OIFNAME);
+ av++; ac--; continue;
+ }
+ if (!strncmp(*av,"fragment",strlen(*av))) {
+ rule.fw_flg |= IPV6_FW_F_FRAG;
+ av++; ac--; continue;
+ }
+ if (!strncmp(*av,"ipv6options",strlen(*av))) {
+ av++; ac--;
+ if (!ac)
+ show_usage("missing argument"
+ " for ``ipv6options''");
+ fill_ip6opt(&rule.fw_ip6opt, &rule.fw_ip6nopt, av);
+ av++; ac--; continue;
+ }
+ if (rule.fw_prot == IPPROTO_TCP) {
+ if (!strncmp(*av,"established",strlen(*av))) {
+ rule.fw_ipflg |= IPV6_FW_IF_TCPEST;
+ av++; ac--; continue;
+ }
+ if (!strncmp(*av,"setup",strlen(*av))) {
+ rule.fw_tcpf |= IPV6_FW_TCPF_SYN;
+ rule.fw_tcpnf |= IPV6_FW_TCPF_ACK;
+ av++; ac--; continue;
+ }
+ if (!strncmp(*av,"tcpflags",strlen(*av))) {
+ av++; ac--;
+ if (!ac)
+ show_usage("missing argument"
+ " for ``tcpflags''");
+ fill_tcpflag(&rule.fw_tcpf, &rule.fw_tcpnf, av);
+ av++; ac--; continue;
+ }
+ }
+ if (rule.fw_prot == IPPROTO_ICMPV6) {
+ if (!strncmp(*av,"icmptypes",strlen(*av))) {
+ av++; ac--;
+ if (!ac)
+ show_usage("missing argument"
+ " for ``icmptypes''");
+ fill_icmptypes(rule.fw_icmp6types,
+ av, &rule.fw_flg);
+ av++; ac--; continue;
+ }
+ }
+ show_usage("unknown argument ``%s''", *av);
+ }
+
+ /* No direction specified -> do both directions */
+ if (!(rule.fw_flg & (IPV6_FW_F_OUT|IPV6_FW_F_IN)))
+ rule.fw_flg |= (IPV6_FW_F_OUT|IPV6_FW_F_IN);
+
+ /* Sanity check interface check, but handle "via" case separately */
+ if (saw_via) {
+ if (rule.fw_flg & IPV6_FW_F_IN)
+ rule.fw_flg |= IPV6_FW_F_IIFACE;
+ if (rule.fw_flg & IPV6_FW_F_OUT)
+ rule.fw_flg |= IPV6_FW_F_OIFACE;
+ } else if ((rule.fw_flg & IPV6_FW_F_OIFACE) && (rule.fw_flg & IPV6_FW_F_IN))
+ show_usage("can't check xmit interface of incoming packets");
+
+ /* frag may not be used in conjunction with ports or TCP flags */
+ if (rule.fw_flg & IPV6_FW_F_FRAG) {
+ if (rule.fw_tcpf || rule.fw_tcpnf)
+ show_usage("can't mix 'frag' and tcpflags");
+
+ if (rule.fw_nports)
+ show_usage("can't mix 'frag' and port specifications");
+ }
+
+ if (!do_quiet)
+ show_ip6fw(&rule);
+ if (!do_test) {
+ i = setsockopt(s, IPPROTO_IPV6, IPV6_FW_ADD, &rule, sizeof rule);
+ if (i)
+ err(EX_UNAVAILABLE, "setsockopt(%s)", "IPV6_FW_ADD");
+ }
+}
+
+static void
+zero (ac, av)
+ int ac;
+ char **av;
+{
+ av++; ac--;
+
+ if (!ac) {
+ /* clear all entries */
+ if (!do_test) {
+ if (setsockopt(s,IPPROTO_IPV6,IPV6_FW_ZERO,NULL,0)<0)
+ err(EX_UNAVAILABLE, "setsockopt(%s)", "IPV6_FW_ZERO");
+ if (!do_quiet)
+ printf("Accounting cleared.\n");
+ } else if (!do_quiet)
+ printf("Accounting not cleared.\n");
+ } else {
+ struct ip6_fw rule;
+ int failed = 0;
+
+ memset(&rule, 0, sizeof rule);
+ while (ac) {
+ /* Rule number */
+ if (isdigit(**av)) {
+ rule.fw_number = atoi(*av); av++; ac--;
+ if (!do_test) {
+ if (setsockopt(s, IPPROTO_IPV6,
+ IPV6_FW_ZERO, &rule, sizeof rule)) {
+ warn("rule %u: setsockopt(%s)", rule.fw_number,"IPV6_FW_ZERO");
+ failed = 1;
+ }
+ if (!do_quiet)
+ printf("Entry %d cleared\n",
+ rule.fw_number);
+ }
+ else if (!do_quiet)
+ printf("Entry %d not cleared\n",
+ rule.fw_number);
+ } else
+ show_usage("invalid rule number ``%s''", *av);
+ }
+ if (failed != 0)
+ exit(failed);
+ }
+}
+
+int
+ip6fw_main(ac,av)
+ int ac;
+ char **av;
+{
+ int ch;
+
+ /* init optind to 1 */
+ optind = 1;
+
+ if ( ac == 1 ) {
+ show_usage(NULL);
+ }
+
+ /* Set the force flag for non-interactive processes */
+ do_force = !isatty(STDIN_FILENO);
+
+ while ((ch = getopt(ac, av ,"afnqtN")) != -1)
+ switch(ch) {
+ case 'a':
+ do_acct=1;
+ break;
+ case 'f':
+ do_force=1;
+ break;
+ case 'n':
+ do_test=1;
+ break;
+ case 'q':
+ do_quiet=1;
+ break;
+ case 't':
+ do_time=1;
+ break;
+ case 'N':
+ do_resolv=1;
+ break;
+ default:
+ show_usage(NULL);
+ }
+
+ ac -= optind;
+ if (*(av+=optind)==NULL) {
+ show_usage("Bad arguments");
+ }
+
+ if (!strncmp(*av, "add", strlen(*av))) {
+ add(ac,av);
+ } else if (!strncmp(*av, "delete", strlen(*av))) {
+ delete(ac,av);
+ } else if (!strncmp(*av, "flush", strlen(*av))) {
+ int do_flush = 0;
+
+ if ( do_force || do_quiet )
+ do_flush = 1;
+ else {
+ int c;
+
+ /* Ask the user */
+ printf("Are you sure? [yn] ");
+ do {
+ fflush(stdout);
+ c = toupper(getc(stdin));
+ while (c != '\n' && getc(stdin) != '\n')
+ if (feof(stdin))
+ return (0);
+ } while (c != 'Y' && c != 'N');
+ printf("\n");
+ if (c == 'Y')
+ do_flush = 1;
+ }
+ if ( do_flush ) {
+ if (!do_test) {
+ if (setsockopt(s,IPPROTO_IPV6,IPV6_FW_FLUSH,NULL,0) < 0)
+ err(EX_UNAVAILABLE, "setsockopt(%s)", "IPV6_FW_FLUSH");
+
+ if (!do_quiet)
+ printf("Flushed all rules.\n");
+ } else if (!do_quiet)
+ printf("Rules not flushed.\n");
+ }
+ } else if (!strncmp(*av, "zero", strlen(*av))) {
+ zero(ac,av);
+ } else if (!strncmp(*av, "print", strlen(*av))) {
+ list(--ac,++av);
+ } else if (!strncmp(*av, "list", strlen(*av))) {
+ list(--ac,++av);
+ } else if (!strncmp(*av, "show", strlen(*av))) {
+ do_acct++;
+ list(--ac,++av);
+ } else {
+ show_usage("Bad arguments");
+ }
+ return 0;
+}
+
+int
+main(ac, av)
+ int ac;
+ char **av;
+{
+#define MAX_ARGS 32
+#define WHITESP " \t\f\v\n\r"
+ char buf[BUFSIZ];
+ char *a, *p, *args[MAX_ARGS], *cmd = NULL;
+ char linename[10];
+ int i, c, lineno, nflag, qflag, pflag, status;
+ FILE *f = NULL;
+ pid_t preproc = 0;
+
+ s = socket( AF_INET6, SOCK_RAW, IPPROTO_RAW );
+ if ( s < 0 )
+ err(EX_UNAVAILABLE, "socket");
+
+ setbuf(stdout,0);
+
+ /*
+ * Only interpret the last command line argument as a file to
+ * be preprocessed if it is specified as an absolute pathname.
+ */
+
+ if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0) {
+ nflag = qflag = pflag = i = 0;
+ lineno = 0;
+
+ while ((c = getopt(ac, av, "D:U:np:q")) != -1)
+ switch(c) {
+ case 'D':
+ if (!pflag)
+ errx(EX_USAGE, "-D requires -p");
+ if (i > MAX_ARGS - 2)
+ errx(EX_USAGE,
+ "too many -D or -U options");
+ args[i++] = "-D";
+ args[i++] = optarg;
+ break;
+
+ case 'U':
+ if (!pflag)
+ errx(EX_USAGE, "-U requires -p");
+ if (i > MAX_ARGS - 2)
+ errx(EX_USAGE,
+ "too many -D or -U options");
+ args[i++] = "-U";
+ args[i++] = optarg;
+ break;
+
+ case 'n':
+ nflag = 1;
+ break;
+
+ case 'p':
+ pflag = 1;
+ cmd = optarg;
+ args[0] = cmd;
+ i = 1;
+ break;
+
+ case 'q':
+ qflag = 1;
+ break;
+
+ default:
+ show_usage(NULL);
+ }
+
+ av += optind;
+ ac -= optind;
+ if (ac != 1)
+ show_usage("extraneous filename arguments");
+
+ if ((f = fopen(av[0], "r")) == NULL)
+ err(EX_UNAVAILABLE, "fopen: %s", av[0]);
+
+ if (pflag) {
+ /* pipe through preprocessor (cpp or m4) */
+ int pipedes[2];
+
+ args[i] = 0;
+
+ if (pipe(pipedes) == -1)
+ err(EX_OSERR, "cannot create pipe");
+
+ switch((preproc = fork())) {
+ case -1:
+ err(EX_OSERR, "cannot fork");
+
+ case 0:
+ /* child */
+ if (dup2(fileno(f), 0) == -1 ||
+ dup2(pipedes[1], 1) == -1)
+ err(EX_OSERR, "dup2()");
+ fclose(f);
+ close(pipedes[1]);
+ close(pipedes[0]);
+ execvp(cmd, args);
+ err(EX_OSERR, "execvp(%s) failed", cmd);
+
+ default:
+ /* parent */
+ fclose(f);
+ close(pipedes[1]);
+ if ((f = fdopen(pipedes[0], "r")) == NULL) {
+ int savederrno = errno;
+
+ (void)kill(preproc, SIGTERM);
+ errno = savederrno;
+ err(EX_OSERR, "fdopen()");
+ }
+ }
+ }
+
+ while (fgets(buf, BUFSIZ, f)) {
+ lineno++;
+ sprintf(linename, "Line %d", lineno);
+ args[0] = linename;
+
+ if (*buf == '#')
+ continue;
+ if ((p = strchr(buf, '#')) != NULL)
+ *p = '\0';
+ i=1;
+ if (qflag) args[i++]="-q";
+ if (nflag) args[i++]="-n";
+ for (a = strtok(buf, WHITESP);
+ a && i < MAX_ARGS; a = strtok(NULL, WHITESP), i++)
+ args[i] = a;
+ if (i == (qflag? 2: 1))
+ continue;
+ if (i == MAX_ARGS)
+ errx(EX_USAGE, "%s: too many arguments", linename);
+ args[i] = NULL;
+
+ ip6fw_main(i, args);
+ }
+ fclose(f);
+ if (pflag) {
+ if (waitpid(preproc, &status, 0) != -1) {
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != EX_OK)
+ errx(EX_UNAVAILABLE,
+ "preprocessor exited with status %d",
+ WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ errx(EX_UNAVAILABLE,
+ "preprocessor exited with signal %d",
+ WTERMSIG(status));
+ }
+ }
+ }
+ } else
+ ip6fw_main(ac,av);
+ return 0;
+}
diff --git a/sbin/ip6fw/sample.sh b/sbin/ip6fw/sample.sh
new file mode 100644
index 0000000..0064140
--- /dev/null
+++ b/sbin/ip6fw/sample.sh
@@ -0,0 +1,28 @@
+#!/bin/sh -
+# $FreeBSD$
+
+fwcmd=/sbin/ip6fw
+
+$fwcmd -f flush
+
+#
+# loopback
+#
+$fwcmd add 1000 pass all from any to any via lo0
+
+#
+# ND
+#
+# DAD
+$fwcmd add 2000 pass ipv6-icmp from ff02::/16 to ::
+$fwcmd add 2100 pass ipv6-icmp from :: to ff02::/16
+# RS, RA, NS, NA, redirect...
+$fwcmd add 2300 pass ipv6-icmp from fe80::/10 to fe80::/10
+$fwcmd add 2400 pass ipv6-icmp from fe80::/10 to ff02::/16
+
+$fwcmd add 5000 pass tcp from any to any established
+
+# RIPng
+$fwcmd add 6000 pass udp from fe80::/10 521 to ff02::9 521
+
+$fwcmd add 65000 pass log all from any to any
diff --git a/sbin/ipf/Makefile b/sbin/ipf/Makefile
new file mode 100644
index 0000000..df57c80
--- /dev/null
+++ b/sbin/ipf/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+#.WAIT
+SUBDIR= libipf
+SUBDIR+= ipf ipfs ipfstat ipftest ipmon ipnat ippool ipresend
+#SUBDIR+= ipsend iptest rules
+
+.include <bsd.subdir.mk>
diff --git a/sbin/ipf/Makefile.inc b/sbin/ipf/Makefile.inc
new file mode 100644
index 0000000..3373b55
--- /dev/null
+++ b/sbin/ipf/Makefile.inc
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ipfilter
+CFLAGS+= -I${.CURDIR}/../../../contrib/ipfilter/tools
+CFLAGS+= -I${.CURDIR}/../../../sys
+CFLAGS+= -I${.CURDIR}/../../../sys/contrib/ipfilter
+CFLAGS+= -DSTATETOP -D__UIO_EXPOSE
+
+LIBIPF= ${.OBJDIR}/../libipf/libipf.a
+DPADD+= ${LIBIPF} ${LIBKVM}
+LDADD+= ${LIBIPF} -lkvm
+
+CLEANFILES+= y.tab.c y.tab.h
+
+.PATH: ${.CURDIR}/../../../contrib/ipfilter \
+ ${.CURDIR}/../../../contrib/ipfilter/lib \
+ ${.CURDIR}/../../../contrib/ipfilter/tools \
+ ${.CURDIR}/../../../contrib/ipfilter/man
+
+.include "../Makefile.inc"
diff --git a/sbin/ipf/ipf/Makefile b/sbin/ipf/ipf/Makefile
new file mode 100644
index 0000000..e0b887f
--- /dev/null
+++ b/sbin/ipf/ipf/Makefile
@@ -0,0 +1,41 @@
+# $FreeBSD$
+
+PROG= ipf
+SRCS= ipf.c ipfcomp.c ipf_y.c ipf_l.c bpf_filter.c
+MAN= ipf.8 ipf.4 ipf.5 ipl.4
+MLINKS= ipl.4 ipfilter.4 ipf.5 ipf.conf.5 ipf.5 ipf6.conf.5
+CFLAGS+= -I. -DIPFILTER_BPF
+
+DPSRCS+= ipf_l.h ipf_y.h
+
+CLEANFILES+= ipf_y.c ipf_y.h
+CLEANFILES+= ipf_l.c ipf_l.h
+
+ipf_y.c: ipf_y.y
+ ${YACC} -d ${.ALLSRC}
+ sed -e 's/yy/ipf_yy/g' \
+ -e 's/"ipf_y.y"/"..\/tools\/ipf_y.y"/' \
+ y.tab.c > ${.TARGET}
+ sed -e 's/yy/ipf_yy/g' \
+ y.tab.h > ${.TARGET:.c=.h}
+
+ipf_y.h: ipf_y.c
+
+ipf_l.c: lexer.c
+ sed -e 's/yy/ipf_yy/g' \
+ -e 's/y.tab.h/ipf_y.h/' \
+ -e 's/lexer.h/ipf_l.h/' \
+ ${.ALLSRC} > ${.TARGET}
+
+ipf_l.h: lexer.h
+ sed -e 's/yy/ipf_yy/g' \
+ ${.ALLSRC} > ${.TARGET}
+
+.if defined(RESCUE)
+LIBIPF_SRCS!= cd ${.CURDIR}/../libipf && ${MAKE} -V SRCS
+SRCS+= ${LIBIPF_SRCS}
+.else
+LDADD+= -lpcap
+.endif
+
+.include <bsd.prog.mk>
diff --git a/sbin/ipf/ipfs/Makefile b/sbin/ipf/ipfs/Makefile
new file mode 100644
index 0000000..a64f805
--- /dev/null
+++ b/sbin/ipf/ipfs/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= ipfs
+MAN= ipfs.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/ipf/ipfstat/Makefile b/sbin/ipf/ipfstat/Makefile
new file mode 100644
index 0000000..a33c5df
--- /dev/null
+++ b/sbin/ipf/ipfstat/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+NOGCCERROR= # defined
+
+PROG= ipfstat
+SRCS= ipfstat.c
+MAN= ipfstat.8
+DPADD+= ${LIBCURSES}
+LDADD+= -lcurses
+
+.include <bsd.prog.mk>
diff --git a/sbin/ipf/ipftest/Makefile b/sbin/ipf/ipftest/Makefile
new file mode 100644
index 0000000..02816cb
--- /dev/null
+++ b/sbin/ipf/ipftest/Makefile
@@ -0,0 +1,90 @@
+# $FreeBSD$
+
+PROG= ipftest
+SRCS= ipftest.c fil.c ip_frag.c ip_state.c ip_nat.c \
+ ip_proxy.c ip_auth.c ip_htable.c ip_lookup.c \
+ ip_pool.c ip_scan.c ip_sync.c ip_rules.c \
+ ip_fil.c ip_log.c ippool_y.c ippool_l.c ipf_y.c \
+ ipf_l.c ipnat_y.c ipnat_l.c md5.c radix.c bpf_filter.c
+MAN= ipftest.1
+
+CFLAGS+= -DIPFILTER_LOG -DIPFILTER_COMPILED -DIPFILTER_LOOKUP \
+ -DIPFILTER_SCAN -DIPFILTER_SYNC -DIPFILTER_CKSUM -I.
+
+.PATH: ${.CURDIR}/../../../sys/contrib/ipfilter/netinet
+
+DPSRCS+= ipnat_l.h ipnat_y.h ippool_l.h ippool_y.h ipf_l.h ipf_y.h
+
+CLEANFILES+= ipf_y.c ipf_y.h
+CLEANFILES+= ipf_l.c ipf_l.h
+CLEANFILES+= ipf.tab.c ipf.tab.h
+CLEANFILES+= ipnat_y.c ipnat_y.h
+CLEANFILES+= ipnat_l.c ipnat_l.h
+CLEANFILES+= ipnat.tab.c ipnat.tab.h
+CLEANFILES+= ippool_y.c ippool_y.h
+CLEANFILES+= ippool_l.c ippool_l.h
+CLEANFILES+= ippool.tab.c ippool.tab.h
+
+ipnat_y.c: ipnat_y.y
+ ${YACC} -b ipnat -d ${.ALLSRC}
+ sed -e 's/yy/ipnat_yy/g' \
+ -e 's/y.tab.c/ipnat_y.c/' \
+ -e s/\"ipnat_y.y\"/\"..\\/tools\\/ipnat_y.y\"/ \
+ ipnat.tab.c > ${.TARGET}
+ sed -e 's/yy/ipnat_yy/g' \
+ -e 's/y.tab.h/ipnat_y.h/' \
+ ipnat.tab.h > ${.TARGET:.c=.h}
+
+ipnat_y.h: ipnat_y.c
+
+ipnat_l.c: lexer.c
+ sed -e 's/yy/ipnat_yy/g' \
+ -e 's/y.tab.h/ipnat_y.h/' \
+ -e 's/lexer.h/ipnat_l.h/' \
+ ${.ALLSRC} > ${.TARGET}
+
+ipnat_l.h: lexer.h
+ sed -e 's/yy/ipnat_yy/g' \
+ ${.ALLSRC} > ${.TARGET}
+
+ippool_y.c: ippool_y.y
+ ${YACC} -b ippool -d ${.ALLSRC}
+ sed -e 's/yy/ippool_yy/g' \
+ -e 's/"ippool_y.y"/"..\/tools\/ippool_y.y"/' \
+ ippool.tab.c > ${.TARGET}
+ sed -e 's/yy/ippool_yy/g' \
+ ippool.tab.h > ${.TARGET:.c=.h}
+
+ippool_y.h: ippool_y.c
+
+ippool_l.c: lexer.c
+ sed -e 's/yy/ippool_yy/g' \
+ -e 's/y.tab.h/ippool_y.h/' \
+ -e 's/lexer.h/ippool_l.h/' \
+ ${.ALLSRC} > ${.TARGET}
+
+ippool_l.h: lexer.h
+ sed -e 's/yy/ippool_yy/g' \
+ ${.ALLSRC} > ${.TARGET}
+
+ipf_y.c: ipf_y.y
+ ${YACC} -b ipf -d ${.ALLSRC}
+ sed -e 's/yy/ipf_yy/g' \
+ -e 's/"ipf_y.y"/"..\/tools\/ipf_y.y"/' \
+ ipf.tab.c > ${.TARGET}
+ sed -e 's/yy/ipf_yy/g' \
+ ipf.tab.h > ${.TARGET:.c=.h}
+
+ipf_y.h: ipf_y.c
+
+ipf_l.c: lexer.c
+ sed -e 's/yy/ipf_yy/g' \
+ -e 's/y.tab.h/ipf_y.h/' \
+ -e 's/lexer.h/ipf_l.h/' \
+ ${.ALLSRC} > ${.TARGET}
+
+ipf_l.h: lexer.h
+ sed -e 's/yy/ipf_yy/g' \
+ ${.ALLSRC} > ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/sbin/ipf/ipmon/Makefile b/sbin/ipf/ipmon/Makefile
new file mode 100644
index 0000000..8103aee
--- /dev/null
+++ b/sbin/ipf/ipmon/Makefile
@@ -0,0 +1,34 @@
+# $FreeBSD$
+
+PROG= ipmon
+SRCS= ipmon.c ipmon_y.c ipmon_l.c
+MAN= ipmon.8
+
+CFLAGS+= -DLOGFAC=LOG_LOCAL0 -I.
+
+DPSRCS+= ipmon_l.h ipmon_y.h
+
+CLEANFILES+= ipmon_y.c ipmon_y.h
+CLEANFILES+= ipmon_l.c ipmon_l.h
+
+ipmon_y.c: ipmon_y.y
+ ${YACC} -d ${.ALLSRC}
+ sed -e 's/yy/ipmon_yy/g' \
+ -e 's/"ipmon_y.y"/"..\/tools\/ipmon_y.y"/' \
+ y.tab.c > ${.TARGET}
+ sed -e 's/yy/ipmon_yy/g' \
+ y.tab.h > ${.TARGET:.c=.h}
+
+ipmon_y.h: ipmon_y.c
+
+ipmon_l.c: lexer.c
+ sed -e 's/yy/ipmon_yy/g' \
+ -e 's/y.tab.h/ipmon_y.h/' \
+ -e 's/lexer.h/ipmon_l.h/' \
+ ${.ALLSRC} > ${.TARGET}
+
+ipmon_l.h: lexer.h
+ sed -e 's/yy/ipmon_yy/g' \
+ ${.ALLSRC} > ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/sbin/ipf/ipnat/Makefile b/sbin/ipf/ipnat/Makefile
new file mode 100644
index 0000000..2e24d26
--- /dev/null
+++ b/sbin/ipf/ipnat/Makefile
@@ -0,0 +1,36 @@
+# $FreeBSD$
+
+PROG= ipnat
+SRCS= ipnat.c ipnat_y.c ipnat_l.c
+MAN= ipnat.8 ipnat.4 ipnat.5
+MLINKS= ipnat.5 ipnat.conf.5
+CFLAGS+= -I.
+
+DPSRCS+= ipnat_l.h ipnat_y.h
+
+CLEANFILES+= ipnat_y.c ipnat_y.h
+CLEANFILES+= ipnat_l.c ipnat_l.h
+
+ipnat_y.c: ipnat_y.y
+ ${YACC} -d ${.ALLSRC}
+ sed -e 's/yy/ipnat_yy/g' \
+ -e 's/y.tab.c/ipnat_y.c/' \
+ -e s/\"ipnat_y.y\"/\"..\\/tools\\/ipnat_y.y\"/ \
+ y.tab.c > ${.TARGET}
+ sed -e 's/yy/ipnat_yy/g' \
+ -e 's/y.tab.h/ipnat_y.h/' \
+ y.tab.h > ${.TARGET:.c=.h}
+
+ipnat_y.h: ipnat_y.c
+
+ipnat_l.c: lexer.c
+ sed -e 's/yy/ipnat_yy/g' \
+ -e 's/y.tab.h/ipnat_y.h/' \
+ -e 's/lexer.h/ipnat_l.h/' \
+ ${.ALLSRC} > ${.TARGET}
+
+ipnat_l.h: lexer.h
+ sed -e 's/yy/ipnat_yy/g' \
+ ${.ALLSRC} > ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/sbin/ipf/ippool/Makefile b/sbin/ipf/ippool/Makefile
new file mode 100644
index 0000000..9ff7c6e
--- /dev/null
+++ b/sbin/ipf/ippool/Makefile
@@ -0,0 +1,33 @@
+# $FreeBSD$
+
+PROG= ippool
+SRCS= ippool_y.c ippool_l.c kmem.c ippool.c
+MAN= ippool.5 ippool.8
+CFLAGS+= -I.
+
+DPSRCS+= ippool_l.h ippool_y.h
+
+CLEANFILES+= ippool_y.c ippool_y.h
+CLEANFILES+= ippool_l.c ippool_l.h
+
+ippool_y.c: ippool_y.y
+ ${YACC} -d ${.ALLSRC}
+ sed -e 's/yy/ippool_yy/g' \
+ -e 's/"ippool_y.y"/"..\/tools\/ippool_y.y"/' \
+ y.tab.c > ${.TARGET}
+ sed -e 's/yy/ippool_yy/g' \
+ y.tab.h > ${.TARGET:.c=.h}
+
+ippool_y.h: ippool_y.c
+
+ippool_l.c: lexer.c
+ sed -e 's/yy/ippool_yy/g' \
+ -e 's/y.tab.h/ippool_y.h/' \
+ -e 's/lexer.h/ippool_l.h/' \
+ ${.ALLSRC} > ${.TARGET}
+
+ippool_l.h: lexer.h
+ sed -e 's/yy/ippool_yy/g' \
+ ${.ALLSRC} > ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/sbin/ipf/ipresend/Makefile b/sbin/ipf/ipresend/Makefile
new file mode 100644
index 0000000..5e0ac15
--- /dev/null
+++ b/sbin/ipf/ipresend/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= ipresend
+SRCS= ipresend.c ip.c resend.c sbpf.c sock.c 44arp.c
+MAN= ipresend.1
+
+.PATH: ${.CURDIR}/../../../contrib/ipfilter/ipsend
+
+.include <bsd.prog.mk>
diff --git a/sbin/ipf/ipsend/Makefile b/sbin/ipf/ipsend/Makefile
new file mode 100644
index 0000000..4ae4c34
--- /dev/null
+++ b/sbin/ipf/ipsend/Makefile
@@ -0,0 +1,39 @@
+# $FreeBSD$
+
+NOGCCERROR= # defined
+
+.include <bsd.own.mk>
+
+PROG= ipsend
+SRCS= ipsend.c ip.c ipsopt.c iplang_y.c iplang_l.l sbpf.c \
+ sock.c 44arp.c
+MAN= ipsend.1 ipsend.5
+DPADD+= ${LIBL}
+LDADD+= -ll
+
+CFLAGS+= -I${NETBSDSRCDIR}/dist/ipf/ipsend
+CFLAGS+= -I${NETBSDSRCDIR}/dist/ipf/iplang
+CFLAGS+= -I.
+
+CLEANFILES+= iplang_y.c iplang_y.h
+
+DPSRCS+= iplang_y.h
+
+.PATH: ${NETBSDSRCDIR}/dist/ipf/ipsend \
+ ${NETBSDSRCDIR}/dist/ipf/iplang
+
+iplang_y.c: iplang_y.y
+ ${YACC} -d ${.ALLSRC}
+ mv y.tab.c ${.TARGET}
+ mv y.tab.h ${.TARGET:.c=.h}
+
+iplang_y.h: iplang_y.c
+
+# XXX
+# We have a problem with make and linking ipsend
+# cc -o /home/source/src/usr.sbin/ipf/ipsend/../../../dist/ipf/ipsend .....
+# isn't correct.
+# Use .NOPATH as an workaround for that problem
+.NOPATH: ipsend
+
+.include <bsd.prog.mk>
diff --git a/sbin/ipf/iptest/Makefile b/sbin/ipf/iptest/Makefile
new file mode 100644
index 0000000..647471c
--- /dev/null
+++ b/sbin/ipf/iptest/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+PROG= iptest
+SRCS= iptest.c iptests.c ip.c sbpf.c sock.c 44arp.c
+MAN= iptest.1
+
+.PATH: ${NETBSDSRCDIR}/dist/ipf/ipsend
+
+.include <bsd.prog.mk>
diff --git a/sbin/ipf/libipf/Makefile b/sbin/ipf/libipf/Makefile
new file mode 100644
index 0000000..0ab36d2
--- /dev/null
+++ b/sbin/ipf/libipf/Makefile
@@ -0,0 +1,29 @@
+# $FreeBSD$
+
+LIB= ipf
+INTERNALLIB=
+
+SRCS= addicmp.c addipopt.c addkeep.c bcopywrap.c binprint.c \
+ buildopts.c checkrev.c count6bits.c count4bits.c debug.c \
+ extras.c facpri.c flags.c fill6bits.c genmask.c gethost.c \
+ getifname.c getline.c getnattype.c getport.c getportproto.c \
+ getproto.c getsumd.c hexdump.c hostmask.c hostname.c \
+ hostnum.c icmpcode.c initparse.c ionames.c \
+ ipoptsec.c ipf_dotuning.c ipft_ef.c ipft_hx.c ipft_pc.c \
+ ipft_sn.c ipft_td.c ipft_tx.c kmem.c kmemcpywrap.c \
+ kvatoname.c load_hash.c load_hashnode.c load_pool.c \
+ load_poolnode.c loglevel.c make_range.c mutex_emul.c \
+ nametokva.c nat_setgroupmap.c ntomask.c optname.c \
+ optprint.c optprintv6.c optvalue.c portname.c portnum.c \
+ ports.c print_toif.c printactivenat.c printaps.c printbuf.c \
+ printhash.c printhashnode.c printip.c printpool.c \
+ printpoolnode.c printfr.c printfraginfo.c printhostmap.c \
+ printifname.c printhostmask.c printlog.c printmask.c \
+ printnat.c printportcmp.c printpacket.c printpacket6.c \
+ printproto.c printsbuf.c printstate.c printtunable.c ratoi.c \
+ ratoui.c remove_hash.c remove_hashnode.c remove_pool.c \
+ remove_poolnode.c resetlexer.c rwlock_emul.c tcpflags.c \
+ tcp_flags.c to_interface.c var.c verbose.c v6ionames.c \
+ v6optvalue.c
+
+.include <bsd.lib.mk>
diff --git a/sbin/ipf/rules/Makefile b/sbin/ipf/rules/Makefile
new file mode 100644
index 0000000..a90907f
--- /dev/null
+++ b/sbin/ipf/rules/Makefile
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+MAN= mkfilters.1
+.if ${MKSHARE} != "no"
+FILESDIR= /usr/share/examples/ipf
+
+FILES= BASIC.NAT BASIC_1.FW BASIC_2.FW example.1 example.2 example.3 \
+ example.4 example.5 example.6 example.7 example.8 example.9 \
+ example.10 example.11 example.12 example.13 example.sr \
+ firewall ftp-proxy ftppxy mediaone nat-setup \
+ nat.eg server tcpstate mkfilters
+.endif
+
+.PATH: ${NETBSDSRCDIR}/dist/ipf/rules
+.include <bsd.prog.mk>
diff --git a/sbin/ipfw/Makefile b/sbin/ipfw/Makefile
new file mode 100644
index 0000000..023f682
--- /dev/null
+++ b/sbin/ipfw/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= ipfw
+SRCS= ipfw2.c
+WARNS?= 0
+MAN= ipfw.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
new file mode 100644
index 0000000..dea4bea
--- /dev/null
+++ b/sbin/ipfw/ipfw.8
@@ -0,0 +1,2440 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 16, 2006
+.Dt IPFW 8
+.Os
+.Sh NAME
+.Nm ipfw
+.Nd IP firewall and traffic shaper control program
+.Sh SYNOPSIS
+.Nm
+.Op Fl cq
+.Cm add
+.Ar rule
+.Nm
+.Op Fl acdefnNStT
+.Brq Cm list | show
+.Op Ar rule | first-last ...
+.Nm
+.Op Fl f | q
+.Cm flush
+.Nm
+.Op Fl q
+.Brq Cm delete | zero | resetlog
+.Op Cm set
+.Op Ar number ...
+.Nm
+.Cm enable
+.Brq Cm firewall | altq | one_pass | debug | verbose | dyn_keepalive
+.Nm
+.Cm disable
+.Brq Cm firewall | altq | one_pass | debug | verbose | dyn_keepalive
+.Pp
+.Nm
+.Cm set Oo Cm disable Ar number ... Oc Op Cm enable Ar number ...
+.Nm
+.Cm set move
+.Op Cm rule
+.Ar number Cm to Ar number
+.Nm
+.Cm set swap Ar number number
+.Nm
+.Cm set show
+.Pp
+.Nm
+.Cm table Ar number Cm add Ar addr Ns Oo / Ns Ar masklen Oc Op Ar value
+.Nm
+.Cm table Ar number Cm delete Ar addr Ns Op / Ns Ar masklen
+.Nm
+.Cm table Ar number Cm flush
+.Nm
+.Cm table Ar number Cm list
+.Pp
+.Nm
+.Brq Cm pipe | queue
+.Ar number
+.Cm config
+.Ar config-options
+.Nm
+.Op Fl s Op Ar field
+.Brq Cm pipe | queue
+.Brq Cm delete | list | show
+.Op Ar number ...
+.Pp
+.Nm
+.Op Fl cfnNqS
+.Oo
+.Fl p Ar preproc
+.Oo
+.Ar preproc-flags
+.Oc
+.Oc
+.Ar pathname
+.Sh DESCRIPTION
+The
+.Nm
+utility is the user interface for controlling the
+.Xr ipfw 4
+firewall and the
+.Xr dummynet 4
+traffic shaper in
+.Fx .
+.Pp
+An
+.Nm
+configuration, or
+.Em ruleset ,
+is made of a list of
+.Em rules
+numbered from 1 to 65535.
+Packets are passed to
+.Nm
+from a number of different places in the protocol stack
+(depending on the source and destination of the packet,
+it is possible that
+.Nm
+is invoked multiple times on the same packet).
+The packet passed to the firewall is compared
+against each of the rules in the firewall
+.Em ruleset .
+When a match is found, the action corresponding to the
+matching rule is performed.
+.Pp
+Depending on the action and certain system settings, packets
+can be reinjected into the firewall at some rule after the
+matching one for further processing.
+.Pp
+An
+.Nm
+ruleset always includes a
+.Em default
+rule (numbered 65535) which cannot be modified or deleted,
+and matches all packets.
+The action associated with the
+.Em default
+rule can be either
+.Cm deny
+or
+.Cm allow
+depending on how the kernel is configured.
+.Pp
+If the ruleset includes one or more rules with the
+.Cm keep-state
+or
+.Cm limit
+option, then
+.Nm
+assumes a
+.Em stateful
+behaviour, i.e., upon a match it will create dynamic rules matching
+the exact parameters (addresses and ports) of the matching packet.
+.Pp
+These dynamic rules, which have a limited lifetime, are checked
+at the first occurrence of a
+.Cm check-state ,
+.Cm keep-state
+or
+.Cm limit
+rule, and are typically used to open the firewall on-demand to
+legitimate traffic only.
+See the
+.Sx STATEFUL FIREWALL
+and
+.Sx EXAMPLES
+Sections below for more information on the stateful behaviour of
+.Nm .
+.Pp
+All rules (including dynamic ones) have a few associated counters:
+a packet count, a byte count, a log count and a timestamp
+indicating the time of the last match.
+Counters can be displayed or reset with
+.Nm
+commands.
+.Pp
+Rules can be added with the
+.Cm add
+command; deleted individually or in groups with the
+.Cm delete
+command, and globally (except those in set 31) with the
+.Cm flush
+command; displayed, optionally with the content of the
+counters, using the
+.Cm show
+and
+.Cm list
+commands.
+Finally, counters can be reset with the
+.Cm zero
+and
+.Cm resetlog
+commands.
+.Pp
+Also, each rule belongs to one of 32 different
+.Em sets
+, and there are
+.Nm
+commands to atomically manipulate sets, such as enable,
+disable, swap sets, move all rules in a set to another
+one, delete all rules in a set.
+These can be useful to
+install temporary configurations, or to test them.
+See Section
+.Sx SETS OF RULES
+for more information on
+.Em sets .
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+While listing, show counter values.
+The
+.Cm show
+command just implies this option.
+.It Fl b
+Only show the action and the comment, not the body of a rule.
+Implies
+.Fl c .
+.It Fl c
+When entering or showing rules, print them in compact form,
+i.e., without the optional "ip from any to any" string
+when this does not carry any additional information.
+.It Fl d
+While listing, show dynamic rules in addition to static ones.
+.It Fl e
+While listing, if the
+.Fl d
+option was specified, also show expired dynamic rules.
+.It Fl f
+Do not ask for confirmation for commands that can cause problems
+if misused,
+.No i.e. Cm flush .
+If there is no tty associated with the process, this is implied.
+.It Fl n
+Only check syntax of the command strings, without actually passing
+them to the kernel.
+.It Fl N
+Try to resolve addresses and service names in output.
+.It Fl q
+While
+.Cm add Ns ing ,
+.Cm zero Ns ing ,
+.Cm resetlog Ns ging
+or
+.Cm flush Ns ing ,
+be quiet about actions
+(implies
+.Fl f ) .
+This is useful for adjusting rules by executing multiple
+.Nm
+commands in a script
+(e.g.,
+.Ql sh\ /etc/rc.firewall ) ,
+or by processing a file of many
+.Nm
+rules across a remote login session.
+If a
+.Cm flush
+is performed in normal (verbose) mode (with the default kernel
+configuration), it prints a message.
+Because all rules are flushed, the message might not be delivered
+to the login session, causing the remote login session to be closed
+and the remainder of the ruleset to not be processed.
+Access to the console would then be required to recover.
+.It Fl S
+While listing rules, show the
+.Em set
+each rule belongs to.
+If this flag is not specified, disabled rules will not be
+listed.
+.It Fl s Op Ar field
+While listing pipes, sort according to one of the four
+counters (total or current packets or bytes).
+.It Fl t
+While listing, show last match timestamp (converted with ctime()).
+.It Fl T
+While listing, show last match timestamp (as seconds from the epoch).
+This form can be more convenient for postprocessing by scripts.
+.El
+.Pp
+To ease configuration, rules can be put into a file which is
+processed using
+.Nm
+as shown in the last synopsis line.
+An absolute
+.Ar pathname
+must be used.
+The file will be read line by line and applied as arguments to the
+.Nm
+utility.
+.Pp
+Optionally, a preprocessor can be specified using
+.Fl p Ar preproc
+where
+.Ar pathname
+is to be piped through.
+Useful preprocessors include
+.Xr cpp 1
+and
+.Xr m4 1 .
+If
+.Ar preproc
+does not start with a slash
+.Pq Ql /
+as its first character, the usual
+.Ev PATH
+name search is performed.
+Care should be taken with this in environments where not all
+file systems are mounted (yet) by the time
+.Nm
+is being run (e.g.\& when they are mounted over NFS).
+Once
+.Fl p
+has been specified, any additional arguments as passed on to the preprocessor
+for interpretation.
+This allows for flexible configuration files (like conditionalizing
+them on the local hostname) and the use of macros to centralize
+frequently required arguments like IP addresses.
+.Pp
+The
+.Nm
+.Cm pipe
+and
+.Cm queue
+commands are used to configure the traffic shaper, as shown in the
+.Sx TRAFFIC SHAPER (DUMMYNET) CONFIGURATION
+Section below.
+.Pp
+If the world and the kernel get out of sync the
+.Nm
+ABI may break, preventing you from being able to add any rules.
+This can
+adversely effect the booting process.
+You can use
+.Nm
+.Cm disable
+.Cm firewall
+to temporarily disable the firewall to regain access to the network,
+allowing you to fix the problem.
+.Sh PACKET FLOW
+A packet is checked against the active ruleset in multiple places
+in the protocol stack, under control of several sysctl variables.
+These places and variables are shown below, and it is important to
+have this picture in mind in order to design a correct ruleset.
+.Bd -literal -offset indent
+ ^ to upper layers V
+ | |
+ +----------->-----------+
+ ^ V
+ [ip(6)_input] [ip(6)_output] net.inet.ip.fw.enable=1
+ | |
+ ^ V
+ [ether_demux] [ether_output_frame] net.link.ether.ipfw=1
+ | |
+ +-->--[bdg_forward]-->--+ net.link.bridge.ipfw=1
+ ^ V
+ | to devices |
+.Ed
+.Pp
+As can be noted from the above picture, the number of
+times the same packet goes through the firewall can
+vary between 0 and 4 depending on packet source and
+destination, and system configuration.
+.Pp
+Note that as packets flow through the stack, headers can be
+stripped or added to it, and so they may or may not be available
+for inspection.
+E.g., incoming packets will include the MAC header when
+.Nm
+is invoked from
+.Cm ether_demux() ,
+but the same packets will have the MAC header stripped off when
+.Nm
+is invoked from
+.Cm ip_input()
+or
+.Cm ip6_input() .
+.Pp
+Also note that each packet is always checked against the complete ruleset,
+irrespective of the place where the check occurs, or the source of the packet.
+If a rule contains some match patterns or actions which are not valid
+for the place of invocation (e.g.\& trying to match a MAC header within
+.Cm ip_input
+or
+.Cm ip6_input ),
+the match pattern will not match, but a
+.Cm not
+operator in front of such patterns
+.Em will
+cause the pattern to
+.Em always
+match on those packets.
+It is thus the responsibility of
+the programmer, if necessary, to write a suitable ruleset to
+differentiate among the possible places.
+.Cm skipto
+rules can be useful here, as an example:
+.Bd -literal -offset indent
+# packets from ether_demux or bdg_forward
+ipfw add 10 skipto 1000 all from any to any layer2 in
+# packets from ip_input
+ipfw add 10 skipto 2000 all from any to any not layer2 in
+# packets from ip_output
+ipfw add 10 skipto 3000 all from any to any not layer2 out
+# packets from ether_output_frame
+ipfw add 10 skipto 4000 all from any to any layer2 out
+.Ed
+.Pp
+(yes, at the moment there is no way to differentiate between
+ether_demux and bdg_forward).
+.Sh SYNTAX
+In general, each keyword or argument must be provided as
+a separate command line argument, with no leading or trailing
+spaces.
+Keywords are case-sensitive, whereas arguments may
+or may not be case-sensitive depending on their nature
+(e.g.\& uid's are, hostnames are not).
+.Pp
+In
+.Nm ipfw2
+you can introduce spaces after commas ',' to make
+the line more readable.
+You can also put the entire
+command (including flags) into a single argument.
+E.g., the following forms are equivalent:
+.Bd -literal -offset indent
+ipfw -q add deny src-ip 10.0.0.0/24,127.0.0.1/8
+ipfw -q add deny src-ip 10.0.0.0/24, 127.0.0.1/8
+ipfw "-q add deny src-ip 10.0.0.0/24, 127.0.0.1/8"
+.Ed
+.Sh RULE FORMAT
+The format of
+.Nm
+rules is the following:
+.Bd -ragged -offset indent
+.Bk -words
+.Op Ar rule_number
+.Op Cm set Ar set_number
+.Op Cm prob Ar match_probability
+.Ar action
+.Op Cm log Op Cm logamount Ar number
+.Op Cm altq Ar queue
+.Ar body
+.Ek
+.Ed
+.Pp
+where the body of the rule specifies which information is used
+for filtering packets, among the following:
+.Pp
+.Bl -tag -width "Source and dest. addresses and ports" -offset XXX -compact
+.It Layer-2 header fields
+When available
+.It IPv4 and IPv6 Protocol
+TCP, UDP, ICMP, etc.
+.It Source and dest. addresses and ports
+.It Direction
+See Section
+.Sx PACKET FLOW
+.It Transmit and receive interface
+By name or address
+.It Misc. IP header fields
+Version, type of service, datagram length, identification,
+fragment flag (non-zero IP offset),
+Time To Live
+.It IP options
+.It IPv6 Extension headers
+Fragmentation, Hop-by-Hop options,
+source routing, IPSec options.
+.It IPv6 Flow-ID
+.It Misc. TCP header fields
+TCP flags (SYN, FIN, ACK, RST, etc.),
+sequence number, acknowledgment number,
+window
+.It TCP options
+.It ICMP types
+for ICMP packets
+.It ICMP6 types
+for ICMP6 packets
+.It User/group ID
+When the packet can be associated with a local socket.
+.It Divert status
+Whether a packet came from a divert socket (e.g.,
+.Xr natd 8 ) .
+.El
+.Pp
+Note that some of the above information, e.g.\& source MAC or IP addresses and
+TCP/UDP ports, could easily be spoofed, so filtering on those fields
+alone might not guarantee the desired results.
+.Bl -tag -width indent
+.It Ar rule_number
+Each rule is associated with a
+.Ar rule_number
+in the range 1..65535, with the latter reserved for the
+.Em default
+rule.
+Rules are checked sequentially by rule number.
+Multiple rules can have the same number, in which case they are
+checked (and listed) according to the order in which they have
+been added.
+If a rule is entered without specifying a number, the kernel will
+assign one in such a way that the rule becomes the last one
+before the
+.Em default
+rule.
+Automatic rule numbers are assigned by incrementing the last
+non-default rule number by the value of the sysctl variable
+.Ar net.inet.ip.fw.autoinc_step
+which defaults to 100.
+If this is not possible (e.g.\& because we would go beyond the
+maximum allowed rule number), the number of the last
+non-default value is used instead.
+.It Cm set Ar set_number
+Each rule is associated with a
+.Ar set_number
+in the range 0..31.
+Sets can be individually disabled and enabled, so this parameter
+is of fundamental importance for atomic ruleset manipulation.
+It can be also used to simplify deletion of groups of rules.
+If a rule is entered without specifying a set number,
+set 0 will be used.
+.br
+Set 31 is special in that it cannot be disabled,
+and rules in set 31 are not deleted by the
+.Nm ipfw flush
+command (but you can delete them with the
+.Nm ipfw delete set 31
+command).
+Set 31 is also used for the
+.Em default
+rule.
+.It Cm prob Ar match_probability
+A match is only declared with the specified probability
+(floating point number between 0 and 1).
+This can be useful for a number of applications such as
+random packet drop or
+(in conjunction with
+.Xr dummynet 4 )
+to simulate the effect of multiple paths leading to out-of-order
+packet delivery.
+.Pp
+Note: this condition is checked before any other condition, including
+ones such as keep-state or check-state which might have side effects.
+.It Cm log Op Cm logamount Ar number
+When a packet matches a rule with the
+.Cm log
+keyword, a message will be
+logged to
+.Xr syslogd 8
+with a
+.Dv LOG_SECURITY
+facility.
+The logging only occurs if the sysctl variable
+.Em net.inet.ip.fw.verbose
+is set to 1
+(which is the default when the kernel is compiled with
+.Dv IPFIREWALL_VERBOSE )
+and the number of packets logged so far for that
+particular rule does not exceed the
+.Cm logamount
+parameter.
+If no
+.Cm logamount
+is specified, the limit is taken from the sysctl variable
+.Em net.inet.ip.fw.verbose_limit .
+In both cases, a value of 0 removes the logging limit.
+.Pp
+Once the limit is reached, logging can be re-enabled by
+clearing the logging counter or the packet counter for that entry, see the
+.Cm resetlog
+command.
+.Pp
+Note: logging is done after all other packet matching conditions
+have been successfully verified, and before performing the final
+action (accept, deny, etc.) on the packet.
+.It Cm altq Ar queue
+When a packet matches a rule with the
+.Cm altq
+keyword, the ALTQ identifier for the given
+.Ar queue
+(see
+.Xr altq 4 )
+will be attached.
+Note that this ALTQ tag is only meaningful for packets going "out" of IPFW,
+and not being rejected or going to divert sockets.
+Note that if there is insufficient memory at the time the packet is
+processed, it will not be tagged, so it is wise to make your ALTQ
+"default" queue policy account for this.
+If multiple
+.Cm altq
+rules match a single packet, only the first one adds the ALTQ classification
+tag.
+In doing so, traffic may be shaped by using
+.Cm count Cm altq Ar queue
+rules for classification early in the ruleset, then later applying
+the filtering decision.
+For example,
+.Cm check-state
+and
+.Cm keep-state
+rules may come later and provide the actual filtering decisions in
+addition to the fallback ALTQ tag.
+.Pp
+You must run
+.Xr pfctl 8
+to set up the queues before IPFW will be able to look them up by name,
+and if the ALTQ disciplines are rearranged, the rules in containing the
+queue identifiers in the kernel will likely have gone stale and need
+to be reloaded.
+Stale queue identifiers will probably result in misclassification.
+.Pp
+All system ALTQ processing can be turned on or off via
+.Nm
+.Cm enable Ar altq
+and
+.Nm
+.Cm disable Ar altq .
+The usage of
+.Em net.inet.ip.fw.one_pass
+is irrelevant to ALTQ traffic shaping, as the actual rule action is followed
+always after adding an ALTQ tag.
+.El
+.Ss RULE ACTIONS
+A rule can be associated with one of the following actions, which
+will be executed when the packet matches the body of the rule.
+.Bl -tag -width indent
+.It Cm allow | accept | pass | permit
+Allow packets that match rule.
+The search terminates.
+.It Cm check-state
+Checks the packet against the dynamic ruleset.
+If a match is found, execute the action associated with
+the rule which generated this dynamic rule, otherwise
+move to the next rule.
+.br
+.Cm Check-state
+rules do not have a body.
+If no
+.Cm check-state
+rule is found, the dynamic ruleset is checked at the first
+.Cm keep-state
+or
+.Cm limit
+rule.
+.It Cm count
+Update counters for all packets that match rule.
+The search continues with the next rule.
+.It Cm deny | drop
+Discard packets that match this rule.
+The search terminates.
+.It Cm divert Ar port
+Divert packets that match this rule to the
+.Xr divert 4
+socket bound to port
+.Ar port .
+The search terminates.
+.It Cm fwd | forward Ar ipaddr Ns Op , Ns Ar port
+Change the next-hop on matching packets to
+.Ar ipaddr ,
+which can be an IP address or a host name.
+The search terminates if this rule matches.
+.Pp
+If
+.Ar ipaddr
+is a local address, then matching packets will be forwarded to
+.Ar port
+(or the port number in the packet if one is not specified in the rule)
+on the local machine.
+.br
+If
+.Ar ipaddr
+is not a local address, then the port number
+(if specified) is ignored, and the packet will be
+forwarded to the remote address, using the route as found in
+the local routing table for that IP.
+.br
+A
+.Ar fwd
+rule will not match layer-2 packets (those received
+on ether_input, ether_output, or bridged).
+.br
+The
+.Cm fwd
+action does not change the contents of the packet at all.
+In particular, the destination address remains unmodified, so
+packets forwarded to another system will usually be rejected by that system
+unless there is a matching rule on that system to capture them.
+For packets forwarded locally,
+the local address of the socket will be
+set to the original destination address of the packet.
+This makes the
+.Xr netstat 1
+entry look rather weird but is intended for
+use with transparent proxy servers.
+.Pp
+To enable
+.Cm fwd
+a custom kernel needs to be compiled with the option
+.Cd "options IPFIREWALL_FORWARD" .
+With the additional option
+.Cd "options IPFIREWALL_FORWARD_EXTENDED"
+all safeguards are removed and it also makes it possible to redirect
+packets destined to locally configured IP addresses.
+Please note that such rules apply to locally generated packets as
+well and great care is required to ensure proper behaviour for
+automatically generated packets like ICMP message size exceeded
+and others.
+.It Cm pipe Ar pipe_nr
+Pass packet to a
+.Xr dummynet 4
+.Dq pipe
+(for bandwidth limitation, delay, etc.).
+See the
+.Sx TRAFFIC SHAPER (DUMMYNET) CONFIGURATION
+Section for further information.
+The search terminates; however, on exit from the pipe and if
+the
+.Xr sysctl 8
+variable
+.Em net.inet.ip.fw.one_pass
+is not set, the packet is passed again to the firewall code
+starting from the next rule.
+.It Cm queue Ar queue_nr
+Pass packet to a
+.Xr dummynet 4
+.Dq queue
+(for bandwidth limitation using WF2Q+).
+.It Cm reject
+(Deprecated).
+Synonym for
+.Cm unreach host .
+.It Cm reset
+Discard packets that match this rule, and if the
+packet is a TCP packet, try to send a TCP reset (RST) notice.
+The search terminates.
+.It Cm reset6
+Discard packets that match this rule, and if the
+packet is a TCP packet, try to send a TCP reset (RST) notice.
+The search terminates.
+.It Cm skipto Ar number
+Skip all subsequent rules numbered less than
+.Ar number .
+The search continues with the first rule numbered
+.Ar number
+or higher.
+.It Cm tee Ar port
+Send a copy of packets matching this rule to the
+.Xr divert 4
+socket bound to port
+.Ar port .
+The search continues with the next rule.
+.It Cm unreach Ar code
+Discard packets that match this rule, and try to send an ICMP
+unreachable notice with code
+.Ar code ,
+where
+.Ar code
+is a number from 0 to 255, or one of these aliases:
+.Cm net , host , protocol , port ,
+.Cm needfrag , srcfail , net-unknown , host-unknown ,
+.Cm isolated , net-prohib , host-prohib , tosnet ,
+.Cm toshost , filter-prohib , host-precedence
+or
+.Cm precedence-cutoff .
+The search terminates.
+.It Cm unreach6 Ar code
+Discard packets that match this rule, and try to send an ICMPv6
+unreachable notice with code
+.Ar code ,
+where
+.Ar code
+is a number from 0, 1, 3 or 4, or one of these aliases:
+.Cm no-route, admin-prohib, address
+or
+.Cm port .
+The search terminates.
+.It Cm netgraph Ar cookie
+Divert packet into netgraph with given
+.Ar cookie .
+The search terminates.
+If packet is later returned from netgraph it is either
+accepted or continues with the next rule, depending on
+.Em net.inet.ip.fw.one_pass
+sysctl variable.
+.It Cm ngtee Ar cookie
+A copy of packet is diverted into netgraph, original
+packet is either accepted or continues with the next rule, depending on
+.Em net.inet.ip.fw.one_pass
+sysctl variable.
+See
+.Xr ng_ipfw 4
+for more information on
+.Cm netgraph
+and
+.Cm ngtee
+actions.
+.El
+.Ss RULE BODY
+The body of a rule contains zero or more patterns (such as
+specific source and destination addresses or ports,
+protocol options, incoming or outgoing interfaces, etc.)
+that the packet must match in order to be recognised.
+In general, the patterns are connected by (implicit)
+.Cm and
+operators -- i.e., all must match in order for the
+rule to match.
+Individual patterns can be prefixed by the
+.Cm not
+operator to reverse the result of the match, as in
+.Pp
+.Dl "ipfw add 100 allow ip from not 1.2.3.4 to any"
+.Pp
+Additionally, sets of alternative match patterns
+.Pq Em or-blocks
+can be constructed by putting the patterns in
+lists enclosed between parentheses ( ) or braces { }, and
+using the
+.Cm or
+operator as follows:
+.Pp
+.Dl "ipfw add 100 allow ip from { x or not y or z } to any"
+.Pp
+Only one level of parentheses is allowed.
+Beware that most shells have special meanings for parentheses
+or braces, so it is advisable to put a backslash \\ in front of them
+to prevent such interpretations.
+.Pp
+The body of a rule must in general include a source and destination
+address specifier.
+The keyword
+.Ar any
+can be used in various places to specify that the content of
+a required field is irrelevant.
+.Pp
+The rule body has the following format:
+.Bd -ragged -offset indent
+.Op Ar proto Cm from Ar src Cm to Ar dst
+.Op Ar options
+.Ed
+.Pp
+The first part (proto from src to dst) is for backward
+compatibility with earlier versions of
+.Fx .
+In modern
+.Fx
+any match pattern (including MAC headers, IP protocols,
+addresses and ports) can be specified in the
+.Ar options
+section.
+.Pp
+Rule fields have the following meaning:
+.Bl -tag -width indent
+.It Ar proto : protocol | Cm { Ar protocol Cm or ... }
+.It Ar protocol : Oo Cm not Oc Ar protocol-name | protocol-number
+An IP protocol specified by number or name
+(for a complete list see
+.Pa /etc/protocols ) ,
+or one of the following keywords:
+.Bl -tag -width indent
+.It Cm ip4 | ipv4
+Matches IPv4 packets.
+.It Cm ip6 | ipv6
+Matches IPv6 packets.
+.It Cm ip | all
+Matches any packet.
+.El
+.Pp
+The
+.Cm ip
+and
+.Cm ipv6
+in
+.Cm proto
+option will be treated as inner protocol.
+And, the
+.Cm ipv4
+is not available in
+.Cm proto
+option.
+.Pp
+The
+.Cm { Ar protocol Cm or ... }
+format (an
+.Em or-block )
+is provided for convenience only but its use is deprecated.
+.It Ar src No and Ar dst : Bro Cm addr | Cm { Ar addr Cm or ... } Brc Op Oo Cm not Oc Ar ports
+An address (or a list, see below)
+optionally followed by
+.Ar ports
+specifiers.
+.Pp
+The second format
+.Em ( or-block
+with multiple addresses) is provided for convenience only and
+its use is discouraged.
+.It Ar addr : Oo Cm not Oc Bro
+.Cm any | me | me6
+.Cm table Ns Pq Ar number Ns Op , Ns Ar value
+.Ar | addr-list | addr-set
+.Brc
+.It Cm any
+matches any IP address.
+.It Cm me
+matches any IP address configured on an interface in the system.
+.It Cm me6
+matches any IPv6 address configured on an interface in the system.
+The address list is evaluated at the time the packet is
+analysed.
+.It Cm table Ns Pq Ar number Ns Op , Ns Ar value
+Matches any IPv4 address for which an entry exists in the lookup table
+.Ar number .
+If an optional 32-bit unsigned
+.Ar value
+is also specified, an entry will match only if it has this value.
+See the
+.Sx LOOKUP TABLES
+section below for more information on lookup tables.
+.It Ar addr-list : ip-addr Ns Op Ns , Ns Ar addr-list
+.It Ar ip-addr :
+A host or subnet address specified in one of the following ways:
+.Bl -tag -width indent
+.It Ar numeric-ip | hostname
+Matches a single IPv4 address, specified as dotted-quad or a hostname.
+Hostnames are resolved at the time the rule is added to the firewall list.
+.It Ar addr Ns / Ns Ar masklen
+Matches all addresses with base
+.Ar addr
+(specified as an IP address or a hostname)
+and mask width of
+.Cm masklen
+bits.
+As an example, 1.2.3.4/25 will match
+all IP numbers from 1.2.3.0 to 1.2.3.127 .
+.It Ar addr Ns : Ns Ar mask
+Matches all addresses with base
+.Ar addr
+(specified as an IP address or a hostname)
+and the mask of
+.Ar mask ,
+specified as a dotted quad.
+As an example, 1.2.3.4:255.0.255.0 will match
+1.*.3.*.
+This form is advised only for non-contiguous
+masks.
+It is better to resort to the
+.Ar addr Ns / Ns Ar masklen
+format for contiguous masks, which is more compact and less
+error-prone.
+.El
+.It Ar addr-set : addr Ns Oo Ns / Ns Ar masklen Oc Ns Cm { Ns Ar list Ns Cm }
+.It Ar list : Bro Ar num | num-num Brc Ns Op Ns , Ns Ar list
+Matches all addresses with base address
+.Ar addr
+(specified as an IP address or a hostname)
+and whose last byte is in the list between braces { } .
+Note that there must be no spaces between braces and
+numbers (spaces after commas are allowed).
+Elements of the list can be specified as single entries
+or ranges.
+The
+.Ar masklen
+field is used to limit the size of the set of addresses,
+and can have any value between 24 and 32.
+If not specified,
+it will be assumed as 24.
+.br
+This format is particularly useful to handle sparse address sets
+within a single rule.
+Because the matching occurs using a
+bitmask, it takes constant time and dramatically reduces
+the complexity of rulesets.
+.br
+As an example, an address specified as 1.2.3.4/24{128,35-55,89}
+will match the following IP addresses:
+.br
+1.2.3.128, 1.2.3.35 to 1.2.3.55, 1.2.3.89 .
+.It Ar addr6-list : ip6-addr Ns Op Ns , Ns Ar addr6-list
+.It Ar ip6-addr :
+A host or subnet specified one of the following ways:
+.Pp
+.Bl -tag -width indent
+.It Ar numeric-ip | hostname
+Matches a single IPv6 address as allowed by
+.Xr inet_pton 3
+or a hostname.
+Hostnames are resolved at the time the rule is added to the firewall
+list.
+.It Ar addr Ns / Ns Ar masklen
+Matches all IPv6 addresses with base
+.Ar addr
+(specified as allowed by
+.Xr inet_pton
+or a hostname)
+and mask width of
+.Cm masklen
+bits.
+.El
+.Pp
+No support for sets of IPv6 addresses is provided because IPv6 addresses
+are typically random past the initial prefix.
+.It Ar ports : Bro Ar port | port Ns \&- Ns Ar port Ns Brc Ns Op , Ns Ar ports
+For protocols which support port numbers (such as TCP and UDP), optional
+.Cm ports
+may be specified as one or more ports or port ranges, separated
+by commas but no spaces, and an optional
+.Cm not
+operator.
+The
+.Ql \&-
+notation specifies a range of ports (including boundaries).
+.Pp
+Service names (from
+.Pa /etc/services )
+may be used instead of numeric port values.
+The length of the port list is limited to 30 ports or ranges,
+though one can specify larger ranges by using an
+.Em or-block
+in the
+.Cm options
+section of the rule.
+.Pp
+A backslash
+.Pq Ql \e
+can be used to escape the dash
+.Pq Ql -
+character in a service name (from a shell, the backslash must be
+typed twice to avoid the shell itself interpreting it as an escape
+character).
+.Pp
+.Dl "ipfw add count tcp from any ftp\e\e-data-ftp to any"
+.Pp
+Fragmented packets which have a non-zero offset (i.e., not the first
+fragment) will never match a rule which has one or more port
+specifications.
+See the
+.Cm frag
+option for details on matching fragmented packets.
+.El
+.Ss RULE OPTIONS (MATCH PATTERNS)
+Additional match patterns can be used within
+rules.
+Zero or more of these so-called
+.Em options
+can be present in a rule, optionally prefixed by the
+.Cm not
+operand, and possibly grouped into
+.Em or-blocks .
+.Pp
+The following match patterns can be used (listed in alphabetical order):
+.Bl -tag -width indent
+.It Cm // this is a comment.
+Inserts the specified text as a comment in the rule.
+Everything following // is considered as a comment and stored in the rule.
+You can have comment-only rules, which are listed as having a
+.Cm count
+action followed by the comment.
+.It Cm bridged
+Alias for
+.Cm layer2 .
+.It Cm diverted
+Matches only packets generated by a divert socket.
+.It Cm diverted-loopback
+Matches only packets coming from a divert socket back into the IP stack
+input for delivery.
+.It Cm diverted-output
+Matches only packets going from a divert socket back outward to the IP
+stack output for delivery.
+.It Cm dst-ip Ar ip-address
+Matches IPv4 packets whose destination IP is one of the address(es)
+specified as argument.
+.It Bro Cm dst-ip6 | dst-ipv6 Brc Ar ip6-address
+Matches IPv6 packets whose destination IP is one of the address(es)
+specified as argument.
+.It Cm dst-port Ar ports
+Matches IP packets whose destination port is one of the port(s)
+specified as argument.
+.It Cm established
+Matches TCP packets that have the RST or ACK bits set.
+.It Cm ext6hdr Ar header
+Matches IPv6 packets containing the extended header given by
+.Ar header .
+Supported headers are:
+.Pp
+Fragment,
+.Pq Cm frag ,
+Hop-to-hop options
+.Pq Cm hopopt ,
+Source routing
+.Pq Cm route ,
+Destination options
+.Pq Cm dstopt ,
+IPSec authentication headers
+.Pq Cm ah ,
+and IPSec encapsulated security payload headers
+.Pq Cm esp .
+.It Cm flow-id Ar labels
+Matches IPv6 packets containing any of the flow labels given in
+.Ar labels .
+.Ar labels
+is a comma seperate list of numeric flow labels.
+.It Cm frag
+Matches packets that are fragments and not the first
+fragment of an IP datagram.
+Note that these packets will not have
+the next protocol header (e.g.\& TCP, UDP) so options that look into
+these headers cannot match.
+.It Cm gid Ar group
+Matches all TCP or UDP packets sent by or received for a
+.Ar group .
+A
+.Ar group
+may be specified by name or number.
+This option should be used only if debug.mpsafenet=0 to avoid possible
+deadlocks due to layering violations in its implementation.
+.It Cm jail Ar prisonID
+Matches all TCP or UDP packets sent by or received for the
+jail whos prison ID is
+.Ar prisonID .
+This option should be used only if debug.mpsafenet=0 to avoid possible
+deadlocks due to layering violations in its implementation.
+.It Cm icmptypes Ar types
+Matches ICMP packets whose ICMP type is in the list
+.Ar types .
+The list may be specified as any combination of
+individual types (numeric) separated by commas.
+.Em Ranges are not allowed.
+The supported ICMP types are:
+.Pp
+echo reply
+.Pq Cm 0 ,
+destination unreachable
+.Pq Cm 3 ,
+source quench
+.Pq Cm 4 ,
+redirect
+.Pq Cm 5 ,
+echo request
+.Pq Cm 8 ,
+router advertisement
+.Pq Cm 9 ,
+router solicitation
+.Pq Cm 10 ,
+time-to-live exceeded
+.Pq Cm 11 ,
+IP header bad
+.Pq Cm 12 ,
+timestamp request
+.Pq Cm 13 ,
+timestamp reply
+.Pq Cm 14 ,
+information request
+.Pq Cm 15 ,
+information reply
+.Pq Cm 16 ,
+address mask request
+.Pq Cm 17
+and address mask reply
+.Pq Cm 18 .
+.It Cm icmp6types Ar types
+Matches ICMP6 packets whose ICMP6 type is in the list of
+.Ar types .
+The list may be specified as any combination of
+individual types (numeric) separated by commas.
+.Em Ranges are not allowed.
+.It Cm in | out
+Matches incoming or outgoing packets, respectively.
+.Cm in
+and
+.Cm out
+are mutually exclusive (in fact,
+.Cm out
+is implemented as
+.Cm not in Ns No ).
+.It Cm ipid Ar id-list
+Matches IPv4 packets whose
+.Cm ip_id
+field has value included in
+.Ar id-list ,
+which is either a single value or a list of values or ranges
+specified in the same way as
+.Ar ports .
+.It Cm iplen Ar len-list
+Matches IP packets whose total length, including header and data, is
+in the set
+.Ar len-list ,
+which is either a single value or a list of values or ranges
+specified in the same way as
+.Ar ports .
+.It Cm ipoptions Ar spec
+Matches packets whose IPv4 header contains the comma separated list of
+options specified in
+.Ar spec .
+The supported IP options are:
+.Pp
+.Cm ssrr
+(strict source route),
+.Cm lsrr
+(loose source route),
+.Cm rr
+(record packet route) and
+.Cm ts
+(timestamp).
+The absence of a particular option may be denoted
+with a
+.Ql \&! .
+.It Cm ipprecedence Ar precedence
+Matches IPv4 packets whose precedence field is equal to
+.Ar precedence .
+.It Cm ipsec
+Matches packets that have IPSEC history associated with them
+(i.e., the packet comes encapsulated in IPSEC, the kernel
+has IPSEC support and IPSEC_FILTERGIF option, and can correctly
+decapsulate it).
+.Pp
+Note that specifying
+.Cm ipsec
+is different from specifying
+.Cm proto Ar ipsec
+as the latter will only look at the specific IP protocol field,
+irrespective of IPSEC kernel support and the validity of the IPSEC data.
+.Pp
+Further note that this flag is silently ignored in kernels without
+IPSEC support.
+It does not affect rule processing when given and the
+rules are handled as if with no
+.Cm ipsec
+flag.
+.It Cm iptos Ar spec
+Matches IPv4 packets whose
+.Cm tos
+field contains the comma separated list of
+service types specified in
+.Ar spec .
+The supported IP types of service are:
+.Pp
+.Cm lowdelay
+.Pq Dv IPTOS_LOWDELAY ,
+.Cm throughput
+.Pq Dv IPTOS_THROUGHPUT ,
+.Cm reliability
+.Pq Dv IPTOS_RELIABILITY ,
+.Cm mincost
+.Pq Dv IPTOS_MINCOST ,
+.Cm congestion
+.Pq Dv IPTOS_CE .
+The absence of a particular type may be denoted
+with a
+.Ql \&! .
+.It Cm ipttl Ar ttl-list
+Matches IPv4 packets whose time to live is included in
+.Ar ttl-list ,
+which is either a single value or a list of values or ranges
+specified in the same way as
+.Ar ports .
+.It Cm ipversion Ar ver
+Matches IP packets whose IP version field is
+.Ar ver .
+.It Cm keep-state
+Upon a match, the firewall will create a dynamic rule, whose
+default behaviour is to match bidirectional traffic between
+source and destination IP/port using the same protocol.
+The rule has a limited lifetime (controlled by a set of
+.Xr sysctl 8
+variables), and the lifetime is refreshed every time a matching
+packet is found.
+.It Cm layer2
+Matches only layer2 packets, i.e., those passed to
+.Nm
+from ether_demux() and ether_output_frame().
+.It Cm limit Bro Cm src-addr | src-port | dst-addr | dst-port Brc Ar N
+The firewall will only allow
+.Ar N
+connections with the same
+set of parameters as specified in the rule.
+One or more
+of source and destination addresses and ports can be
+specified.
+Currently,
+only IPv4 flows are supported.
+.It Cm { MAC | mac } Ar dst-mac src-mac
+Match packets with a given
+.Ar dst-mac
+and
+.Ar src-mac
+addresses, specified as the
+.Cm any
+keyword (matching any MAC address), or six groups of hex digits
+separated by colons,
+and optionally followed by a mask indicating the significant bits.
+The mask may be specified using either of the following methods:
+.Bl -enum -width indent
+.It
+A slash
+.Pq /
+followed by the number of significant bits.
+For example, an address with 33 significant bits could be specified as:
+.Pp
+.Dl "MAC 10:20:30:40:50:60/33 any"
+.Pp
+.It
+An ampersand
+.Pq &
+followed by a bitmask specified as six groups of hex digits separated
+by colons.
+For example, an address in which the last 16 bits are significant could
+be specified as:
+.Pp
+.Dl "MAC 10:20:30:40:50:60&00:00:00:00:ff:ff any"
+.Pp
+Note that the ampersand character has a special meaning in many shells
+and should generally be escaped.
+.Pp
+.El
+Note that the order of MAC addresses (destination first,
+source second) is
+the same as on the wire, but the opposite of the one used for
+IP addresses.
+.It Cm mac-type Ar mac-type
+Matches packets whose Ethernet Type field
+corresponds to one of those specified as argument.
+.Ar mac-type
+is specified in the same way as
+.Cm port numbers
+(i.e., one or more comma-separated single values or ranges).
+You can use symbolic names for known values such as
+.Em vlan , ipv4, ipv6 .
+Values can be entered as decimal or hexadecimal (if prefixed by 0x),
+and they are always printed as hexadecimal (unless the
+.Cm -N
+option is used, in which case symbolic resolution will be attempted).
+.It Cm proto Ar protocol
+Matches packets with the corresponding IP protocol.
+.It Cm recv | xmit | via Brq Ar ifX | Ar if Ns Cm * | Ar ipno | Ar any
+Matches packets received, transmitted or going through,
+respectively, the interface specified by exact name
+.Ns No ( Ar ifX Ns No ),
+by device name
+.Ns No ( Ar if Ns Ar * Ns No ),
+by IP address, or through some interface.
+.Pp
+The
+.Cm via
+keyword causes the interface to always be checked.
+If
+.Cm recv
+or
+.Cm xmit
+is used instead of
+.Cm via ,
+then only the receive or transmit interface (respectively)
+is checked.
+By specifying both, it is possible to match packets based on
+both receive and transmit interface, e.g.:
+.Pp
+.Dl "ipfw add deny ip from any to any out recv ed0 xmit ed1"
+.Pp
+The
+.Cm recv
+interface can be tested on either incoming or outgoing packets,
+while the
+.Cm xmit
+interface can only be tested on outgoing packets.
+So
+.Cm out
+is required (and
+.Cm in
+is invalid) whenever
+.Cm xmit
+is used.
+.Pp
+A packet may not have a receive or transmit interface: packets
+originating from the local host have no receive interface,
+while packets destined for the local host have no transmit
+interface.
+.It Cm setup
+Matches TCP packets that have the SYN bit set but no ACK bit.
+This is the short form of
+.Dq Li tcpflags\ syn,!ack .
+.It Cm src-ip Ar ip-address
+Matches IPv4 packets whose source IP is one of the address(es)
+specified as an argument.
+.It Cm src-ip6 Ar ip6-address
+Matches IPv6 packets whose source IP is one of the address(es)
+specified as an argument.
+.It Cm src-port Ar ports
+Matches IP packets whose source port is one of the port(s)
+specified as argument.
+.It Cm tcpack Ar ack
+TCP packets only.
+Match if the TCP header acknowledgment number field is set to
+.Ar ack .
+.It Cm tcpdatalen Ar tcpdatalen-list
+Matches TCP packets whose length of TCP data is
+.Ar tcpdatalen-list ,
+which is either a single value or a list of values or ranges
+specified in the same way as
+.Ar ports .
+.It Cm tcpflags Ar spec
+TCP packets only.
+Match if the TCP header contains the comma separated list of
+flags specified in
+.Ar spec .
+The supported TCP flags are:
+.Pp
+.Cm fin ,
+.Cm syn ,
+.Cm rst ,
+.Cm psh ,
+.Cm ack
+and
+.Cm urg .
+The absence of a particular flag may be denoted
+with a
+.Ql \&! .
+A rule which contains a
+.Cm tcpflags
+specification can never match a fragmented packet which has
+a non-zero offset.
+See the
+.Cm frag
+option for details on matching fragmented packets.
+.It Cm tcpseq Ar seq
+TCP packets only.
+Match if the TCP header sequence number field is set to
+.Ar seq .
+.It Cm tcpwin Ar win
+TCP packets only.
+Match if the TCP header window field is set to
+.Ar win .
+.It Cm tcpoptions Ar spec
+TCP packets only.
+Match if the TCP header contains the comma separated list of
+options specified in
+.Ar spec .
+The supported TCP options are:
+.Pp
+.Cm mss
+(maximum segment size),
+.Cm window
+(tcp window advertisement),
+.Cm sack
+(selective ack),
+.Cm ts
+(rfc1323 timestamp) and
+.Cm cc
+(rfc1644 t/tcp connection count).
+The absence of a particular option may be denoted
+with a
+.Ql \&! .
+.It Cm uid Ar user
+Match all TCP or UDP packets sent by or received for a
+.Ar user .
+A
+.Ar user
+may be matched by name or identification number.
+This option should be used only if debug.mpsafenet=0 to avoid possible
+deadlocks due to layering violations in its implementation.
+.It Cm verrevpath
+For incoming packets,
+a routing table lookup is done on the packet's source address.
+If the interface on which the packet entered the system matches the
+outgoing interface for the route,
+the packet matches.
+If the interfaces do not match up,
+the packet does not match.
+All outgoing packets or packets with no incoming interface match.
+.Pp
+The name and functionality of the option is intentionally similar to
+the Cisco IOS command:
+.Pp
+.Dl ip verify unicast reverse-path
+.Pp
+This option can be used to make anti-spoofing rules to reject all
+packets with source addresses not from this interface.
+See also the option
+.Cm antispoof .
+.It Cm versrcreach
+For incoming packets,
+a routing table lookup is done on the packet's source address.
+If a route to the source address exists, but not the default route
+or a blackhole/reject route, the packet matches.
+Otherwise, the packet does not match.
+All outgoing packets match.
+.Pp
+The name and functionality of the option is intentionally similar to
+the Cisco IOS command:
+.Pp
+.Dl ip verify unicast source reachable-via any
+.Pp
+This option can be used to make anti-spoofing rules to reject all
+packets whose source address is unreachable.
+.It Cm antispoof
+For incoming packets, the packet's source address is checked if it
+belongs to a directly connected network.
+If the network is directly connected, then the interface the packet
+came on in is compared to the interface the network is connected to.
+When incoming interface and directly connected interface are not the
+same, the packet does not match.
+Otherwise, the packet does match.
+All outgoing packets match.
+.Pp
+This option can be used to make anti-spoofing rules to reject all
+packets that pretend to be from a directly connected network but do
+not come in through that interface.
+This option is similar to but more restricted than
+.Cm verrevpath
+because it engages only on packets with source addresses of directly
+connected networks instead of all source addresses.
+.El
+.Sh LOOKUP TABLES
+Lookup tables are useful to handle large sparse address sets,
+typically from a hundred to several thousands of entries.
+There may be up to 128 different lookup tables, numbered 0 to 127.
+.Pp
+Each entry is represented by an
+.Ar addr Ns Op / Ns Ar masklen
+and will match all addresses with base
+.Ar addr
+(specified as an IP address or a hostname)
+and mask width of
+.Ar masklen
+bits.
+If
+.Ar masklen
+is not specified, it defaults to 32.
+When looking up an IP address in a table, the most specific
+entry will match.
+Associated with each entry is a 32-bit unsigned
+.Ar value ,
+which can optionally be checked by a rule matching code.
+When adding an entry, if
+.Ar value
+is not specified, it defaults to 0.
+.Pp
+An entry can be added to a table
+.Pq Cm add ,
+removed from a table
+.Pq Cm delete ,
+a table can be examined
+.Pq Cm list
+or flushed
+.Pq Cm flush .
+.Pp
+Internally, each table is stored in a Radix tree, the same way as
+the routing table (see
+.Xr route 4 ) .
+.Pp
+Lookup tables currently support IPv4 addresses only.
+.Pp
+The
+.Cm tablearg
+feature provides the ability to use a value, looked up in the table, as
+the argument for a rule action.
+This can significantly reduce number of rules in some configurations.
+The
+.Cm tablearg
+argument can be used with the following actions:
+.Cm pipe , queue, divert, tee, netgraph, ngtee .
+See the
+.Sx EXAMPLES
+Section for example usage of tables and the tablearg keyword.
+.Sh SETS OF RULES
+Each rule belongs to one of 32 different
+.Em sets
+, numbered 0 to 31.
+Set 31 is reserved for the default rule.
+.Pp
+By default, rules are put in set 0, unless you use the
+.Cm set N
+attribute when entering a new rule.
+Sets can be individually and atomically enabled or disabled,
+so this mechanism permits an easy way to store multiple configurations
+of the firewall and quickly (and atomically) switch between them.
+The command to enable/disable sets is
+.Bd -ragged -offset indent
+.Nm
+.Cm set Oo Cm disable Ar number ... Oc Op Cm enable Ar number ...
+.Ed
+.Pp
+where multiple
+.Cm enable
+or
+.Cm disable
+sections can be specified.
+Command execution is atomic on all the sets specified in the command.
+By default, all sets are enabled.
+.Pp
+When you disable a set, its rules behave as if they do not exist
+in the firewall configuration, with only one exception:
+.Bd -ragged -offset indent
+dynamic rules created from a rule before it had been disabled
+will still be active until they expire.
+In order to delete
+dynamic rules you have to explicitly delete the parent rule
+which generated them.
+.Ed
+.Pp
+The set number of rules can be changed with the command
+.Bd -ragged -offset indent
+.Nm
+.Cm set move
+.Brq Cm rule Ar rule-number | old-set
+.Cm to Ar new-set
+.Ed
+.Pp
+Also, you can atomically swap two rulesets with the command
+.Bd -ragged -offset indent
+.Nm
+.Cm set swap Ar first-set second-set
+.Ed
+.Pp
+See the
+.Sx EXAMPLES
+Section on some possible uses of sets of rules.
+.Sh STATEFUL FIREWALL
+Stateful operation is a way for the firewall to dynamically
+create rules for specific flows when packets that
+match a given pattern are detected.
+Support for stateful
+operation comes through the
+.Cm check-state , keep-state
+and
+.Cm limit
+options of
+.Nm rules .
+.Pp
+Dynamic rules are created when a packet matches a
+.Cm keep-state
+or
+.Cm limit
+rule, causing the creation of a
+.Em dynamic
+rule which will match all and only packets with
+a given
+.Em protocol
+between a
+.Em src-ip/src-port dst-ip/dst-port
+pair of addresses
+.Em ( src
+and
+.Em dst
+are used here only to denote the initial match addresses, but they
+are completely equivalent afterwards).
+Dynamic rules will be checked at the first
+.Cm check-state, keep-state
+or
+.Cm limit
+occurrence, and the action performed upon a match will be the same
+as in the parent rule.
+.Pp
+Note that no additional attributes other than protocol and IP addresses
+and ports are checked on dynamic rules.
+.Pp
+The typical use of dynamic rules is to keep a closed firewall configuration,
+but let the first TCP SYN packet from the inside network install a
+dynamic rule for the flow so that packets belonging to that session
+will be allowed through the firewall:
+.Pp
+.Dl "ipfw add check-state"
+.Dl "ipfw add allow tcp from my-subnet to any setup keep-state"
+.Dl "ipfw add deny tcp from any to any"
+.Pp
+A similar approach can be used for UDP, where an UDP packet coming
+from the inside will install a dynamic rule to let the response through
+the firewall:
+.Pp
+.Dl "ipfw add check-state"
+.Dl "ipfw add allow udp from my-subnet to any keep-state"
+.Dl "ipfw add deny udp from any to any"
+.Pp
+Dynamic rules expire after some time, which depends on the status
+of the flow and the setting of some
+.Cm sysctl
+variables.
+See Section
+.Sx SYSCTL VARIABLES
+for more details.
+For TCP sessions, dynamic rules can be instructed to periodically
+send keepalive packets to refresh the state of the rule when it is
+about to expire.
+.Pp
+See Section
+.Sx EXAMPLES
+for more examples on how to use dynamic rules.
+.Sh TRAFFIC SHAPER (DUMMYNET) CONFIGURATION
+.Nm
+is also the user interface for the
+.Xr dummynet 4
+traffic shaper.
+.Pp
+.Nm dummynet
+operates by first using the firewall to classify packets and divide them into
+.Em flows ,
+using any match pattern that can be used in
+.Nm
+rules.
+Depending on local policies, a flow can contain packets for a single
+TCP connection, or from/to a given host, or entire subnet, or a
+protocol type, etc.
+.Pp
+Packets belonging to the same flow are then passed to either of two
+different objects, which implement the traffic regulation:
+.Bl -hang -offset XXXX
+.It Em pipe
+A pipe emulates a link with given bandwidth, propagation delay,
+queue size and packet loss rate.
+Packets are queued in front of the pipe as they come out from the classifier,
+and then transferred to the pipe according to the pipe's parameters.
+.Pp
+.It Em queue
+A queue
+is an abstraction used to implement the WF2Q+
+(Worst-case Fair Weighted Fair Queueing) policy, which is
+an efficient variant of the WFQ policy.
+.br
+The queue associates a
+.Em weight
+and a reference pipe to each flow, and then all backlogged (i.e.,
+with packets queued) flows linked to the same pipe share the pipe's
+bandwidth proportionally to their weights.
+Note that weights are not priorities; a flow with a lower weight
+is still guaranteed to get its fraction of the bandwidth even if a
+flow with a higher weight is permanently backlogged.
+.Pp
+.El
+In practice,
+.Em pipes
+can be used to set hard limits to the bandwidth that a flow can use, whereas
+.Em queues
+can be used to determine how different flow share the available bandwidth.
+.Pp
+The
+.Em pipe
+and
+.Em queue
+configuration commands are the following:
+.Bd -ragged -offset indent
+.Cm pipe Ar number Cm config Ar pipe-configuration
+.Pp
+.Cm queue Ar number Cm config Ar queue-configuration
+.Ed
+.Pp
+The following parameters can be configured for a pipe:
+.Pp
+.Bl -tag -width indent -compact
+.It Cm bw Ar bandwidth | device
+Bandwidth, measured in
+.Sm off
+.Op Cm K | M
+.Brq Cm bit/s | Byte/s .
+.Sm on
+.Pp
+A value of 0 (default) means unlimited bandwidth.
+The unit must immediately follow the number, as in
+.Pp
+.Dl "ipfw pipe 1 config bw 300Kbit/s"
+.Pp
+If a device name is specified instead of a numeric value, as in
+.Pp
+.Dl "ipfw pipe 1 config bw tun0"
+.Pp
+then the transmit clock is supplied by the specified device.
+At the moment only the
+.Xr tun 4
+device supports this
+functionality, for use in conjunction with
+.Xr ppp 8 .
+.Pp
+.It Cm delay Ar ms-delay
+Propagation delay, measured in milliseconds.
+The value is rounded to the next multiple of the clock tick
+(typically 10ms, but it is a good practice to run kernels
+with
+.Dq "options HZ=1000"
+to reduce
+the granularity to 1ms or less).
+Default value is 0, meaning no delay.
+.El
+.Pp
+The following parameters can be configured for a queue:
+.Pp
+.Bl -tag -width indent -compact
+.It Cm pipe Ar pipe_nr
+Connects a queue to the specified pipe.
+Multiple queues (with the same or different weights) can be connected to
+the same pipe, which specifies the aggregate rate for the set of queues.
+.Pp
+.It Cm weight Ar weight
+Specifies the weight to be used for flows matching this queue.
+The weight must be in the range 1..100, and defaults to 1.
+.El
+.Pp
+Finally, the following parameters can be configured for both
+pipes and queues:
+.Pp
+.Bl -tag -width XXXX -compact
+.Pp
+.It Cm buckets Ar hash-table-size
+Specifies the size of the hash table used for storing the
+various queues.
+Default value is 64 controlled by the
+.Xr sysctl 8
+variable
+.Em net.inet.ip.dummynet.hash_size ,
+allowed range is 16 to 65536.
+.Pp
+.It Cm mask Ar mask-specifier
+Packets sent to a given pipe or queue by an
+.Nm
+rule can be further classified into multiple flows, each of which is then
+sent to a different
+.Em dynamic
+pipe or queue.
+A flow identifier is constructed by masking the IP addresses,
+ports and protocol types as specified with the
+.Cm mask
+options in the configuration of the pipe or queue.
+For each different flow identifier, a new pipe or queue is created
+with the same parameters as the original object, and matching packets
+are sent to it.
+.Pp
+Thus, when
+.Em dynamic pipes
+are used, each flow will get the same bandwidth as defined by the pipe,
+whereas when
+.Em dynamic queues
+are used, each flow will share the parent's pipe bandwidth evenly
+with other flows generated by the same queue (note that other queues
+with different weights might be connected to the same pipe).
+.br
+Available mask specifiers are a combination of one or more of the following:
+.Pp
+.Cm dst-ip Ar mask ,
+.Cm dst-ip6 Ar mask ,
+.Cm src-ip Ar mask ,
+.Cm src-ip6 Ar mask ,
+.Cm dst-port Ar mask ,
+.Cm src-port Ar mask ,
+.Cm flow-id Ar mask ,
+.Cm proto Ar mask
+or
+.Cm all ,
+.Pp
+where the latter means all bits in all fields are significant.
+.Pp
+.It Cm noerror
+When a packet is dropped by a dummynet queue or pipe, the error
+is normally reported to the caller routine in the kernel, in the
+same way as it happens when a device queue fills up.
+Setting this
+option reports the packet as successfully delivered, which can be
+needed for some experimental setups where you want to simulate
+loss or congestion at a remote router.
+.Pp
+.It Cm plr Ar packet-loss-rate
+Packet loss rate.
+Argument
+.Ar packet-loss-rate
+is a floating-point number between 0 and 1, with 0 meaning no
+loss, 1 meaning 100% loss.
+The loss rate is internally represented on 31 bits.
+.Pp
+.It Cm queue Brq Ar slots | size Ns Cm Kbytes
+Queue size, in
+.Ar slots
+or
+.Cm KBytes .
+Default value is 50 slots, which
+is the typical queue size for Ethernet devices.
+Note that for slow speed links you should keep the queue
+size short or your traffic might be affected by a significant
+queueing delay.
+E.g., 50 max-sized ethernet packets (1500 bytes) mean 600Kbit
+or 20s of queue on a 30Kbit/s pipe.
+Even worse effects can result if you get packets from an
+interface with a much larger MTU, e.g.\& the loopback interface
+with its 16KB packets.
+.Pp
+.It Cm red | gred Ar w_q Ns / Ns Ar min_th Ns / Ns Ar max_th Ns / Ns Ar max_p
+Make use of the RED (Random Early Detection) queue management algorithm.
+.Ar w_q
+and
+.Ar max_p
+are floating
+point numbers between 0 and 1 (0 not included), while
+.Ar min_th
+and
+.Ar max_th
+are integer numbers specifying thresholds for queue management
+(thresholds are computed in bytes if the queue has been defined
+in bytes, in slots otherwise).
+The
+.Xr dummynet 4
+also supports the gentle RED variant (gred).
+Three
+.Xr sysctl 8
+variables can be used to control the RED behaviour:
+.Bl -tag -width indent
+.It Em net.inet.ip.dummynet.red_lookup_depth
+specifies the accuracy in computing the average queue
+when the link is idle (defaults to 256, must be greater than zero)
+.It Em net.inet.ip.dummynet.red_avg_pkt_size
+specifies the expected average packet size (defaults to 512, must be
+greater than zero)
+.It Em net.inet.ip.dummynet.red_max_pkt_size
+specifies the expected maximum packet size, only used when queue
+thresholds are in bytes (defaults to 1500, must be greater than zero).
+.El
+.El
+.Pp
+When used with IPv6 data, dummynet currently has several limitations.
+First, debug.mpsafenet=0 must be set.
+Second, the information necessicary to route link-local packets to an
+interface is not avalable after processing by dummynet so those packets
+are dropped in the output path.
+Care should be taken to insure that link-local packets are not passed to
+dummynet.
+.Sh CHECKLIST
+Here are some important points to consider when designing your
+rules:
+.Bl -bullet
+.It
+Remember that you filter both packets going
+.Cm in
+and
+.Cm out .
+Most connections need packets going in both directions.
+.It
+Remember to test very carefully.
+It is a good idea to be near the console when doing this.
+If you cannot be near the console,
+use an auto-recovery script such as the one in
+.Pa /usr/share/examples/ipfw/change_rules.sh .
+.It
+Do not forget the loopback interface.
+.El
+.Sh FINE POINTS
+.Bl -bullet
+.It
+There are circumstances where fragmented datagrams are unconditionally
+dropped.
+TCP packets are dropped if they do not contain at least 20 bytes of
+TCP header, UDP packets are dropped if they do not contain a full 8
+byte UDP header, and ICMP packets are dropped if they do not contain
+4 bytes of ICMP header, enough to specify the ICMP type, code, and
+checksum.
+These packets are simply logged as
+.Dq pullup failed
+since there may not be enough good data in the packet to produce a
+meaningful log entry.
+.It
+Another type of packet is unconditionally dropped, a TCP packet with a
+fragment offset of one.
+This is a valid packet, but it only has one use, to try
+to circumvent firewalls.
+When logging is enabled, these packets are
+reported as being dropped by rule -1.
+.It
+If you are logged in over a network, loading the
+.Xr kld 4
+version of
+.Nm
+is probably not as straightforward as you would think.
+I recommend the following command line:
+.Bd -literal -offset indent
+kldload ipfw && \e
+ipfw add 32000 allow ip from any to any
+.Ed
+.Pp
+Along the same lines, doing an
+.Bd -literal -offset indent
+ipfw flush
+.Ed
+.Pp
+in similar surroundings is also a bad idea.
+.It
+The
+.Nm
+filter list may not be modified if the system security level
+is set to 3 or higher
+(see
+.Xr init 8
+for information on system security levels).
+.El
+.Sh PACKET DIVERSION
+A
+.Xr divert 4
+socket bound to the specified port will receive all packets
+diverted to that port.
+If no socket is bound to the destination port, or if the divert module is
+not loaded, or if the kernel was not compiled with divert socket support,
+the packets are dropped.
+.Sh SYSCTL VARIABLES
+A set of
+.Xr sysctl 8
+variables controls the behaviour of the firewall and
+associated modules
+.Pq Nm dummynet , bridge .
+These are shown below together with their default value
+(but always check with the
+.Xr sysctl 8
+command what value is actually in use) and meaning:
+.Bl -tag -width indent
+.It Em net.inet.ip.dummynet.expire : No 1
+Lazily delete dynamic pipes/queue once they have no pending traffic.
+You can disable this by setting the variable to 0, in which case
+the pipes/queues will only be deleted when the threshold is reached.
+.It Em net.inet.ip.dummynet.hash_size : No 64
+Default size of the hash table used for dynamic pipes/queues.
+This value is used when no
+.Cm buckets
+option is specified when configuring a pipe/queue.
+.It Em net.inet.ip.dummynet.max_chain_len : No 16
+Target value for the maximum number of pipes/queues in a hash bucket.
+The product
+.Cm max_chain_len*hash_size
+is used to determine the threshold over which empty pipes/queues
+will be expired even when
+.Cm net.inet.ip.dummynet.expire=0 .
+.It Em net.inet.ip.dummynet.red_lookup_depth : No 256
+.It Em net.inet.ip.dummynet.red_avg_pkt_size : No 512
+.It Em net.inet.ip.dummynet.red_max_pkt_size : No 1500
+Parameters used in the computations of the drop probability
+for the RED algorithm.
+.It Em net.inet.ip.fw.autoinc_step : No 100
+Delta between rule numbers when auto-generating them.
+The value must be in the range 1..1000.
+.It Em net.inet.ip.fw.curr_dyn_buckets : Em net.inet.ip.fw.dyn_buckets
+The current number of buckets in the hash table for dynamic rules
+(readonly).
+.It Em net.inet.ip.fw.debug : No 1
+Controls debugging messages produced by
+.Nm .
+.It Em net.inet.ip.fw.dyn_buckets : No 256
+The number of buckets in the hash table for dynamic rules.
+Must be a power of 2, up to 65536.
+It only takes effect when all dynamic rules have expired, so you
+are advised to use a
+.Cm flush
+command to make sure that the hash table is resized.
+.It Em net.inet.ip.fw.dyn_count : No 3
+Current number of dynamic rules
+(read-only).
+.It Em net.inet.ip.fw.dyn_keepalive : No 1
+Enables generation of keepalive packets for
+.Cm keep-state
+rules on TCP sessions.
+A keepalive is generated to both
+sides of the connection every 5 seconds for the last 20
+seconds of the lifetime of the rule.
+.It Em net.inet.ip.fw.dyn_max : No 8192
+Maximum number of dynamic rules.
+When you hit this limit, no more dynamic rules can be
+installed until old ones expire.
+.It Em net.inet.ip.fw.dyn_ack_lifetime : No 300
+.It Em net.inet.ip.fw.dyn_syn_lifetime : No 20
+.It Em net.inet.ip.fw.dyn_fin_lifetime : No 1
+.It Em net.inet.ip.fw.dyn_rst_lifetime : No 1
+.It Em net.inet.ip.fw.dyn_udp_lifetime : No 5
+.It Em net.inet.ip.fw.dyn_short_lifetime : No 30
+These variables control the lifetime, in seconds, of dynamic
+rules.
+Upon the initial SYN exchange the lifetime is kept short,
+then increased after both SYN have been seen, then decreased
+again during the final FIN exchange or when a RST is received.
+Both
+.Em dyn_fin_lifetime
+and
+.Em dyn_rst_lifetime
+must be strictly lower than 5 seconds, the period of
+repetition of keepalives.
+The firewall enforces that.
+.It Em net.inet.ip.fw.enable : No 1
+Enables the firewall.
+Setting this variable to 0 lets you run your machine without
+firewall even if compiled in.
+.It Em net.inet.ip.fw.one_pass : No 1
+When set, the packet exiting from the
+.Xr dummynet 4
+pipe or from
+.Xr ng_ipfw 4
+node is not passed though the firewall again.
+Otherwise, after an action, the packet is
+reinjected into the firewall at the next rule.
+.It Em net.inet.ip.fw.verbose : No 1
+Enables verbose messages.
+.It Em net.inet.ip.fw.verbose_limit : No 0
+Limits the number of messages produced by a verbose firewall.
+.It Em net.inet6.ip6.fw.deny_unknown_exthdrs : No 1
+If enabled packets with unknown IPv6 Extension Headers will be denied.
+.It Em net.link.ether.ipfw : No 0
+Controls whether layer-2 packets are passed to
+.Nm .
+Default is no.
+.It Em net.link.bridge.ipfw : No 0
+Controls whether bridged packets are passed to
+.Nm .
+Default is no.
+.El
+.Pp
+.Sh EXAMPLES
+There are far too many possible uses of
+.Nm
+so this Section will only give a small set of examples.
+.Pp
+.Ss BASIC PACKET FILTERING
+This command adds an entry which denies all tcp packets from
+.Em cracker.evil.org
+to the telnet port of
+.Em wolf.tambov.su
+from being forwarded by the host:
+.Pp
+.Dl "ipfw add deny tcp from cracker.evil.org to wolf.tambov.su telnet"
+.Pp
+This one disallows any connection from the entire cracker's
+network to my host:
+.Pp
+.Dl "ipfw add deny ip from 123.45.67.0/24 to my.host.org"
+.Pp
+A first and efficient way to limit access (not using dynamic rules)
+is the use of the following rules:
+.Pp
+.Dl "ipfw add allow tcp from any to any established"
+.Dl "ipfw add allow tcp from net1 portlist1 to net2 portlist2 setup"
+.Dl "ipfw add allow tcp from net3 portlist3 to net3 portlist3 setup"
+.Dl "..."
+.Dl "ipfw add deny tcp from any to any"
+.Pp
+The first rule will be a quick match for normal TCP packets,
+but it will not match the initial SYN packet, which will be
+matched by the
+.Cm setup
+rules only for selected source/destination pairs.
+All other SYN packets will be rejected by the final
+.Cm deny
+rule.
+.Pp
+If you administer one or more subnets, you can take advantage
+of the address sets and or-blocks and write extremely
+compact rulesets which selectively enable services to blocks
+of clients, as below:
+.Pp
+.Dl "goodguys=\*q{ 10.1.2.0/24{20,35,66,18} or 10.2.3.0/28{6,3,11} }\*q"
+.Dl "badguys=\*q10.1.2.0/24{8,38,60}\*q"
+.Dl ""
+.Dl "ipfw add allow ip from ${goodguys} to any"
+.Dl "ipfw add deny ip from ${badguys} to any"
+.Dl "... normal policies ..."
+.Pp
+The
+.Cm verrevpath
+option could be used to do automated anti-spoofing by adding the
+following to the top of a ruleset:
+.Pp
+.Dl "ipfw add deny ip from any to any not verrevpath in"
+.Pp
+This rule drops all incoming packets that appear to be coming to the
+system on the wrong interface.
+For example, a packet with a source
+address belonging to a host on a protected internal network would be
+dropped if it tried to enter the system from an external interface.
+.Pp
+The
+.Cm antispoof
+option could be used to do similar but more restricted anti-spoofing
+by adding the following to the top of a ruleset:
+.Pp
+.Dl "ipfw add deny ip from any to any not antispoof in"
+.Pp
+This rule drops all incoming packets that appear to be coming from another
+directly connected system but on the wrong interface.
+For example, a packet with a source address of
+.Li 192.168.0.0/24
+, configured on
+.Li fxp0
+, but coming in on
+.Li fxp1
+would be dropped.
+.Ss DYNAMIC RULES
+In order to protect a site from flood attacks involving fake
+TCP packets, it is safer to use dynamic rules:
+.Pp
+.Dl "ipfw add check-state"
+.Dl "ipfw add deny tcp from any to any established"
+.Dl "ipfw add allow tcp from my-net to any setup keep-state"
+.Pp
+This will let the firewall install dynamic rules only for
+those connection which start with a regular SYN packet coming
+from the inside of our network.
+Dynamic rules are checked when encountering the first
+.Cm check-state
+or
+.Cm keep-state
+rule.
+A
+.Cm check-state
+rule should usually be placed near the beginning of the
+ruleset to minimize the amount of work scanning the ruleset.
+Your mileage may vary.
+.Pp
+To limit the number of connections a user can open
+you can use the following type of rules:
+.Pp
+.Dl "ipfw add allow tcp from my-net/24 to any setup limit src-addr 10"
+.Dl "ipfw add allow tcp from any to me setup limit src-addr 4"
+.Pp
+The former (assuming it runs on a gateway) will allow each host
+on a /24 network to open at most 10 TCP connections.
+The latter can be placed on a server to make sure that a single
+client does not use more than 4 simultaneous connections.
+.Pp
+.Em BEWARE :
+stateful rules can be subject to denial-of-service attacks
+by a SYN-flood which opens a huge number of dynamic rules.
+The effects of such attacks can be partially limited by
+acting on a set of
+.Xr sysctl 8
+variables which control the operation of the firewall.
+.Pp
+Here is a good usage of the
+.Cm list
+command to see accounting records and timestamp information:
+.Pp
+.Dl ipfw -at list
+.Pp
+or in short form without timestamps:
+.Pp
+.Dl ipfw -a list
+.Pp
+which is equivalent to:
+.Pp
+.Dl ipfw show
+.Pp
+Next rule diverts all incoming packets from 192.168.2.0/24
+to divert port 5000:
+.Pp
+.Dl ipfw divert 5000 ip from 192.168.2.0/24 to any in
+.Pp
+.Ss TRAFFIC SHAPING
+The following rules show some of the applications of
+.Nm
+and
+.Xr dummynet 4
+for simulations and the like.
+.Pp
+This rule drops random incoming packets with a probability
+of 5%:
+.Pp
+.Dl "ipfw add prob 0.05 deny ip from any to any in"
+.Pp
+A similar effect can be achieved making use of dummynet pipes:
+.Pp
+.Dl "ipfw add pipe 10 ip from any to any"
+.Dl "ipfw pipe 10 config plr 0.05"
+.Pp
+We can use pipes to artificially limit bandwidth, e.g.\& on a
+machine acting as a router, if we want to limit traffic from
+local clients on 192.168.2.0/24 we do:
+.Pp
+.Dl "ipfw add pipe 1 ip from 192.168.2.0/24 to any out"
+.Dl "ipfw pipe 1 config bw 300Kbit/s queue 50KBytes"
+.Pp
+note that we use the
+.Cm out
+modifier so that the rule is not used twice.
+Remember in fact that
+.Nm
+rules are checked both on incoming and outgoing packets.
+.Pp
+Should we want to simulate a bidirectional link with bandwidth
+limitations, the correct way is the following:
+.Pp
+.Dl "ipfw add pipe 1 ip from any to any out"
+.Dl "ipfw add pipe 2 ip from any to any in"
+.Dl "ipfw pipe 1 config bw 64Kbit/s queue 10Kbytes"
+.Dl "ipfw pipe 2 config bw 64Kbit/s queue 10Kbytes"
+.Pp
+The above can be very useful, e.g.\& if you want to see how
+your fancy Web page will look for a residential user who
+is connected only through a slow link.
+You should not use only one pipe for both directions, unless
+you want to simulate a half-duplex medium (e.g.\& AppleTalk,
+Ethernet, IRDA).
+It is not necessary that both pipes have the same configuration,
+so we can also simulate asymmetric links.
+.Pp
+Should we want to verify network performance with the RED queue
+management algorithm:
+.Pp
+.Dl "ipfw add pipe 1 ip from any to any"
+.Dl "ipfw pipe 1 config bw 500Kbit/s queue 100 red 0.002/30/80/0.1"
+.Pp
+Another typical application of the traffic shaper is to
+introduce some delay in the communication.
+This can significantly affect applications which do a lot of Remote
+Procedure Calls, and where the round-trip-time of the
+connection often becomes a limiting factor much more than
+bandwidth:
+.Pp
+.Dl "ipfw add pipe 1 ip from any to any out"
+.Dl "ipfw add pipe 2 ip from any to any in"
+.Dl "ipfw pipe 1 config delay 250ms bw 1Mbit/s"
+.Dl "ipfw pipe 2 config delay 250ms bw 1Mbit/s"
+.Pp
+Per-flow queueing can be useful for a variety of purposes.
+A very simple one is counting traffic:
+.Pp
+.Dl "ipfw add pipe 1 tcp from any to any"
+.Dl "ipfw add pipe 1 udp from any to any"
+.Dl "ipfw add pipe 1 ip from any to any"
+.Dl "ipfw pipe 1 config mask all"
+.Pp
+The above set of rules will create queues (and collect
+statistics) for all traffic.
+Because the pipes have no limitations, the only effect is
+collecting statistics.
+Note that we need 3 rules, not just the last one, because
+when
+.Nm
+tries to match IP packets it will not consider ports, so we
+would not see connections on separate ports as different
+ones.
+.Pp
+A more sophisticated example is limiting the outbound traffic
+on a net with per-host limits, rather than per-network limits:
+.Pp
+.Dl "ipfw add pipe 1 ip from 192.168.2.0/24 to any out"
+.Dl "ipfw add pipe 2 ip from any to 192.168.2.0/24 in"
+.Dl "ipfw pipe 1 config mask src-ip 0x000000ff bw 200Kbit/s queue 20Kbytes"
+.Dl "ipfw pipe 2 config mask dst-ip 0x000000ff bw 200Kbit/s queue 20Kbytes"
+.Ss LOOKUP TABLES
+In the following example, we need to create several traffic bandwidth
+classes and we need different hosts/networks to fall into different classes.
+We create one pipe for each class and configure them accordingly.
+Then we create a single table and fill it with IP subnets and addresses.
+For each subnet/host we set the argument equal to the number of the pipe
+that it should use.
+Then we classify traffic using a single rule:
+.Pp
+.Dl "ipfw pipe 1 config bw 1000Kbyte/s"
+.Dl "ipfw pipe 4 config bw 4000Kbyte/s"
+.Dl "..."
+.Dl "ipfw table 1 add 192.168.2.0/24 1"
+.Dl "ipfw table 1 add 192.168.0.0/27 4"
+.Dl "ipfw table 1 add 192.168.0.2 1"
+.Dl "..."
+.Dl "ipfw pipe tablearg ip from table(1) to any"
+.Ss SETS OF RULES
+To add a set of rules atomically, e.g.\& set 18:
+.Pp
+.Dl "ipfw set disable 18"
+.Dl "ipfw add NN set 18 ... # repeat as needed"
+.Dl "ipfw set enable 18"
+.Pp
+To delete a set of rules atomically the command is simply:
+.Pp
+.Dl "ipfw delete set 18"
+.Pp
+To test a ruleset and disable it and regain control if something goes wrong:
+.Pp
+.Dl "ipfw set disable 18"
+.Dl "ipfw add NN set 18 ... # repeat as needed"
+.Dl "ipfw set enable 18; echo done; sleep 30 && ipfw set disable 18"
+.Pp
+Here if everything goes well, you press control-C before the "sleep"
+terminates, and your ruleset will be left active.
+Otherwise, e.g.\& if
+you cannot access your box, the ruleset will be disabled after
+the sleep terminates thus restoring the previous situation.
+.Sh SEE ALSO
+.Xr cpp 1 ,
+.Xr m4 1 ,
+.Xr altq 4 ,
+.Xr divert 4 ,
+.Xr dummynet 4 ,
+.Xr if_bridge 4 ,
+.Xr ip 4 ,
+.Xr ipfirewall 4 ,
+.Xr ng_ipfw 4 ,
+.Xr protocols 5 ,
+.Xr services 5 ,
+.Xr init 8 ,
+.Xr kldload 8 ,
+.Xr reboot 8 ,
+.Xr sysctl 8 ,
+.Xr syslogd 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 2.0 .
+.Xr dummynet 4
+was introduced in
+.Fx 2.2.8 .
+Stateful extensions were introduced in
+.Fx 4.0 .
+.Nm ipfw2
+was introduced in Summer 2002.
+.Sh AUTHORS
+.An Ugen J. S. Antsilevich ,
+.An Poul-Henning Kamp ,
+.An Alex Nash ,
+.An Archie Cobbs ,
+.An Luigi Rizzo .
+.Pp
+.An -nosplit
+API based upon code written by
+.An Daniel Boulet
+for BSDI.
+.Pp
+Work on
+.Xr dummynet 4
+traffic shaper supported by Akamba Corp.
+.Sh BUGS
+Use of dummynet with IPv6 requires that debug.mpsafenet be set to 0.
+.Pp
+The syntax has grown over the years and sometimes it might be confusing.
+Unfortunately, backward compatibility prevents cleaning up mistakes
+made in the definition of the syntax.
+.Pp
+.Em !!! WARNING !!!
+.Pp
+Misconfiguring the firewall can put your computer in an unusable state,
+possibly shutting down network services and requiring console access to
+regain control of it.
+.Pp
+Incoming packet fragments diverted by
+.Cm divert
+are reassembled before delivery to the socket.
+The action used on those packet is the one from the
+rule which matches the first fragment of the packet.
+.Pp
+Packets diverted to userland, and then reinserted by a userland process
+may lose various packet attributes.
+The packet source interface name
+will be preserved if it is shorter than 8 bytes and the userland process
+saves and reuses the sockaddr_in
+(as does
+.Xr natd 8 ) ;
+otherwise, it may be lost.
+If a packet is reinserted in this manner, later rules may be incorrectly
+applied, making the order of
+.Cm divert
+rules in the rule sequence very important.
+.Pp
+Dummynet drops all packets with IPv6 link-local addresses.
+.Pp
+Rules using
+.Cm uid
+or
+.Cm gid
+may not behave as expected.
+In particular, incoming SYN packets may
+have no uid or gid associated with them since they do not yet belong
+to a TCP connection, and the uid/gid associated with a packet may not
+be as expected if the associated process calls
+.Xr setuid 2
+or similar system calls.
+.Pp
+Rules which use uid, gid or jail based matching should be used only
+if debug.mpsafenet=0 to avoid possible deadlocks due to layering
+violations in its implementation.
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
new file mode 100644
index 0000000..13e1df3
--- /dev/null
+++ b/sbin/ipfw/ipfw2.c
@@ -0,0 +1,5237 @@
+/*
+ * Copyright (c) 2002-2003 Luigi Rizzo
+ * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Idea and grammar partially left from:
+ * Copyright (c) 1993 Daniel Boulet
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ *
+ * NEW command line interface for IP firewall facility
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/queue.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <limits.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <timeconv.h> /* XXX do we need this ? */
+#include <unistd.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <net/if.h>
+#include <net/pfvar.h>
+#include <net/route.h> /* def. of struct route */
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip_fw.h>
+#include <netinet/ip_dummynet.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+int
+ do_resolv, /* Would try to resolve all */
+ do_time, /* Show time stamps */
+ do_quiet, /* Be quiet in add and flush */
+ do_pipe, /* this cmd refers to a pipe */
+ do_sort, /* field to sort results (0 = no) */
+ do_dynamic, /* display dynamic rules */
+ do_expired, /* display expired dynamic rules */
+ do_compact, /* show rules in compact mode */
+ do_force, /* do not ask for confirmation */
+ show_sets, /* display rule sets */
+ test_only, /* only check syntax */
+ comment_only, /* only print action and comment */
+ verbose;
+
+#define IP_MASK_ALL 0xffffffff
+/*
+ * the following macro returns an error message if we run out of
+ * arguments.
+ */
+#define NEED1(msg) {if (!ac) errx(EX_USAGE, msg);}
+
+/*
+ * _s_x is a structure that stores a string <-> token pairs, used in
+ * various places in the parser. Entries are stored in arrays,
+ * with an entry with s=NULL as terminator.
+ * The search routines are match_token() and match_value().
+ * Often, an element with x=0 contains an error string.
+ *
+ */
+struct _s_x {
+ char const *s;
+ int x;
+};
+
+static struct _s_x f_tcpflags[] = {
+ { "syn", TH_SYN },
+ { "fin", TH_FIN },
+ { "ack", TH_ACK },
+ { "psh", TH_PUSH },
+ { "rst", TH_RST },
+ { "urg", TH_URG },
+ { "tcp flag", 0 },
+ { NULL, 0 }
+};
+
+static struct _s_x f_tcpopts[] = {
+ { "mss", IP_FW_TCPOPT_MSS },
+ { "maxseg", IP_FW_TCPOPT_MSS },
+ { "window", IP_FW_TCPOPT_WINDOW },
+ { "sack", IP_FW_TCPOPT_SACK },
+ { "ts", IP_FW_TCPOPT_TS },
+ { "timestamp", IP_FW_TCPOPT_TS },
+ { "cc", IP_FW_TCPOPT_CC },
+ { "tcp option", 0 },
+ { NULL, 0 }
+};
+
+/*
+ * IP options span the range 0 to 255 so we need to remap them
+ * (though in fact only the low 5 bits are significant).
+ */
+static struct _s_x f_ipopts[] = {
+ { "ssrr", IP_FW_IPOPT_SSRR},
+ { "lsrr", IP_FW_IPOPT_LSRR},
+ { "rr", IP_FW_IPOPT_RR},
+ { "ts", IP_FW_IPOPT_TS},
+ { "ip option", 0 },
+ { NULL, 0 }
+};
+
+static struct _s_x f_iptos[] = {
+ { "lowdelay", IPTOS_LOWDELAY},
+ { "throughput", IPTOS_THROUGHPUT},
+ { "reliability", IPTOS_RELIABILITY},
+ { "mincost", IPTOS_MINCOST},
+ { "congestion", IPTOS_CE},
+ { "ecntransport", IPTOS_ECT},
+ { "ip tos option", 0},
+ { NULL, 0 }
+};
+
+static struct _s_x limit_masks[] = {
+ {"all", DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
+ {"src-addr", DYN_SRC_ADDR},
+ {"src-port", DYN_SRC_PORT},
+ {"dst-addr", DYN_DST_ADDR},
+ {"dst-port", DYN_DST_PORT},
+ {NULL, 0}
+};
+
+/*
+ * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines
+ * This is only used in this code.
+ */
+#define IPPROTO_ETHERTYPE 0x1000
+static struct _s_x ether_types[] = {
+ /*
+ * Note, we cannot use "-:&/" in the names because they are field
+ * separators in the type specifications. Also, we use s = NULL as
+ * end-delimiter, because a type of 0 can be legal.
+ */
+ { "ip", 0x0800 },
+ { "ipv4", 0x0800 },
+ { "ipv6", 0x86dd },
+ { "arp", 0x0806 },
+ { "rarp", 0x8035 },
+ { "vlan", 0x8100 },
+ { "loop", 0x9000 },
+ { "trail", 0x1000 },
+ { "at", 0x809b },
+ { "atalk", 0x809b },
+ { "aarp", 0x80f3 },
+ { "pppoe_disc", 0x8863 },
+ { "pppoe_sess", 0x8864 },
+ { "ipx_8022", 0x00E0 },
+ { "ipx_8023", 0x0000 },
+ { "ipx_ii", 0x8137 },
+ { "ipx_snap", 0x8137 },
+ { "ipx", 0x8137 },
+ { "ns", 0x0600 },
+ { NULL, 0 }
+};
+
+static void show_usage(void);
+
+enum tokens {
+ TOK_NULL=0,
+
+ TOK_OR,
+ TOK_NOT,
+ TOK_STARTBRACE,
+ TOK_ENDBRACE,
+
+ TOK_ACCEPT,
+ TOK_COUNT,
+ TOK_PIPE,
+ TOK_QUEUE,
+ TOK_DIVERT,
+ TOK_TEE,
+ TOK_NETGRAPH,
+ TOK_NGTEE,
+ TOK_FORWARD,
+ TOK_SKIPTO,
+ TOK_DENY,
+ TOK_REJECT,
+ TOK_RESET,
+ TOK_UNREACH,
+ TOK_CHECKSTATE,
+
+ TOK_ALTQ,
+ TOK_LOG,
+
+ TOK_UID,
+ TOK_GID,
+ TOK_JAIL,
+ TOK_IN,
+ TOK_LIMIT,
+ TOK_KEEPSTATE,
+ TOK_LAYER2,
+ TOK_OUT,
+ TOK_DIVERTED,
+ TOK_DIVERTEDLOOPBACK,
+ TOK_DIVERTEDOUTPUT,
+ TOK_XMIT,
+ TOK_RECV,
+ TOK_VIA,
+ TOK_FRAG,
+ TOK_IPOPTS,
+ TOK_IPLEN,
+ TOK_IPID,
+ TOK_IPPRECEDENCE,
+ TOK_IPTOS,
+ TOK_IPTTL,
+ TOK_IPVER,
+ TOK_ESTAB,
+ TOK_SETUP,
+ TOK_TCPDATALEN,
+ TOK_TCPFLAGS,
+ TOK_TCPOPTS,
+ TOK_TCPSEQ,
+ TOK_TCPACK,
+ TOK_TCPWIN,
+ TOK_ICMPTYPES,
+ TOK_MAC,
+ TOK_MACTYPE,
+ TOK_VERREVPATH,
+ TOK_VERSRCREACH,
+ TOK_ANTISPOOF,
+ TOK_IPSEC,
+ TOK_COMMENT,
+
+ TOK_PLR,
+ TOK_NOERROR,
+ TOK_BUCKETS,
+ TOK_DSTIP,
+ TOK_SRCIP,
+ TOK_DSTPORT,
+ TOK_SRCPORT,
+ TOK_ALL,
+ TOK_MASK,
+ TOK_BW,
+ TOK_DELAY,
+ TOK_RED,
+ TOK_GRED,
+ TOK_DROPTAIL,
+ TOK_PROTO,
+ TOK_WEIGHT,
+
+ TOK_IPV6,
+ TOK_FLOWID,
+ TOK_ICMP6TYPES,
+ TOK_EXT6HDR,
+ TOK_DSTIP6,
+ TOK_SRCIP6,
+
+ TOK_IPV4,
+ TOK_UNREACH6,
+ TOK_RESET6,
+};
+
+struct _s_x dummynet_params[] = {
+ { "plr", TOK_PLR },
+ { "noerror", TOK_NOERROR },
+ { "buckets", TOK_BUCKETS },
+ { "dst-ip", TOK_DSTIP },
+ { "src-ip", TOK_SRCIP },
+ { "dst-port", TOK_DSTPORT },
+ { "src-port", TOK_SRCPORT },
+ { "proto", TOK_PROTO },
+ { "weight", TOK_WEIGHT },
+ { "all", TOK_ALL },
+ { "mask", TOK_MASK },
+ { "droptail", TOK_DROPTAIL },
+ { "red", TOK_RED },
+ { "gred", TOK_GRED },
+ { "bw", TOK_BW },
+ { "bandwidth", TOK_BW },
+ { "delay", TOK_DELAY },
+ { "pipe", TOK_PIPE },
+ { "queue", TOK_QUEUE },
+ { "flow-id", TOK_FLOWID},
+ { "dst-ipv6", TOK_DSTIP6},
+ { "dst-ip6", TOK_DSTIP6},
+ { "src-ipv6", TOK_SRCIP6},
+ { "src-ip6", TOK_SRCIP6},
+ { "dummynet-params", TOK_NULL },
+ { NULL, 0 } /* terminator */
+};
+
+struct _s_x rule_actions[] = {
+ { "accept", TOK_ACCEPT },
+ { "pass", TOK_ACCEPT },
+ { "allow", TOK_ACCEPT },
+ { "permit", TOK_ACCEPT },
+ { "count", TOK_COUNT },
+ { "pipe", TOK_PIPE },
+ { "queue", TOK_QUEUE },
+ { "divert", TOK_DIVERT },
+ { "tee", TOK_TEE },
+ { "netgraph", TOK_NETGRAPH },
+ { "ngtee", TOK_NGTEE },
+ { "fwd", TOK_FORWARD },
+ { "forward", TOK_FORWARD },
+ { "skipto", TOK_SKIPTO },
+ { "deny", TOK_DENY },
+ { "drop", TOK_DENY },
+ { "reject", TOK_REJECT },
+ { "reset6", TOK_RESET6 },
+ { "reset", TOK_RESET },
+ { "unreach6", TOK_UNREACH6 },
+ { "unreach", TOK_UNREACH },
+ { "check-state", TOK_CHECKSTATE },
+ { "//", TOK_COMMENT },
+ { NULL, 0 } /* terminator */
+};
+
+struct _s_x rule_action_params[] = {
+ { "altq", TOK_ALTQ },
+ { "log", TOK_LOG },
+ { NULL, 0 } /* terminator */
+};
+
+struct _s_x rule_options[] = {
+ { "uid", TOK_UID },
+ { "gid", TOK_GID },
+ { "jail", TOK_JAIL },
+ { "in", TOK_IN },
+ { "limit", TOK_LIMIT },
+ { "keep-state", TOK_KEEPSTATE },
+ { "bridged", TOK_LAYER2 },
+ { "layer2", TOK_LAYER2 },
+ { "out", TOK_OUT },
+ { "diverted", TOK_DIVERTED },
+ { "diverted-loopback", TOK_DIVERTEDLOOPBACK },
+ { "diverted-output", TOK_DIVERTEDOUTPUT },
+ { "xmit", TOK_XMIT },
+ { "recv", TOK_RECV },
+ { "via", TOK_VIA },
+ { "fragment", TOK_FRAG },
+ { "frag", TOK_FRAG },
+ { "ipoptions", TOK_IPOPTS },
+ { "ipopts", TOK_IPOPTS },
+ { "iplen", TOK_IPLEN },
+ { "ipid", TOK_IPID },
+ { "ipprecedence", TOK_IPPRECEDENCE },
+ { "iptos", TOK_IPTOS },
+ { "ipttl", TOK_IPTTL },
+ { "ipversion", TOK_IPVER },
+ { "ipver", TOK_IPVER },
+ { "estab", TOK_ESTAB },
+ { "established", TOK_ESTAB },
+ { "setup", TOK_SETUP },
+ { "tcpdatalen", TOK_TCPDATALEN },
+ { "tcpflags", TOK_TCPFLAGS },
+ { "tcpflgs", TOK_TCPFLAGS },
+ { "tcpoptions", TOK_TCPOPTS },
+ { "tcpopts", TOK_TCPOPTS },
+ { "tcpseq", TOK_TCPSEQ },
+ { "tcpack", TOK_TCPACK },
+ { "tcpwin", TOK_TCPWIN },
+ { "icmptype", TOK_ICMPTYPES },
+ { "icmptypes", TOK_ICMPTYPES },
+ { "dst-ip", TOK_DSTIP },
+ { "src-ip", TOK_SRCIP },
+ { "dst-port", TOK_DSTPORT },
+ { "src-port", TOK_SRCPORT },
+ { "proto", TOK_PROTO },
+ { "MAC", TOK_MAC },
+ { "mac", TOK_MAC },
+ { "mac-type", TOK_MACTYPE },
+ { "verrevpath", TOK_VERREVPATH },
+ { "versrcreach", TOK_VERSRCREACH },
+ { "antispoof", TOK_ANTISPOOF },
+ { "ipsec", TOK_IPSEC },
+ { "icmp6type", TOK_ICMP6TYPES },
+ { "icmp6types", TOK_ICMP6TYPES },
+ { "ext6hdr", TOK_EXT6HDR},
+ { "flow-id", TOK_FLOWID},
+ { "ipv6", TOK_IPV6},
+ { "ip6", TOK_IPV6},
+ { "ipv4", TOK_IPV4},
+ { "ip4", TOK_IPV4},
+ { "dst-ipv6", TOK_DSTIP6},
+ { "dst-ip6", TOK_DSTIP6},
+ { "src-ipv6", TOK_SRCIP6},
+ { "src-ip6", TOK_SRCIP6},
+ { "//", TOK_COMMENT },
+
+ { "not", TOK_NOT }, /* pseudo option */
+ { "!", /* escape ? */ TOK_NOT }, /* pseudo option */
+ { "or", TOK_OR }, /* pseudo option */
+ { "|", /* escape */ TOK_OR }, /* pseudo option */
+ { "{", TOK_STARTBRACE }, /* pseudo option */
+ { "(", TOK_STARTBRACE }, /* pseudo option */
+ { "}", TOK_ENDBRACE }, /* pseudo option */
+ { ")", TOK_ENDBRACE }, /* pseudo option */
+ { NULL, 0 } /* terminator */
+};
+
+#define TABLEARG "tablearg"
+
+static __inline uint64_t
+align_uint64(uint64_t *pll) {
+ uint64_t ret;
+
+ bcopy (pll, &ret, sizeof(ret));
+ return ret;
+}
+
+/*
+ * conditionally runs the command.
+ */
+static int
+do_cmd(int optname, void *optval, uintptr_t optlen)
+{
+ static int s = -1; /* the socket */
+ int i;
+
+ if (test_only)
+ return 0;
+
+ if (s == -1)
+ s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+ if (s < 0)
+ err(EX_UNAVAILABLE, "socket");
+
+ if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
+ optname == IP_FW_ADD || optname == IP_FW_TABLE_LIST ||
+ optname == IP_FW_TABLE_GETSIZE)
+ i = getsockopt(s, IPPROTO_IP, optname, optval,
+ (socklen_t *)optlen);
+ else
+ i = setsockopt(s, IPPROTO_IP, optname, optval, optlen);
+ return i;
+}
+
+/**
+ * match_token takes a table and a string, returns the value associated
+ * with the string (-1 in case of failure).
+ */
+static int
+match_token(struct _s_x *table, char *string)
+{
+ struct _s_x *pt;
+ uint i = strlen(string);
+
+ for (pt = table ; i && pt->s != NULL ; pt++)
+ if (strlen(pt->s) == i && !bcmp(string, pt->s, i))
+ return pt->x;
+ return -1;
+}
+
+/**
+ * match_value takes a table and a value, returns the string associated
+ * with the value (NULL in case of failure).
+ */
+static char const *
+match_value(struct _s_x *p, int value)
+{
+ for (; p->s != NULL; p++)
+ if (p->x == value)
+ return p->s;
+ return NULL;
+}
+
+/*
+ * _substrcmp takes two strings and returns 1 if they do not match,
+ * and 0 if they match exactly or the first string is a sub-string
+ * of the second. A warning is printed to stderr in the case that the
+ * first string is a sub-string of the second.
+ *
+ * This function will be removed in the future through the usual
+ * deprecation process.
+ */
+static int
+_substrcmp(const char *str1, const char* str2)
+{
+
+ if (strncmp(str1, str2, strlen(str1)) != 0)
+ return 1;
+
+ if (strlen(str1) != strlen(str2))
+ warnx("DEPRECATED: '%s' matched '%s' as a sub-string",
+ str1, str2);
+ return 0;
+}
+
+/*
+ * _substrcmp2 takes three strings and returns 1 if the first two do not match,
+ * and 0 if they match exactly or the second string is a sub-string
+ * of the first. A warning is printed to stderr in the case that the
+ * first string does not match the third.
+ *
+ * This function exists to warn about the bizzare construction
+ * strncmp(str, "by", 2) which is used to allow people to use a shotcut
+ * for "bytes". The problem is that in addition to accepting "by",
+ * "byt", "byte", and "bytes", it also excepts "by_rabid_dogs" and any
+ * other string beginning with "by".
+ *
+ * This function will be removed in the future through the usual
+ * deprecation process.
+ */
+static int
+_substrcmp2(const char *str1, const char* str2, const char* str3)
+{
+
+ if (strncmp(str1, str2, strlen(str2)) != 0)
+ return 1;
+
+ if (strcmp(str1, str3) != 0)
+ warnx("DEPRECATED: '%s' matched '%s'",
+ str1, str3);
+ return 0;
+}
+
+/*
+ * prints one port, symbolic or numeric
+ */
+static void
+print_port(int proto, uint16_t port)
+{
+
+ if (proto == IPPROTO_ETHERTYPE) {
+ char const *s;
+
+ if (do_resolv && (s = match_value(ether_types, port)) )
+ printf("%s", s);
+ else
+ printf("0x%04x", port);
+ } else {
+ struct servent *se = NULL;
+ if (do_resolv) {
+ struct protoent *pe = getprotobynumber(proto);
+
+ se = getservbyport(htons(port), pe ? pe->p_name : NULL);
+ }
+ if (se)
+ printf("%s", se->s_name);
+ else
+ printf("%d", port);
+ }
+}
+
+struct _s_x _port_name[] = {
+ {"dst-port", O_IP_DSTPORT},
+ {"src-port", O_IP_SRCPORT},
+ {"ipid", O_IPID},
+ {"iplen", O_IPLEN},
+ {"ipttl", O_IPTTL},
+ {"mac-type", O_MAC_TYPE},
+ {"tcpdatalen", O_TCPDATALEN},
+ {NULL, 0}
+};
+
+/*
+ * Print the values in a list 16-bit items of the types above.
+ * XXX todo: add support for mask.
+ */
+static void
+print_newports(ipfw_insn_u16 *cmd, int proto, int opcode)
+{
+ uint16_t *p = cmd->ports;
+ int i;
+ char const *sep;
+
+ if (cmd->o.len & F_NOT)
+ printf(" not");
+ if (opcode != 0) {
+ sep = match_value(_port_name, opcode);
+ if (sep == NULL)
+ sep = "???";
+ printf (" %s", sep);
+ }
+ sep = " ";
+ for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
+ printf(sep);
+ print_port(proto, p[0]);
+ if (p[0] != p[1]) {
+ printf("-");
+ print_port(proto, p[1]);
+ }
+ sep = ",";
+ }
+}
+
+/*
+ * Like strtol, but also translates service names into port numbers
+ * for some protocols.
+ * In particular:
+ * proto == -1 disables the protocol check;
+ * proto == IPPROTO_ETHERTYPE looks up an internal table
+ * proto == <some value in /etc/protocols> matches the values there.
+ * Returns *end == s in case the parameter is not found.
+ */
+static int
+strtoport(char *s, char **end, int base, int proto)
+{
+ char *p, *buf;
+ char *s1;
+ int i;
+
+ *end = s; /* default - not found */
+ if (*s == '\0')
+ return 0; /* not found */
+
+ if (isdigit(*s))
+ return strtol(s, end, base);
+
+ /*
+ * find separator. '\\' escapes the next char.
+ */
+ for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++)
+ if (*s1 == '\\' && s1[1] != '\0')
+ s1++;
+
+ buf = malloc(s1 - s + 1);
+ if (buf == NULL)
+ return 0;
+
+ /*
+ * copy into a buffer skipping backslashes
+ */
+ for (p = s, i = 0; p != s1 ; p++)
+ if (*p != '\\')
+ buf[i++] = *p;
+ buf[i++] = '\0';
+
+ if (proto == IPPROTO_ETHERTYPE) {
+ i = match_token(ether_types, buf);
+ free(buf);
+ if (i != -1) { /* found */
+ *end = s1;
+ return i;
+ }
+ } else {
+ struct protoent *pe = NULL;
+ struct servent *se;
+
+ if (proto != 0)
+ pe = getprotobynumber(proto);
+ setservent(1);
+ se = getservbyname(buf, pe ? pe->p_name : NULL);
+ free(buf);
+ if (se != NULL) {
+ *end = s1;
+ return ntohs(se->s_port);
+ }
+ }
+ return 0; /* not found */
+}
+
+/*
+ * Map between current altq queue id numbers and names.
+ */
+static int altq_fetched = 0;
+static TAILQ_HEAD(, pf_altq) altq_entries =
+ TAILQ_HEAD_INITIALIZER(altq_entries);
+
+static void
+altq_set_enabled(int enabled)
+{
+ int pffd;
+
+ pffd = open("/dev/pf", O_RDWR);
+ if (pffd == -1)
+ err(EX_UNAVAILABLE,
+ "altq support opening pf(4) control device");
+ if (enabled) {
+ if (ioctl(pffd, DIOCSTARTALTQ) != 0 && errno != EEXIST)
+ err(EX_UNAVAILABLE, "enabling altq");
+ } else {
+ if (ioctl(pffd, DIOCSTOPALTQ) != 0 && errno != ENOENT)
+ err(EX_UNAVAILABLE, "disabling altq");
+ }
+ close(pffd);
+}
+
+static void
+altq_fetch()
+{
+ struct pfioc_altq pfioc;
+ struct pf_altq *altq;
+ int pffd, mnr;
+
+ if (altq_fetched)
+ return;
+ altq_fetched = 1;
+ pffd = open("/dev/pf", O_RDONLY);
+ if (pffd == -1) {
+ warn("altq support opening pf(4) control device");
+ return;
+ }
+ bzero(&pfioc, sizeof(pfioc));
+ if (ioctl(pffd, DIOCGETALTQS, &pfioc) != 0) {
+ warn("altq support getting queue list");
+ close(pffd);
+ return;
+ }
+ mnr = pfioc.nr;
+ for (pfioc.nr = 0; pfioc.nr < mnr; pfioc.nr++) {
+ if (ioctl(pffd, DIOCGETALTQ, &pfioc) != 0) {
+ if (errno == EBUSY)
+ break;
+ warn("altq support getting queue list");
+ close(pffd);
+ return;
+ }
+ if (pfioc.altq.qid == 0)
+ continue;
+ altq = malloc(sizeof(*altq));
+ if (altq == NULL)
+ err(EX_OSERR, "malloc");
+ *altq = pfioc.altq;
+ TAILQ_INSERT_TAIL(&altq_entries, altq, entries);
+ }
+ close(pffd);
+}
+
+static u_int32_t
+altq_name_to_qid(const char *name)
+{
+ struct pf_altq *altq;
+
+ altq_fetch();
+ TAILQ_FOREACH(altq, &altq_entries, entries)
+ if (strcmp(name, altq->qname) == 0)
+ break;
+ if (altq == NULL)
+ errx(EX_DATAERR, "altq has no queue named `%s'", name);
+ return altq->qid;
+}
+
+static const char *
+altq_qid_to_name(u_int32_t qid)
+{
+ struct pf_altq *altq;
+
+ altq_fetch();
+ TAILQ_FOREACH(altq, &altq_entries, entries)
+ if (qid == altq->qid)
+ break;
+ if (altq == NULL)
+ return NULL;
+ return altq->qname;
+}
+
+static void
+fill_altq_qid(u_int32_t *qid, const char *av)
+{
+ *qid = altq_name_to_qid(av);
+}
+
+/*
+ * Fill the body of the command with the list of port ranges.
+ */
+static int
+fill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
+{
+ uint16_t a, b, *p = cmd->ports;
+ int i = 0;
+ char *s = av;
+
+ while (*s) {
+ a = strtoport(av, &s, 0, proto);
+ if (s == av) /* no parameter */
+ break;
+ if (*s == '-') { /* a range */
+ av = s+1;
+ b = strtoport(av, &s, 0, proto);
+ if (s == av) /* no parameter */
+ break;
+ p[0] = a;
+ p[1] = b;
+ } else if (*s == ',' || *s == '\0' )
+ p[0] = p[1] = a;
+ else /* invalid separator */
+ errx(EX_DATAERR, "invalid separator <%c> in <%s>\n",
+ *s, av);
+ i++;
+ p += 2;
+ av = s+1;
+ }
+ if (i > 0) {
+ if (i+1 > F_LEN_MASK)
+ errx(EX_DATAERR, "too many ports/ranges\n");
+ cmd->o.len |= i+1; /* leave F_NOT and F_OR untouched */
+ }
+ return i;
+}
+
+static struct _s_x icmpcodes[] = {
+ { "net", ICMP_UNREACH_NET },
+ { "host", ICMP_UNREACH_HOST },
+ { "protocol", ICMP_UNREACH_PROTOCOL },
+ { "port", ICMP_UNREACH_PORT },
+ { "needfrag", ICMP_UNREACH_NEEDFRAG },
+ { "srcfail", ICMP_UNREACH_SRCFAIL },
+ { "net-unknown", ICMP_UNREACH_NET_UNKNOWN },
+ { "host-unknown", ICMP_UNREACH_HOST_UNKNOWN },
+ { "isolated", ICMP_UNREACH_ISOLATED },
+ { "net-prohib", ICMP_UNREACH_NET_PROHIB },
+ { "host-prohib", ICMP_UNREACH_HOST_PROHIB },
+ { "tosnet", ICMP_UNREACH_TOSNET },
+ { "toshost", ICMP_UNREACH_TOSHOST },
+ { "filter-prohib", ICMP_UNREACH_FILTER_PROHIB },
+ { "host-precedence", ICMP_UNREACH_HOST_PRECEDENCE },
+ { "precedence-cutoff", ICMP_UNREACH_PRECEDENCE_CUTOFF },
+ { NULL, 0 }
+};
+
+static void
+fill_reject_code(u_short *codep, char *str)
+{
+ int val;
+ char *s;
+
+ val = strtoul(str, &s, 0);
+ if (s == str || *s != '\0' || val >= 0x100)
+ val = match_token(icmpcodes, str);
+ if (val < 0)
+ errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
+ *codep = val;
+ return;
+}
+
+static void
+print_reject_code(uint16_t code)
+{
+ char const *s = match_value(icmpcodes, code);
+
+ if (s != NULL)
+ printf("unreach %s", s);
+ else
+ printf("unreach %u", code);
+}
+
+static struct _s_x icmp6codes[] = {
+ { "no-route", ICMP6_DST_UNREACH_NOROUTE },
+ { "admin-prohib", ICMP6_DST_UNREACH_ADMIN },
+ { "address", ICMP6_DST_UNREACH_ADDR },
+ { "port", ICMP6_DST_UNREACH_NOPORT },
+ { NULL, 0 }
+};
+
+static void
+fill_unreach6_code(u_short *codep, char *str)
+{
+ int val;
+ char *s;
+
+ val = strtoul(str, &s, 0);
+ if (s == str || *s != '\0' || val >= 0x100)
+ val = match_token(icmp6codes, str);
+ if (val < 0)
+ errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str);
+ *codep = val;
+ return;
+}
+
+static void
+print_unreach6_code(uint16_t code)
+{
+ char const *s = match_value(icmp6codes, code);
+
+ if (s != NULL)
+ printf("unreach6 %s", s);
+ else
+ printf("unreach6 %u", code);
+}
+
+/*
+ * Returns the number of bits set (from left) in a contiguous bitmask,
+ * or -1 if the mask is not contiguous.
+ * XXX this needs a proper fix.
+ * This effectively works on masks in big-endian (network) format.
+ * when compiled on little endian architectures.
+ *
+ * First bit is bit 7 of the first byte -- note, for MAC addresses,
+ * the first bit on the wire is bit 0 of the first byte.
+ * len is the max length in bits.
+ */
+static int
+contigmask(uint8_t *p, int len)
+{
+ int i, n;
+
+ for (i=0; i<len ; i++)
+ if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
+ break;
+ for (n=i+1; n < len; n++)
+ if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
+ return -1; /* mask not contiguous */
+ return i;
+}
+
+/*
+ * print flags set/clear in the two bitmasks passed as parameters.
+ * There is a specialized check for f_tcpflags.
+ */
+static void
+print_flags(char const *name, ipfw_insn *cmd, struct _s_x *list)
+{
+ char const *comma = "";
+ int i;
+ uint8_t set = cmd->arg1 & 0xff;
+ uint8_t clear = (cmd->arg1 >> 8) & 0xff;
+
+ if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) {
+ printf(" setup");
+ return;
+ }
+
+ printf(" %s ", name);
+ for (i=0; list[i].x != 0; i++) {
+ if (set & list[i].x) {
+ set &= ~list[i].x;
+ printf("%s%s", comma, list[i].s);
+ comma = ",";
+ }
+ if (clear & list[i].x) {
+ clear &= ~list[i].x;
+ printf("%s!%s", comma, list[i].s);
+ comma = ",";
+ }
+ }
+}
+
+/*
+ * Print the ip address contained in a command.
+ */
+static void
+print_ip(ipfw_insn_ip *cmd, char const *s)
+{
+ struct hostent *he = NULL;
+ int len = F_LEN((ipfw_insn *)cmd);
+ uint32_t *a = ((ipfw_insn_u32 *)cmd)->d;
+
+ printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
+
+ if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) {
+ printf("me");
+ return;
+ }
+ if (cmd->o.opcode == O_IP_SRC_LOOKUP ||
+ cmd->o.opcode == O_IP_DST_LOOKUP) {
+ printf("table(%u", ((ipfw_insn *)cmd)->arg1);
+ if (len == F_INSN_SIZE(ipfw_insn_u32))
+ printf(",%u", *a);
+ printf(")");
+ return;
+ }
+ if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) {
+ uint32_t x, *map = (uint32_t *)&(cmd->mask);
+ int i, j;
+ char comma = '{';
+
+ x = cmd->o.arg1 - 1;
+ x = htonl( ~x );
+ cmd->addr.s_addr = htonl(cmd->addr.s_addr);
+ printf("%s/%d", inet_ntoa(cmd->addr),
+ contigmask((uint8_t *)&x, 32));
+ x = cmd->addr.s_addr = htonl(cmd->addr.s_addr);
+ x &= 0xff; /* base */
+ /*
+ * Print bits and ranges.
+ * Locate first bit set (i), then locate first bit unset (j).
+ * If we have 3+ consecutive bits set, then print them as a
+ * range, otherwise only print the initial bit and rescan.
+ */
+ for (i=0; i < cmd->o.arg1; i++)
+ if (map[i/32] & (1<<(i & 31))) {
+ for (j=i+1; j < cmd->o.arg1; j++)
+ if (!(map[ j/32] & (1<<(j & 31))))
+ break;
+ printf("%c%d", comma, i+x);
+ if (j>i+2) { /* range has at least 3 elements */
+ printf("-%d", j-1+x);
+ i = j-1;
+ }
+ comma = ',';
+ }
+ printf("}");
+ return;
+ }
+ /*
+ * len == 2 indicates a single IP, whereas lists of 1 or more
+ * addr/mask pairs have len = (2n+1). We convert len to n so we
+ * use that to count the number of entries.
+ */
+ for (len = len / 2; len > 0; len--, a += 2) {
+ int mb = /* mask length */
+ (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ?
+ 32 : contigmask((uint8_t *)&(a[1]), 32);
+ if (mb == 32 && do_resolv)
+ he = gethostbyaddr((char *)&(a[0]), sizeof(u_long), AF_INET);
+ if (he != NULL) /* resolved to name */
+ printf("%s", he->h_name);
+ else if (mb == 0) /* any */
+ printf("any");
+ else { /* numeric IP followed by some kind of mask */
+ printf("%s", inet_ntoa( *((struct in_addr *)&a[0]) ) );
+ if (mb < 0)
+ printf(":%s", inet_ntoa( *((struct in_addr *)&a[1]) ) );
+ else if (mb < 32)
+ printf("/%d", mb);
+ }
+ if (len > 1)
+ printf(",");
+ }
+}
+
+/*
+ * prints a MAC address/mask pair
+ */
+static void
+print_mac(uint8_t *addr, uint8_t *mask)
+{
+ int l = contigmask(mask, 48);
+
+ if (l == 0)
+ printf(" any");
+ else {
+ printf(" %02x:%02x:%02x:%02x:%02x:%02x",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ if (l == -1)
+ printf("&%02x:%02x:%02x:%02x:%02x:%02x",
+ mask[0], mask[1], mask[2],
+ mask[3], mask[4], mask[5]);
+ else if (l < 48)
+ printf("/%d", l);
+ }
+}
+
+static void
+fill_icmptypes(ipfw_insn_u32 *cmd, char *av)
+{
+ uint8_t type;
+
+ cmd->d[0] = 0;
+ while (*av) {
+ if (*av == ',')
+ av++;
+
+ type = strtoul(av, &av, 0);
+
+ if (*av != ',' && *av != '\0')
+ errx(EX_DATAERR, "invalid ICMP type");
+
+ if (type > 31)
+ errx(EX_DATAERR, "ICMP type out of range");
+
+ cmd->d[0] |= 1 << type;
+ }
+ cmd->o.opcode = O_ICMPTYPE;
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
+}
+
+static void
+print_icmptypes(ipfw_insn_u32 *cmd)
+{
+ int i;
+ char sep= ' ';
+
+ printf(" icmptypes");
+ for (i = 0; i < 32; i++) {
+ if ( (cmd->d[0] & (1 << (i))) == 0)
+ continue;
+ printf("%c%d", sep, i);
+ sep = ',';
+ }
+}
+
+/*
+ * Print the ip address contained in a command.
+ */
+static void
+print_ip6(ipfw_insn_ip6 *cmd, char const *s)
+{
+ struct hostent *he = NULL;
+ int len = F_LEN((ipfw_insn *) cmd) - 1;
+ struct in6_addr *a = &(cmd->addr6);
+ char trad[255];
+
+ printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
+
+ if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) {
+ printf("me6");
+ return;
+ }
+ if (cmd->o.opcode == O_IP6) {
+ printf(" ip6");
+ return;
+ }
+
+ /*
+ * len == 4 indicates a single IP, whereas lists of 1 or more
+ * addr/mask pairs have len = (2n+1). We convert len to n so we
+ * use that to count the number of entries.
+ */
+
+ for (len = len / 4; len > 0; len -= 2, a += 2) {
+ int mb = /* mask length */
+ (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ?
+ 128 : contigmask((uint8_t *)&(a[1]), 128);
+
+ if (mb == 128 && do_resolv)
+ he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6);
+ if (he != NULL) /* resolved to name */
+ printf("%s", he->h_name);
+ else if (mb == 0) /* any */
+ printf("any");
+ else { /* numeric IP followed by some kind of mask */
+ if (inet_ntop(AF_INET6, a, trad, sizeof( trad ) ) == NULL)
+ printf("Error ntop in print_ip6\n");
+ printf("%s", trad );
+ if (mb < 0) /* XXX not really legal... */
+ printf(":%s",
+ inet_ntop(AF_INET6, &a[1], trad, sizeof(trad)));
+ else if (mb < 128)
+ printf("/%d", mb);
+ }
+ if (len > 2)
+ printf(",");
+ }
+}
+
+static void
+fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av)
+{
+ uint8_t type;
+
+ cmd->d[0] = 0;
+ while (*av) {
+ if (*av == ',')
+ av++;
+ type = strtoul(av, &av, 0);
+ if (*av != ',' && *av != '\0')
+ errx(EX_DATAERR, "invalid ICMP6 type");
+ /*
+ * XXX: shouldn't this be 0xFF? I can't see any reason why
+ * we shouldn't be able to filter all possiable values
+ * regardless of the ability of the rest of the kernel to do
+ * anything useful with them.
+ */
+ if (type > ICMP6_MAXTYPE)
+ errx(EX_DATAERR, "ICMP6 type out of range");
+ cmd->d[type / 32] |= ( 1 << (type % 32));
+ }
+ cmd->o.opcode = O_ICMP6TYPE;
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6);
+}
+
+
+static void
+print_icmp6types(ipfw_insn_u32 *cmd)
+{
+ int i, j;
+ char sep= ' ';
+
+ printf(" ip6 icmp6types");
+ for (i = 0; i < 7; i++)
+ for (j=0; j < 32; ++j) {
+ if ( (cmd->d[i] & (1 << (j))) == 0)
+ continue;
+ printf("%c%d", sep, (i*32 + j));
+ sep = ',';
+ }
+}
+
+static void
+print_flow6id( ipfw_insn_u32 *cmd)
+{
+ uint16_t i, limit = cmd->o.arg1;
+ char sep = ',';
+
+ printf(" flow-id ");
+ for( i=0; i < limit; ++i) {
+ if (i == limit - 1)
+ sep = ' ';
+ printf("%d%c", cmd->d[i], sep);
+ }
+}
+
+/* structure and define for the extension header in ipv6 */
+static struct _s_x ext6hdrcodes[] = {
+ { "frag", EXT_FRAGMENT },
+ { "hopopt", EXT_HOPOPTS },
+ { "route", EXT_ROUTING },
+ { "dstopt", EXT_DSTOPTS },
+ { "ah", EXT_AH },
+ { "esp", EXT_ESP },
+ { NULL, 0 }
+};
+
+/* fills command for the extension header filtering */
+int
+fill_ext6hdr( ipfw_insn *cmd, char *av)
+{
+ int tok;
+ char *s = av;
+
+ cmd->arg1 = 0;
+
+ while(s) {
+ av = strsep( &s, ",") ;
+ tok = match_token(ext6hdrcodes, av);
+ switch (tok) {
+ case EXT_FRAGMENT:
+ cmd->arg1 |= EXT_FRAGMENT;
+ break;
+
+ case EXT_HOPOPTS:
+ cmd->arg1 |= EXT_HOPOPTS;
+ break;
+
+ case EXT_ROUTING:
+ cmd->arg1 |= EXT_ROUTING;
+ break;
+
+ case EXT_DSTOPTS:
+ cmd->arg1 |= EXT_DSTOPTS;
+ break;
+
+ case EXT_AH:
+ cmd->arg1 |= EXT_AH;
+ break;
+
+ case EXT_ESP:
+ cmd->arg1 |= EXT_ESP;
+ break;
+
+ default:
+ errx( EX_DATAERR, "invalid option for ipv6 exten header" );
+ break;
+ }
+ }
+ if (cmd->arg1 == 0 )
+ return 0;
+ cmd->opcode = O_EXT_HDR;
+ cmd->len |= F_INSN_SIZE( ipfw_insn );
+ return 1;
+}
+
+void
+print_ext6hdr( ipfw_insn *cmd )
+{
+ char sep = ' ';
+
+ printf(" extension header:");
+ if (cmd->arg1 & EXT_FRAGMENT ) {
+ printf("%cfragmentation", sep);
+ sep = ',';
+ }
+ if (cmd->arg1 & EXT_HOPOPTS ) {
+ printf("%chop options", sep);
+ sep = ',';
+ }
+ if (cmd->arg1 & EXT_ROUTING ) {
+ printf("%crouting options", sep);
+ sep = ',';
+ }
+ if (cmd->arg1 & EXT_DSTOPTS ) {
+ printf("%cdestination options", sep);
+ sep = ',';
+ }
+ if (cmd->arg1 & EXT_AH ) {
+ printf("%cauthentication header", sep);
+ sep = ',';
+ }
+ if (cmd->arg1 & EXT_ESP ) {
+ printf("%cencapsulated security payload", sep);
+ }
+}
+
+/*
+ * show_ipfw() prints the body of an ipfw rule.
+ * Because the standard rule has at least proto src_ip dst_ip, we use
+ * a helper function to produce these entries if not provided explicitly.
+ * The first argument is the list of fields we have, the second is
+ * the list of fields we want to be printed.
+ *
+ * Special cases if we have provided a MAC header:
+ * + if the rule does not contain IP addresses/ports, do not print them;
+ * + if the rule does not contain an IP proto, print "all" instead of "ip";
+ *
+ * Once we have 'have_options', IP header fields are printed as options.
+ */
+#define HAVE_PROTO 0x0001
+#define HAVE_SRCIP 0x0002
+#define HAVE_DSTIP 0x0004
+#define HAVE_MAC 0x0008
+#define HAVE_MACTYPE 0x0010
+#define HAVE_PROTO4 0x0040
+#define HAVE_PROTO6 0x0080
+#define HAVE_OPTIONS 0x8000
+
+#define HAVE_IP (HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP)
+static void
+show_prerequisites(int *flags, int want, int cmd)
+{
+ if (comment_only)
+ return;
+ if ( (*flags & HAVE_IP) == HAVE_IP)
+ *flags |= HAVE_OPTIONS;
+
+ if ( (*flags & (HAVE_MAC|HAVE_MACTYPE|HAVE_OPTIONS)) == HAVE_MAC &&
+ cmd != O_MAC_TYPE) {
+ /*
+ * mac-type was optimized out by the compiler,
+ * restore it
+ */
+ printf(" any");
+ *flags |= HAVE_MACTYPE | HAVE_OPTIONS;
+ return;
+ }
+ if ( !(*flags & HAVE_OPTIONS)) {
+ if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO))
+ if ( (*flags & HAVE_PROTO4))
+ printf(" ip4");
+ else if ( (*flags & HAVE_PROTO6))
+ printf(" ip6");
+ else
+ printf(" ip");
+
+ if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
+ printf(" from any");
+ if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP))
+ printf(" to any");
+ }
+ *flags |= want;
+}
+
+static void
+show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
+{
+ static int twidth = 0;
+ int l;
+ ipfw_insn *cmd;
+ char *comment = NULL; /* ptr to comment if we have one */
+ int proto = 0; /* default */
+ int flags = 0; /* prerequisites */
+ ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */
+ ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */
+ int or_block = 0; /* we are in an or block */
+ uint32_t set_disable;
+
+ bcopy(&rule->next_rule, &set_disable, sizeof(set_disable));
+
+ if (set_disable & (1 << rule->set)) { /* disabled */
+ if (!show_sets)
+ return;
+ else
+ printf("# DISABLED ");
+ }
+ printf("%05u ", rule->rulenum);
+
+ if (pcwidth>0 || bcwidth>0)
+ printf("%*llu %*llu ", pcwidth, align_uint64(&rule->pcnt),
+ bcwidth, align_uint64(&rule->bcnt));
+
+ if (do_time == 2)
+ printf("%10u ", rule->timestamp);
+ else if (do_time == 1) {
+ char timestr[30];
+ time_t t = (time_t)0;
+
+ if (twidth == 0) {
+ strcpy(timestr, ctime(&t));
+ *strchr(timestr, '\n') = '\0';
+ twidth = strlen(timestr);
+ }
+ if (rule->timestamp) {
+ t = _long_to_time(rule->timestamp);
+
+ strcpy(timestr, ctime(&t));
+ *strchr(timestr, '\n') = '\0';
+ printf("%s ", timestr);
+ } else {
+ printf("%*s", twidth, " ");
+ }
+ }
+
+ if (show_sets)
+ printf("set %d ", rule->set);
+
+ /*
+ * print the optional "match probability"
+ */
+ if (rule->cmd_len > 0) {
+ cmd = rule->cmd ;
+ if (cmd->opcode == O_PROB) {
+ ipfw_insn_u32 *p = (ipfw_insn_u32 *)cmd;
+ double d = 1.0 * p->d[0];
+
+ d = (d / 0x7fffffff);
+ printf("prob %f ", d);
+ }
+ }
+
+ /*
+ * first print actions
+ */
+ for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule);
+ l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
+ switch(cmd->opcode) {
+ case O_CHECK_STATE:
+ printf("check-state");
+ flags = HAVE_IP; /* avoid printing anything else */
+ break;
+
+ case O_ACCEPT:
+ printf("allow");
+ break;
+
+ case O_COUNT:
+ printf("count");
+ break;
+
+ case O_DENY:
+ printf("deny");
+ break;
+
+ case O_REJECT:
+ if (cmd->arg1 == ICMP_REJECT_RST)
+ printf("reset");
+ else if (cmd->arg1 == ICMP_UNREACH_HOST)
+ printf("reject");
+ else
+ print_reject_code(cmd->arg1);
+ break;
+
+ case O_UNREACH6:
+ if (cmd->arg1 == ICMP6_UNREACH_RST)
+ printf("reset6");
+ else
+ print_unreach6_code(cmd->arg1);
+ break;
+
+#define PRINT_WITH_ARG(o) \
+ if (cmd->arg1 == IP_FW_TABLEARG) \
+ printf("%s tablearg", (o)); \
+ else \
+ printf("%s %u", (o), cmd->arg1); \
+ break;
+
+ case O_SKIPTO:
+ PRINT_WITH_ARG("skipto");
+ case O_PIPE:
+ PRINT_WITH_ARG("pipe");
+ case O_QUEUE:
+ PRINT_WITH_ARG("queue");
+ case O_DIVERT:
+ PRINT_WITH_ARG("divert");
+ case O_TEE:
+ PRINT_WITH_ARG("tee");
+ case O_NETGRAPH:
+ PRINT_WITH_ARG("netgraph");
+ case O_NGTEE:
+ PRINT_WITH_ARG("ngtee");
+#undef PRINT_WITH_ARG
+
+ case O_FORWARD_IP:
+ {
+ ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
+
+ printf("fwd %s", inet_ntoa(s->sa.sin_addr));
+ if (s->sa.sin_port)
+ printf(",%d", s->sa.sin_port);
+ }
+ break;
+
+ case O_LOG: /* O_LOG is printed last */
+ logptr = (ipfw_insn_log *)cmd;
+ break;
+
+ case O_ALTQ: /* O_ALTQ is printed after O_LOG */
+ altqptr = (ipfw_insn_altq *)cmd;
+ break;
+
+ default:
+ printf("** unrecognized action %d len %d ",
+ cmd->opcode, cmd->len);
+ }
+ }
+ if (logptr) {
+ if (logptr->max_log > 0)
+ printf(" log logamount %d", logptr->max_log);
+ else
+ printf(" log");
+ }
+ if (altqptr) {
+ const char *qname;
+
+ qname = altq_qid_to_name(altqptr->qid);
+ if (qname == NULL)
+ printf(" altq ?<%u>", altqptr->qid);
+ else
+ printf(" altq %s", qname);
+ }
+
+ /*
+ * then print the body.
+ */
+ for (l = rule->act_ofs, cmd = rule->cmd ;
+ l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
+ if ((cmd->len & F_OR) || (cmd->len & F_NOT))
+ continue;
+ if (cmd->opcode == O_IP4) {
+ flags |= HAVE_PROTO4;
+ break;
+ } else if (cmd->opcode == O_IP6) {
+ flags |= HAVE_PROTO6;
+ break;
+ }
+ }
+ if (rule->_pad & 1) { /* empty rules before options */
+ if (!do_compact) {
+ show_prerequisites(&flags, HAVE_PROTO, 0);
+ printf(" from any to any");
+ }
+ flags |= HAVE_IP | HAVE_OPTIONS;
+ }
+
+ if (comment_only)
+ comment = "...";
+
+ for (l = rule->act_ofs, cmd = rule->cmd ;
+ l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
+ /* useful alias */
+ ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
+
+ if (comment_only) {
+ if (cmd->opcode != O_NOP)
+ continue;
+ printf(" // %s\n", (char *)(cmd + 1));
+ return;
+ }
+
+ show_prerequisites(&flags, 0, cmd->opcode);
+
+ switch(cmd->opcode) {
+ case O_PROB:
+ break; /* done already */
+
+ case O_PROBE_STATE:
+ break; /* no need to print anything here */
+
+ case O_MACADDR2: {
+ ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
+
+ if ((cmd->len & F_OR) && !or_block)
+ printf(" {");
+ if (cmd->len & F_NOT)
+ printf(" not");
+ printf(" MAC");
+ flags |= HAVE_MAC;
+ print_mac(m->addr, m->mask);
+ print_mac(m->addr + 6, m->mask + 6);
+ }
+ break;
+
+ case O_MAC_TYPE:
+ if ((cmd->len & F_OR) && !or_block)
+ printf(" {");
+ print_newports((ipfw_insn_u16 *)cmd, IPPROTO_ETHERTYPE,
+ (flags & HAVE_OPTIONS) ? cmd->opcode : 0);
+ flags |= HAVE_MAC | HAVE_MACTYPE | HAVE_OPTIONS;
+ break;
+
+ case O_IP_SRC:
+ case O_IP_SRC_LOOKUP:
+ case O_IP_SRC_MASK:
+ case O_IP_SRC_ME:
+ case O_IP_SRC_SET:
+ show_prerequisites(&flags, HAVE_PROTO, 0);
+ if (!(flags & HAVE_SRCIP))
+ printf(" from");
+ if ((cmd->len & F_OR) && !or_block)
+ printf(" {");
+ print_ip((ipfw_insn_ip *)cmd,
+ (flags & HAVE_OPTIONS) ? " src-ip" : "");
+ flags |= HAVE_SRCIP;
+ break;
+
+ case O_IP_DST:
+ case O_IP_DST_LOOKUP:
+ case O_IP_DST_MASK:
+ case O_IP_DST_ME:
+ case O_IP_DST_SET:
+ show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
+ if (!(flags & HAVE_DSTIP))
+ printf(" to");
+ if ((cmd->len & F_OR) && !or_block)
+ printf(" {");
+ print_ip((ipfw_insn_ip *)cmd,
+ (flags & HAVE_OPTIONS) ? " dst-ip" : "");
+ flags |= HAVE_DSTIP;
+ break;
+
+ case O_IP6_SRC:
+ case O_IP6_SRC_MASK:
+ case O_IP6_SRC_ME:
+ show_prerequisites(&flags, HAVE_PROTO, 0);
+ if (!(flags & HAVE_SRCIP))
+ printf(" from");
+ if ((cmd->len & F_OR) && !or_block)
+ printf(" {");
+ print_ip6((ipfw_insn_ip6 *)cmd,
+ (flags & HAVE_OPTIONS) ? " src-ip6" : "");
+ flags |= HAVE_SRCIP | HAVE_PROTO;
+ break;
+
+ case O_IP6_DST:
+ case O_IP6_DST_MASK:
+ case O_IP6_DST_ME:
+ show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
+ if (!(flags & HAVE_DSTIP))
+ printf(" to");
+ if ((cmd->len & F_OR) && !or_block)
+ printf(" {");
+ print_ip6((ipfw_insn_ip6 *)cmd,
+ (flags & HAVE_OPTIONS) ? " dst-ip6" : "");
+ flags |= HAVE_DSTIP;
+ break;
+
+ case O_FLOW6ID:
+ print_flow6id( (ipfw_insn_u32 *) cmd );
+ flags |= HAVE_OPTIONS;
+ break;
+
+ case O_IP_DSTPORT:
+ show_prerequisites(&flags, HAVE_IP, 0);
+ case O_IP_SRCPORT:
+ show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
+ if ((cmd->len & F_OR) && !or_block)
+ printf(" {");
+ print_newports((ipfw_insn_u16 *)cmd, proto,
+ (flags & HAVE_OPTIONS) ? cmd->opcode : 0);
+ break;
+
+ case O_PROTO: {
+ struct protoent *pe = NULL;
+
+ if ((cmd->len & F_OR) && !or_block)
+ printf(" {");
+ if (cmd->len & F_NOT)
+ printf(" not");
+ proto = cmd->arg1;
+ pe = getprotobynumber(cmd->arg1);
+ if ((flags & (HAVE_PROTO4 | HAVE_PROTO6)) &&
+ !(flags & HAVE_PROTO))
+ show_prerequisites(&flags,
+ HAVE_IP | HAVE_OPTIONS, 0);
+ if (flags & HAVE_OPTIONS)
+ printf(" proto");
+ if (pe)
+ printf(" %s", pe->p_name);
+ else
+ printf(" %u", cmd->arg1);
+ }
+ flags |= HAVE_PROTO;
+ break;
+
+ default: /*options ... */
+ if (!(cmd->len & (F_OR|F_NOT)))
+ if (((cmd->opcode == O_IP6) &&
+ (flags & HAVE_PROTO6)) ||
+ ((cmd->opcode == O_IP4) &&
+ (flags & HAVE_PROTO4)))
+ break;
+ show_prerequisites(&flags, HAVE_IP | HAVE_OPTIONS, 0);
+ if ((cmd->len & F_OR) && !or_block)
+ printf(" {");
+ if (cmd->len & F_NOT && cmd->opcode != O_IN)
+ printf(" not");
+ switch(cmd->opcode) {
+ case O_FRAG:
+ printf(" frag");
+ break;
+
+ case O_IN:
+ printf(cmd->len & F_NOT ? " out" : " in");
+ break;
+
+ case O_DIVERTED:
+ switch (cmd->arg1) {
+ case 3:
+ printf(" diverted");
+ break;
+ case 1:
+ printf(" diverted-loopback");
+ break;
+ case 2:
+ printf(" diverted-output");
+ break;
+ default:
+ printf(" diverted-?<%u>", cmd->arg1);
+ break;
+ }
+ break;
+
+ case O_LAYER2:
+ printf(" layer2");
+ break;
+ case O_XMIT:
+ case O_RECV:
+ case O_VIA:
+ {
+ char const *s;
+ ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd;
+
+ if (cmd->opcode == O_XMIT)
+ s = "xmit";
+ else if (cmd->opcode == O_RECV)
+ s = "recv";
+ else /* if (cmd->opcode == O_VIA) */
+ s = "via";
+ if (cmdif->name[0] == '\0')
+ printf(" %s %s", s,
+ inet_ntoa(cmdif->p.ip));
+ else
+ printf(" %s %s", s, cmdif->name);
+
+ break;
+ }
+ case O_IPID:
+ if (F_LEN(cmd) == 1)
+ printf(" ipid %u", cmd->arg1 );
+ else
+ print_newports((ipfw_insn_u16 *)cmd, 0,
+ O_IPID);
+ break;
+
+ case O_IPTTL:
+ if (F_LEN(cmd) == 1)
+ printf(" ipttl %u", cmd->arg1 );
+ else
+ print_newports((ipfw_insn_u16 *)cmd, 0,
+ O_IPTTL);
+ break;
+
+ case O_IPVER:
+ printf(" ipver %u", cmd->arg1 );
+ break;
+
+ case O_IPPRECEDENCE:
+ printf(" ipprecedence %u", (cmd->arg1) >> 5 );
+ break;
+
+ case O_IPLEN:
+ if (F_LEN(cmd) == 1)
+ printf(" iplen %u", cmd->arg1 );
+ else
+ print_newports((ipfw_insn_u16 *)cmd, 0,
+ O_IPLEN);
+ break;
+
+ case O_IPOPT:
+ print_flags("ipoptions", cmd, f_ipopts);
+ break;
+
+ case O_IPTOS:
+ print_flags("iptos", cmd, f_iptos);
+ break;
+
+ case O_ICMPTYPE:
+ print_icmptypes((ipfw_insn_u32 *)cmd);
+ break;
+
+ case O_ESTAB:
+ printf(" established");
+ break;
+
+ case O_TCPDATALEN:
+ if (F_LEN(cmd) == 1)
+ printf(" tcpdatalen %u", cmd->arg1 );
+ else
+ print_newports((ipfw_insn_u16 *)cmd, 0,
+ O_TCPDATALEN);
+ break;
+
+ case O_TCPFLAGS:
+ print_flags("tcpflags", cmd, f_tcpflags);
+ break;
+
+ case O_TCPOPTS:
+ print_flags("tcpoptions", cmd, f_tcpopts);
+ break;
+
+ case O_TCPWIN:
+ printf(" tcpwin %d", ntohs(cmd->arg1));
+ break;
+
+ case O_TCPACK:
+ printf(" tcpack %d", ntohl(cmd32->d[0]));
+ break;
+
+ case O_TCPSEQ:
+ printf(" tcpseq %d", ntohl(cmd32->d[0]));
+ break;
+
+ case O_UID:
+ {
+ struct passwd *pwd = getpwuid(cmd32->d[0]);
+
+ if (pwd)
+ printf(" uid %s", pwd->pw_name);
+ else
+ printf(" uid %u", cmd32->d[0]);
+ }
+ break;
+
+ case O_GID:
+ {
+ struct group *grp = getgrgid(cmd32->d[0]);
+
+ if (grp)
+ printf(" gid %s", grp->gr_name);
+ else
+ printf(" gid %u", cmd32->d[0]);
+ }
+ break;
+
+ case O_JAIL:
+ printf(" jail %d", cmd32->d[0]);
+ break;
+
+ case O_VERREVPATH:
+ printf(" verrevpath");
+ break;
+
+ case O_VERSRCREACH:
+ printf(" versrcreach");
+ break;
+
+ case O_ANTISPOOF:
+ printf(" antispoof");
+ break;
+
+ case O_IPSEC:
+ printf(" ipsec");
+ break;
+
+ case O_NOP:
+ comment = (char *)(cmd + 1);
+ break;
+
+ case O_KEEP_STATE:
+ printf(" keep-state");
+ break;
+
+ case O_LIMIT:
+ {
+ struct _s_x *p = limit_masks;
+ ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
+ uint8_t x = c->limit_mask;
+ char const *comma = " ";
+
+ printf(" limit");
+ for (; p->x != 0 ; p++)
+ if ((x & p->x) == p->x) {
+ x &= ~p->x;
+ printf("%s%s", comma, p->s);
+ comma = ",";
+ }
+ printf(" %d", c->conn_limit);
+ }
+ break;
+
+ case O_IP6:
+ printf(" ip6");
+ break;
+
+ case O_IP4:
+ printf(" ip4");
+ break;
+
+ case O_ICMP6TYPE:
+ print_icmp6types((ipfw_insn_u32 *)cmd);
+ break;
+
+ case O_EXT_HDR:
+ print_ext6hdr( (ipfw_insn *) cmd );
+ break;
+
+ default:
+ printf(" [opcode %d len %d]",
+ cmd->opcode, cmd->len);
+ }
+ }
+ if (cmd->len & F_OR) {
+ printf(" or");
+ or_block = 1;
+ } else if (or_block) {
+ printf(" }");
+ or_block = 0;
+ }
+ }
+ show_prerequisites(&flags, HAVE_IP, 0);
+ if (comment)
+ printf(" // %s", comment);
+ printf("\n");
+}
+
+static void
+show_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth)
+{
+ struct protoent *pe;
+ struct in_addr a;
+ uint16_t rulenum;
+
+ if (!do_expired) {
+ if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT))
+ return;
+ }
+ bcopy(&d->rule, &rulenum, sizeof(rulenum));
+ printf("%05d", rulenum);
+ if (pcwidth>0 || bcwidth>0)
+ printf(" %*llu %*llu (%ds)", pcwidth,
+ align_uint64(&d->pcnt), bcwidth,
+ align_uint64(&d->bcnt), d->expire);
+ switch (d->dyn_type) {
+ case O_LIMIT_PARENT:
+ printf(" PARENT %d", d->count);
+ break;
+ case O_LIMIT:
+ printf(" LIMIT");
+ break;
+ case O_KEEP_STATE: /* bidir, no mask */
+ printf(" STATE");
+ break;
+ }
+
+ if ((pe = getprotobynumber(d->id.proto)) != NULL)
+ printf(" %s", pe->p_name);
+ else
+ printf(" proto %u", d->id.proto);
+
+ a.s_addr = htonl(d->id.src_ip);
+ printf(" %s %d", inet_ntoa(a), d->id.src_port);
+
+ a.s_addr = htonl(d->id.dst_ip);
+ printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port);
+ printf("\n");
+}
+
+static int
+sort_q(const void *pa, const void *pb)
+{
+ int rev = (do_sort < 0);
+ int field = rev ? -do_sort : do_sort;
+ long long res = 0;
+ const struct dn_flow_queue *a = pa;
+ const struct dn_flow_queue *b = pb;
+
+ switch (field) {
+ case 1: /* pkts */
+ res = a->len - b->len;
+ break;
+ case 2: /* bytes */
+ res = a->len_bytes - b->len_bytes;
+ break;
+
+ case 3: /* tot pkts */
+ res = a->tot_pkts - b->tot_pkts;
+ break;
+
+ case 4: /* tot bytes */
+ res = a->tot_bytes - b->tot_bytes;
+ break;
+ }
+ if (res < 0)
+ res = -1;
+ if (res > 0)
+ res = 1;
+ return (int)(rev ? res : -res);
+}
+
+static void
+list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
+{
+ int l;
+ int index_printed, indexes = 0;
+ char buff[255];
+ struct protoent *pe;
+
+ if (fs->rq_elements == 0)
+ return;
+
+ if (do_sort != 0)
+ heapsort(q, fs->rq_elements, sizeof *q, sort_q);
+
+ /* Print IPv4 flows */
+ index_printed = 0;
+ for (l = 0; l < fs->rq_elements; l++) {
+ struct in_addr ina;
+
+ /* XXX: Should check for IPv4 flows */
+ if (IS_IP6_FLOW_ID(&(q[l].id)))
+ continue;
+
+ if (!index_printed) {
+ index_printed = 1;
+ if (indexes > 0) /* currently a no-op */
+ printf("\n");
+ indexes++;
+ printf(" "
+ "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
+ fs->flow_mask.proto,
+ fs->flow_mask.src_ip, fs->flow_mask.src_port,
+ fs->flow_mask.dst_ip, fs->flow_mask.dst_port);
+
+ printf("BKT Prot ___Source IP/port____ "
+ "____Dest. IP/port____ "
+ "Tot_pkt/bytes Pkt/Byte Drp\n");
+ }
+
+ printf("%3d ", q[l].hash_slot);
+ pe = getprotobynumber(q[l].id.proto);
+ if (pe)
+ printf("%-4s ", pe->p_name);
+ else
+ printf("%4u ", q[l].id.proto);
+ ina.s_addr = htonl(q[l].id.src_ip);
+ printf("%15s/%-5d ",
+ inet_ntoa(ina), q[l].id.src_port);
+ ina.s_addr = htonl(q[l].id.dst_ip);
+ printf("%15s/%-5d ",
+ inet_ntoa(ina), q[l].id.dst_port);
+ printf("%4qu %8qu %2u %4u %3u\n",
+ q[l].tot_pkts, q[l].tot_bytes,
+ q[l].len, q[l].len_bytes, q[l].drops);
+ if (verbose)
+ printf(" S %20qd F %20qd\n",
+ q[l].S, q[l].F);
+ }
+
+ /* Print IPv6 flows */
+ index_printed = 0;
+ for (l = 0; l < fs->rq_elements; l++) {
+ if (!IS_IP6_FLOW_ID(&(q[l].id)))
+ continue;
+
+ if (!index_printed) {
+ index_printed = 1;
+ if (indexes > 0)
+ printf("\n");
+ indexes++;
+ printf("\n mask: proto: 0x%02x, flow_id: 0x%08x, ",
+ fs->flow_mask.proto, fs->flow_mask.flow_id6);
+ inet_ntop(AF_INET6, &(fs->flow_mask.src_ip6),
+ buff, sizeof(buff));
+ printf("%s/0x%04x -> ", buff, fs->flow_mask.src_port);
+ inet_ntop( AF_INET6, &(fs->flow_mask.dst_ip6),
+ buff, sizeof(buff) );
+ printf("%s/0x%04x\n", buff, fs->flow_mask.dst_port);
+
+ printf("BKT ___Prot___ _flow-id_ "
+ "______________Source IPv6/port_______________ "
+ "_______________Dest. IPv6/port_______________ "
+ "Tot_pkt/bytes Pkt/Byte Drp\n");
+ }
+ printf("%3d ", q[l].hash_slot);
+ pe = getprotobynumber(q[l].id.proto);
+ if (pe != NULL)
+ printf("%9s ", pe->p_name);
+ else
+ printf("%9u ", q[l].id.proto);
+ printf("%7d %39s/%-5d ", q[l].id.flow_id6,
+ inet_ntop(AF_INET6, &(q[l].id.src_ip6), buff, sizeof(buff)),
+ q[l].id.src_port);
+ printf(" %39s/%-5d ",
+ inet_ntop(AF_INET6, &(q[l].id.dst_ip6), buff, sizeof(buff)),
+ q[l].id.dst_port);
+ printf(" %4qu %8qu %2u %4u %3u\n",
+ q[l].tot_pkts, q[l].tot_bytes,
+ q[l].len, q[l].len_bytes, q[l].drops);
+ if (verbose)
+ printf(" S %20qd F %20qd\n", q[l].S, q[l].F);
+ }
+}
+
+static void
+print_flowset_parms(struct dn_flow_set *fs, char *prefix)
+{
+ int l;
+ char qs[30];
+ char plr[30];
+ char red[90]; /* Display RED parameters */
+
+ l = fs->qsize;
+ if (fs->flags_fs & DN_QSIZE_IS_BYTES) {
+ if (l >= 8192)
+ sprintf(qs, "%d KB", l / 1024);
+ else
+ sprintf(qs, "%d B", l);
+ } else
+ sprintf(qs, "%3d sl.", l);
+ if (fs->plr)
+ sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
+ else
+ plr[0] = '\0';
+ if (fs->flags_fs & DN_IS_RED) /* RED parameters */
+ sprintf(red,
+ "\n\t %cRED w_q %f min_th %d max_th %d max_p %f",
+ (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ',
+ 1.0 * fs->w_q / (double)(1 << SCALE_RED),
+ SCALE_VAL(fs->min_th),
+ SCALE_VAL(fs->max_th),
+ 1.0 * fs->max_p / (double)(1 << SCALE_RED));
+ else
+ sprintf(red, "droptail");
+
+ printf("%s %s%s %d queues (%d buckets) %s\n",
+ prefix, qs, plr, fs->rq_elements, fs->rq_size, red);
+}
+
+static void
+list_pipes(void *data, uint nbytes, int ac, char *av[])
+{
+ int rulenum;
+ void *next = data;
+ struct dn_pipe *p = (struct dn_pipe *) data;
+ struct dn_flow_set *fs;
+ struct dn_flow_queue *q;
+ int l;
+
+ if (ac > 0)
+ rulenum = strtoul(*av++, NULL, 10);
+ else
+ rulenum = 0;
+ for (; nbytes >= sizeof *p; p = (struct dn_pipe *)next) {
+ double b = p->bandwidth;
+ char buf[30];
+ char prefix[80];
+
+ if (p->next.sle_next != (struct dn_pipe *)DN_IS_PIPE)
+ break; /* done with pipes, now queues */
+
+ /*
+ * compute length, as pipe have variable size
+ */
+ l = sizeof(*p) + p->fs.rq_elements * sizeof(*q);
+ next = (char *)p + l;
+ nbytes -= l;
+
+ if ((rulenum != 0 && rulenum != p->pipe_nr) || do_pipe == 2)
+ continue;
+
+ /*
+ * Print rate (or clocking interface)
+ */
+ if (p->if_name[0] != '\0')
+ sprintf(buf, "%s", p->if_name);
+ else if (b == 0)
+ sprintf(buf, "unlimited");
+ else if (b >= 1000000)
+ sprintf(buf, "%7.3f Mbit/s", b/1000000);
+ else if (b >= 1000)
+ sprintf(buf, "%7.3f Kbit/s", b/1000);
+ else
+ sprintf(buf, "%7.3f bit/s ", b);
+
+ sprintf(prefix, "%05d: %s %4d ms ",
+ p->pipe_nr, buf, p->delay);
+ print_flowset_parms(&(p->fs), prefix);
+ if (verbose)
+ printf(" V %20qd\n", p->V >> MY_M);
+
+ q = (struct dn_flow_queue *)(p+1);
+ list_queues(&(p->fs), q);
+ }
+ for (fs = next; nbytes >= sizeof *fs; fs = next) {
+ char prefix[80];
+
+ if (fs->next.sle_next != (struct dn_flow_set *)DN_IS_QUEUE)
+ break;
+ l = sizeof(*fs) + fs->rq_elements * sizeof(*q);
+ next = (char *)fs + l;
+ nbytes -= l;
+
+ if (rulenum != 0 && ((rulenum != fs->fs_nr && do_pipe == 2) ||
+ (rulenum != fs->parent_nr && do_pipe == 1))) {
+ continue;
+ }
+
+ q = (struct dn_flow_queue *)(fs+1);
+ sprintf(prefix, "q%05d: weight %d pipe %d ",
+ fs->fs_nr, fs->weight, fs->parent_nr);
+ print_flowset_parms(fs, prefix);
+ list_queues(fs, q);
+ }
+}
+
+/*
+ * This one handles all set-related commands
+ * ipfw set { show | enable | disable }
+ * ipfw set swap X Y
+ * ipfw set move X to Y
+ * ipfw set move rule X to Y
+ */
+static void
+sets_handler(int ac, char *av[])
+{
+ uint32_t set_disable, masks[2];
+ int i, nbytes;
+ uint16_t rulenum;
+ uint8_t cmd, new_set;
+
+ ac--;
+ av++;
+
+ if (!ac)
+ errx(EX_USAGE, "set needs command");
+ if (_substrcmp(*av, "show") == 0) {
+ void *data;
+ char const *msg;
+
+ nbytes = sizeof(struct ip_fw);
+ if ((data = calloc(1, nbytes)) == NULL)
+ err(EX_OSERR, "calloc");
+ if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0)
+ err(EX_OSERR, "getsockopt(IP_FW_GET)");
+ bcopy(&((struct ip_fw *)data)->next_rule,
+ &set_disable, sizeof(set_disable));
+
+ for (i = 0, msg = "disable" ; i < RESVD_SET; i++)
+ if ((set_disable & (1<<i))) {
+ printf("%s %d", msg, i);
+ msg = "";
+ }
+ msg = (set_disable) ? " enable" : "enable";
+ for (i = 0; i < RESVD_SET; i++)
+ if (!(set_disable & (1<<i))) {
+ printf("%s %d", msg, i);
+ msg = "";
+ }
+ printf("\n");
+ } else if (_substrcmp(*av, "swap") == 0) {
+ ac--; av++;
+ if (ac != 2)
+ errx(EX_USAGE, "set swap needs 2 set numbers\n");
+ rulenum = atoi(av[0]);
+ new_set = atoi(av[1]);
+ if (!isdigit(*(av[0])) || rulenum > RESVD_SET)
+ errx(EX_DATAERR, "invalid set number %s\n", av[0]);
+ if (!isdigit(*(av[1])) || new_set > RESVD_SET)
+ errx(EX_DATAERR, "invalid set number %s\n", av[1]);
+ masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
+ i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
+ } else if (_substrcmp(*av, "move") == 0) {
+ ac--; av++;
+ if (ac && _substrcmp(*av, "rule") == 0) {
+ cmd = 2;
+ ac--; av++;
+ } else
+ cmd = 3;
+ if (ac != 3 || _substrcmp(av[1], "to") != 0)
+ errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
+ rulenum = atoi(av[0]);
+ new_set = atoi(av[2]);
+ if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) ||
+ (cmd == 2 && rulenum == 65535) )
+ errx(EX_DATAERR, "invalid source number %s\n", av[0]);
+ if (!isdigit(*(av[2])) || new_set > RESVD_SET)
+ errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
+ masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
+ i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
+ } else if (_substrcmp(*av, "disable") == 0 ||
+ _substrcmp(*av, "enable") == 0 ) {
+ int which = _substrcmp(*av, "enable") == 0 ? 1 : 0;
+
+ ac--; av++;
+ masks[0] = masks[1] = 0;
+
+ while (ac) {
+ if (isdigit(**av)) {
+ i = atoi(*av);
+ if (i < 0 || i > RESVD_SET)
+ errx(EX_DATAERR,
+ "invalid set number %d\n", i);
+ masks[which] |= (1<<i);
+ } else if (_substrcmp(*av, "disable") == 0)
+ which = 0;
+ else if (_substrcmp(*av, "enable") == 0)
+ which = 1;
+ else
+ errx(EX_DATAERR,
+ "invalid set command %s\n", *av);
+ av++; ac--;
+ }
+ if ( (masks[0] & masks[1]) != 0 )
+ errx(EX_DATAERR,
+ "cannot enable and disable the same set\n");
+
+ i = do_cmd(IP_FW_DEL, masks, sizeof(masks));
+ if (i)
+ warn("set enable/disable: setsockopt(IP_FW_DEL)");
+ } else
+ errx(EX_USAGE, "invalid set command %s\n", *av);
+}
+
+static void
+sysctl_handler(int ac, char *av[], int which)
+{
+ ac--;
+ av++;
+
+ if (ac == 0) {
+ warnx("missing keyword to enable/disable\n");
+ } else if (_substrcmp(*av, "firewall") == 0) {
+ sysctlbyname("net.inet.ip.fw.enable", NULL, 0,
+ &which, sizeof(which));
+ } else if (_substrcmp(*av, "one_pass") == 0) {
+ sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0,
+ &which, sizeof(which));
+ } else if (_substrcmp(*av, "debug") == 0) {
+ sysctlbyname("net.inet.ip.fw.debug", NULL, 0,
+ &which, sizeof(which));
+ } else if (_substrcmp(*av, "verbose") == 0) {
+ sysctlbyname("net.inet.ip.fw.verbose", NULL, 0,
+ &which, sizeof(which));
+ } else if (_substrcmp(*av, "dyn_keepalive") == 0) {
+ sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0,
+ &which, sizeof(which));
+ } else if (_substrcmp(*av, "altq") == 0) {
+ altq_set_enabled(which);
+ } else {
+ warnx("unrecognize enable/disable keyword: %s\n", *av);
+ }
+}
+
+static void
+list(int ac, char *av[], int show_counters)
+{
+ struct ip_fw *r;
+ ipfw_dyn_rule *dynrules, *d;
+
+#define NEXT(r) ((struct ip_fw *)((char *)r + RULESIZE(r)))
+ char *lim;
+ void *data = NULL;
+ int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width;
+ int exitval = EX_OK;
+ int lac;
+ char **lav;
+ u_long rnum, last;
+ char *endptr;
+ int seen = 0;
+
+ const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
+ int nalloc = 1024; /* start somewhere... */
+
+ last = 0;
+
+ if (test_only) {
+ fprintf(stderr, "Testing only, list disabled\n");
+ return;
+ }
+
+ ac--;
+ av++;
+
+ /* get rules or pipes from kernel, resizing array as necessary */
+ nbytes = nalloc;
+
+ while (nbytes >= nalloc) {
+ nalloc = nalloc * 2 + 200;
+ nbytes = nalloc;
+ if ((data = realloc(data, nbytes)) == NULL)
+ err(EX_OSERR, "realloc");
+ if (do_cmd(ocmd, data, (uintptr_t)&nbytes) < 0)
+ err(EX_OSERR, "getsockopt(IP_%s_GET)",
+ do_pipe ? "DUMMYNET" : "FW");
+ }
+
+ if (do_pipe) {
+ list_pipes(data, nbytes, ac, av);
+ goto done;
+ }
+
+ /*
+ * Count static rules. They have variable size so we
+ * need to scan the list to count them.
+ */
+ for (nstat = 1, r = data, lim = (char *)data + nbytes;
+ r->rulenum < 65535 && (char *)r < lim;
+ ++nstat, r = NEXT(r) )
+ ; /* nothing */
+
+ /*
+ * Count dynamic rules. This is easier as they have
+ * fixed size.
+ */
+ r = NEXT(r);
+ dynrules = (ipfw_dyn_rule *)r ;
+ n = (char *)r - (char *)data;
+ ndyn = (nbytes - n) / sizeof *dynrules;
+
+ /* if showing stats, figure out column widths ahead of time */
+ bcwidth = pcwidth = 0;
+ if (show_counters) {
+ for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
+ /* packet counter */
+ width = snprintf(NULL, 0, "%llu",
+ align_uint64(&r->pcnt));
+ if (width > pcwidth)
+ pcwidth = width;
+
+ /* byte counter */
+ width = snprintf(NULL, 0, "%llu",
+ align_uint64(&r->bcnt));
+ if (width > bcwidth)
+ bcwidth = width;
+ }
+ }
+ if (do_dynamic && ndyn) {
+ for (n = 0, d = dynrules; n < ndyn; n++, d++) {
+ width = snprintf(NULL, 0, "%llu",
+ align_uint64(&d->pcnt));
+ if (width > pcwidth)
+ pcwidth = width;
+
+ width = snprintf(NULL, 0, "%llu",
+ align_uint64(&d->bcnt));
+ if (width > bcwidth)
+ bcwidth = width;
+ }
+ }
+ /* if no rule numbers were specified, list all rules */
+ if (ac == 0) {
+ for (n = 0, r = data; n < nstat; n++, r = NEXT(r) )
+ show_ipfw(r, pcwidth, bcwidth);
+
+ if (do_dynamic && ndyn) {
+ printf("## Dynamic rules (%d):\n", ndyn);
+ for (n = 0, d = dynrules; n < ndyn; n++, d++)
+ show_dyn_ipfw(d, pcwidth, bcwidth);
+ }
+ goto done;
+ }
+
+ /* display specific rules requested on command line */
+
+ for (lac = ac, lav = av; lac != 0; lac--) {
+ /* convert command line rule # */
+ last = rnum = strtoul(*lav++, &endptr, 10);
+ if (*endptr == '-')
+ last = strtoul(endptr+1, &endptr, 10);
+ if (*endptr) {
+ exitval = EX_USAGE;
+ warnx("invalid rule number: %s", *(lav - 1));
+ continue;
+ }
+ for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) {
+ if (r->rulenum > last)
+ break;
+ if (r->rulenum >= rnum && r->rulenum <= last) {
+ show_ipfw(r, pcwidth, bcwidth);
+ seen = 1;
+ }
+ }
+ if (!seen) {
+ /* give precedence to other error(s) */
+ if (exitval == EX_OK)
+ exitval = EX_UNAVAILABLE;
+ warnx("rule %lu does not exist", rnum);
+ }
+ }
+
+ if (do_dynamic && ndyn) {
+ printf("## Dynamic rules:\n");
+ for (lac = ac, lav = av; lac != 0; lac--) {
+ last = rnum = strtoul(*lav++, &endptr, 10);
+ if (*endptr == '-')
+ last = strtoul(endptr+1, &endptr, 10);
+ if (*endptr)
+ /* already warned */
+ continue;
+ for (n = 0, d = dynrules; n < ndyn; n++, d++) {
+ uint16_t rulenum;
+
+ bcopy(&d->rule, &rulenum, sizeof(rulenum));
+ if (rulenum > rnum)
+ break;
+ if (r->rulenum >= rnum && r->rulenum <= last)
+ show_dyn_ipfw(d, pcwidth, bcwidth);
+ }
+ }
+ }
+
+ ac = 0;
+
+done:
+ free(data);
+
+ if (exitval != EX_OK)
+ exit(exitval);
+#undef NEXT
+}
+
+static void
+show_usage(void)
+{
+ fprintf(stderr, "usage: ipfw [options]\n"
+"do \"ipfw -h\" or see ipfw manpage for details\n"
+);
+ exit(EX_USAGE);
+}
+
+static void
+help(void)
+{
+ fprintf(stderr,
+"ipfw syntax summary (but please do read the ipfw(8) manpage):\n"
+"ipfw [-abcdefhnNqStTv] <command> where <command> is one of:\n"
+"add [num] [set N] [prob x] RULE-BODY\n"
+"{pipe|queue} N config PIPE-BODY\n"
+"[pipe|queue] {zero|delete|show} [N{,N}]\n"
+"set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n"
+"table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n"
+"\n"
+"RULE-BODY: check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n"
+"ACTION: check-state | allow | count | deny | unreach{,6} CODE |\n"
+" skipto N | {divert|tee} PORT | forward ADDR |\n"
+" pipe N | queue N\n"
+"PARAMS: [log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n"
+"ADDR: [ MAC dst src ether_type ] \n"
+" [ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
+" [ ipv6|ip6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n"
+"IPADDR: [not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n"
+"IP6ADDR: [not] { any | me | me6 | ip6/bits | IP6LIST }\n"
+"IP6LIST: { ip6 | ip6/bits }[,IP6LIST]\n"
+"IPLIST: { ip | ip/bits | ip:mask }[,IPLIST]\n"
+"OPTION_LIST: OPTION [OPTION_LIST]\n"
+"OPTION: bridged | diverted | diverted-loopback | diverted-output |\n"
+" {dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n"
+" {dst-port|src-port} LIST |\n"
+" estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n"
+" iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n"
+" ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n"
+" icmp6types LIST | ext6hdr LIST | flow-id N[,N] |\n"
+" mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n"
+" setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n"
+" tcpdatalen LIST | verrevpath | versrcreach | antispoof\n"
+);
+exit(0);
+}
+
+
+static int
+lookup_host (char *host, struct in_addr *ipaddr)
+{
+ struct hostent *he;
+
+ if (!inet_aton(host, ipaddr)) {
+ if ((he = gethostbyname(host)) == NULL)
+ return(-1);
+ *ipaddr = *(struct in_addr *)he->h_addr_list[0];
+ }
+ return(0);
+}
+
+/*
+ * fills the addr and mask fields in the instruction as appropriate from av.
+ * Update length as appropriate.
+ * The following formats are allowed:
+ * me returns O_IP_*_ME
+ * 1.2.3.4 single IP address
+ * 1.2.3.4:5.6.7.8 address:mask
+ * 1.2.3.4/24 address/mask
+ * 1.2.3.4/26{1,6,5,4,23} set of addresses in a subnet
+ * We can have multiple comma-separated address/mask entries.
+ */
+static void
+fill_ip(ipfw_insn_ip *cmd, char *av)
+{
+ int len = 0;
+ uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
+
+ cmd->o.len &= ~F_LEN_MASK; /* zero len */
+
+ if (_substrcmp(av, "any") == 0)
+ return;
+
+ if (_substrcmp(av, "me") == 0) {
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn);
+ return;
+ }
+
+ if (strncmp(av, "table(", 6) == 0) {
+ char *p = strchr(av + 6, ',');
+
+ if (p)
+ *p++ = '\0';
+ cmd->o.opcode = O_IP_DST_LOOKUP;
+ cmd->o.arg1 = strtoul(av + 6, NULL, 0);
+ if (p) {
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
+ d[0] = strtoul(p, NULL, 0);
+ } else
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn);
+ return;
+ }
+
+ while (av) {
+ /*
+ * After the address we can have '/' or ':' indicating a mask,
+ * ',' indicating another address follows, '{' indicating a
+ * set of addresses of unspecified size.
+ */
+ char *p = strpbrk(av, "/:,{");
+ int masklen;
+ char md;
+
+ if (p) {
+ md = *p;
+ *p++ = '\0';
+ } else
+ md = '\0';
+
+ if (lookup_host(av, (struct in_addr *)&d[0]) != 0)
+ errx(EX_NOHOST, "hostname ``%s'' unknown", av);
+ switch (md) {
+ case ':':
+ if (!inet_aton(p, (struct in_addr *)&d[1]))
+ errx(EX_DATAERR, "bad netmask ``%s''", p);
+ break;
+ case '/':
+ masklen = atoi(p);
+ if (masklen == 0)
+ d[1] = htonl(0); /* mask */
+ else if (masklen > 32)
+ errx(EX_DATAERR, "bad width ``%s''", p);
+ else
+ d[1] = htonl(~0 << (32 - masklen));
+ break;
+ case '{': /* no mask, assume /24 and put back the '{' */
+ d[1] = htonl(~0 << (32 - 24));
+ *(--p) = md;
+ break;
+
+ case ',': /* single address plus continuation */
+ *(--p) = md;
+ /* FALLTHROUGH */
+ case 0: /* initialization value */
+ default:
+ d[1] = htonl(~0); /* force /32 */
+ break;
+ }
+ d[0] &= d[1]; /* mask base address with mask */
+ /* find next separator */
+ if (p)
+ p = strpbrk(p, ",{");
+ if (p && *p == '{') {
+ /*
+ * We have a set of addresses. They are stored as follows:
+ * arg1 is the set size (powers of 2, 2..256)
+ * addr is the base address IN HOST FORMAT
+ * mask.. is an array of arg1 bits (rounded up to
+ * the next multiple of 32) with bits set
+ * for each host in the map.
+ */
+ uint32_t *map = (uint32_t *)&cmd->mask;
+ int low, high;
+ int i = contigmask((uint8_t *)&(d[1]), 32);
+
+ if (len > 0)
+ errx(EX_DATAERR, "address set cannot be in a list");
+ if (i < 24 || i > 31)
+ errx(EX_DATAERR, "invalid set with mask %d\n", i);
+ cmd->o.arg1 = 1<<(32-i); /* map length */
+ d[0] = ntohl(d[0]); /* base addr in host format */
+ cmd->o.opcode = O_IP_DST_SET; /* default */
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32;
+ for (i = 0; i < (cmd->o.arg1+31)/32 ; i++)
+ map[i] = 0; /* clear map */
+
+ av = p + 1;
+ low = d[0] & 0xff;
+ high = low + cmd->o.arg1 - 1;
+ /*
+ * Here, i stores the previous value when we specify a range
+ * of addresses within a mask, e.g. 45-63. i = -1 means we
+ * have no previous value.
+ */
+ i = -1; /* previous value in a range */
+ while (isdigit(*av)) {
+ char *s;
+ int a = strtol(av, &s, 0);
+
+ if (s == av) { /* no parameter */
+ if (*av != '}')
+ errx(EX_DATAERR, "set not closed\n");
+ if (i != -1)
+ errx(EX_DATAERR, "incomplete range %d-", i);
+ break;
+ }
+ if (a < low || a > high)
+ errx(EX_DATAERR, "addr %d out of range [%d-%d]\n",
+ a, low, high);
+ a -= low;
+ if (i == -1) /* no previous in range */
+ i = a;
+ else { /* check that range is valid */
+ if (i > a)
+ errx(EX_DATAERR, "invalid range %d-%d",
+ i+low, a+low);
+ if (*s == '-')
+ errx(EX_DATAERR, "double '-' in range");
+ }
+ for (; i <= a; i++)
+ map[i/32] |= 1<<(i & 31);
+ i = -1;
+ if (*s == '-')
+ i = a;
+ else if (*s == '}')
+ break;
+ av = s+1;
+ }
+ return;
+ }
+ av = p;
+ if (av) /* then *av must be a ',' */
+ av++;
+
+ /* Check this entry */
+ if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */
+ /*
+ * 'any' turns the entire list into a NOP.
+ * 'not any' never matches, so it is removed from the
+ * list unless it is the only item, in which case we
+ * report an error.
+ */
+ if (cmd->o.len & F_NOT) { /* "not any" never matches */
+ if (av == NULL && len == 0) /* only this entry */
+ errx(EX_DATAERR, "not any never matches");
+ }
+ /* else do nothing and skip this entry */
+ return;
+ }
+ /* A single IP can be stored in an optimized format */
+ if (d[1] == IP_MASK_ALL && av == NULL && len == 0) {
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
+ return;
+ }
+ len += 2; /* two words... */
+ d += 2;
+ } /* end while */
+ cmd->o.len |= len+1;
+}
+
+
+/* Try to find ipv6 address by hostname */
+static int
+lookup_host6 (char *host, struct in6_addr *ip6addr)
+{
+ struct hostent *he;
+
+ if (!inet_pton(AF_INET6, host, ip6addr)) {
+ if ((he = gethostbyname2(host, AF_INET6)) == NULL)
+ return(-1);
+ memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr));
+ }
+ return(0);
+}
+
+
+/* n2mask sets n bits of the mask */
+static void
+n2mask(struct in6_addr *mask, int n)
+{
+ static int minimask[9] =
+ { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
+ u_char *p;
+
+ memset(mask, 0, sizeof(struct in6_addr));
+ p = (u_char *) mask;
+ for (; n > 0; p++, n -= 8) {
+ if (n >= 8)
+ *p = 0xff;
+ else
+ *p = minimask[n];
+ }
+ return;
+}
+
+
+/*
+ * fill the addr and mask fields in the instruction as appropriate from av.
+ * Update length as appropriate.
+ * The following formats are allowed:
+ * any matches any IP6. Actually returns an empty instruction.
+ * me returns O_IP6_*_ME
+ *
+ * 03f1::234:123:0342 single IP6 addres
+ * 03f1::234:123:0342/24 address/mask
+ * 03f1::234:123:0342/24,03f1::234:123:0343/ List of address
+ *
+ * Set of address (as in ipv6) not supported because ipv6 address
+ * are typically random past the initial prefix.
+ * Return 1 on success, 0 on failure.
+ */
+static int
+fill_ip6(ipfw_insn_ip6 *cmd, char *av)
+{
+ int len = 0;
+ struct in6_addr *d = &(cmd->addr6);
+ /*
+ * Needed for multiple address.
+ * Note d[1] points to struct in6_add r mask6 of cmd
+ */
+
+ cmd->o.len &= ~F_LEN_MASK; /* zero len */
+
+ if (strcmp(av, "any") == 0)
+ return (1);
+
+
+ if (strcmp(av, "me") == 0) { /* Set the data for "me" opt*/
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn);
+ return (1);
+ }
+
+ if (strcmp(av, "me6") == 0) { /* Set the data for "me" opt*/
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn);
+ return (1);
+ }
+
+ av = strdup(av);
+ while (av) {
+ /*
+ * After the address we can have '/' indicating a mask,
+ * or ',' indicating another address follows.
+ */
+
+ char *p;
+ int masklen;
+ char md = '\0';
+
+ if ((p = strpbrk(av, "/,")) ) {
+ md = *p; /* save the separator */
+ *p = '\0'; /* terminate address string */
+ p++; /* and skip past it */
+ }
+ /* now p points to NULL, mask or next entry */
+
+ /* lookup stores address in *d as a side effect */
+ if (lookup_host6(av, d) != 0) {
+ /* XXX: failed. Free memory and go */
+ errx(EX_DATAERR, "bad address \"%s\"", av);
+ }
+ /* next, look at the mask, if any */
+ masklen = (md == '/') ? atoi(p) : 128;
+ if (masklen > 128 || masklen < 0)
+ errx(EX_DATAERR, "bad width \"%s\''", p);
+ else
+ n2mask(&d[1], masklen);
+
+ APPLY_MASK(d, &d[1]) /* mask base address with mask */
+
+ /* find next separator */
+
+ if (md == '/') { /* find separator past the mask */
+ p = strpbrk(p, ",");
+ if (p != NULL)
+ p++;
+ }
+ av = p;
+
+ /* Check this entry */
+ if (masklen == 0) {
+ /*
+ * 'any' turns the entire list into a NOP.
+ * 'not any' never matches, so it is removed from the
+ * list unless it is the only item, in which case we
+ * report an error.
+ */
+ if (cmd->o.len & F_NOT && av == NULL && len == 0)
+ errx(EX_DATAERR, "not any never matches");
+ continue;
+ }
+
+ /*
+ * A single IP can be stored alone
+ */
+ if (masklen == 128 && av == NULL && len == 0) {
+ len = F_INSN_SIZE(struct in6_addr);
+ break;
+ }
+
+ /* Update length and pointer to arguments */
+ len += F_INSN_SIZE(struct in6_addr)*2;
+ d += 2;
+ } /* end while */
+
+ /*
+ * Total length of the command, remember that 1 is the size of
+ * the base command.
+ */
+ cmd->o.len |= len+1;
+ free(av);
+ return (1);
+}
+
+/*
+ * fills command for ipv6 flow-id filtering
+ * note that the 20 bit flow number is stored in a array of u_int32_t
+ * it's supported lists of flow-id, so in the o.arg1 we store how many
+ * additional flow-id we want to filter, the basic is 1
+ */
+void
+fill_flow6( ipfw_insn_u32 *cmd, char *av )
+{
+ u_int32_t type; /* Current flow number */
+ u_int16_t nflow = 0; /* Current flow index */
+ char *s = av;
+ cmd->d[0] = 0; /* Initializing the base number*/
+
+ while (s) {
+ av = strsep( &s, ",") ;
+ type = strtoul(av, &av, 0);
+ if (*av != ',' && *av != '\0')
+ errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
+ if (type > 0xfffff)
+ errx(EX_DATAERR, "flow number out of range %s", av);
+ cmd->d[nflow] |= type;
+ nflow++;
+ }
+ if( nflow > 0 ) {
+ cmd->o.opcode = O_FLOW6ID;
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow;
+ cmd->o.arg1 = nflow;
+ }
+ else {
+ errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
+ }
+}
+
+static ipfw_insn *
+add_srcip6(ipfw_insn *cmd, char *av)
+{
+
+ fill_ip6((ipfw_insn_ip6 *)cmd, av);
+ if (F_LEN(cmd) == 0) /* any */
+ ;
+ if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */
+ cmd->opcode = O_IP6_SRC_ME;
+ } else if (F_LEN(cmd) ==
+ (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
+ /* single IP, no mask*/
+ cmd->opcode = O_IP6_SRC;
+ } else { /* addr/mask opt */
+ cmd->opcode = O_IP6_SRC_MASK;
+ }
+ return cmd;
+}
+
+static ipfw_insn *
+add_dstip6(ipfw_insn *cmd, char *av)
+{
+
+ fill_ip6((ipfw_insn_ip6 *)cmd, av);
+ if (F_LEN(cmd) == 0) /* any */
+ ;
+ if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */
+ cmd->opcode = O_IP6_DST_ME;
+ } else if (F_LEN(cmd) ==
+ (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
+ /* single IP, no mask*/
+ cmd->opcode = O_IP6_DST;
+ } else { /* addr/mask opt */
+ cmd->opcode = O_IP6_DST_MASK;
+ }
+ return cmd;
+}
+
+
+/*
+ * helper function to process a set of flags and set bits in the
+ * appropriate masks.
+ */
+static void
+fill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
+ struct _s_x *flags, char *p)
+{
+ uint8_t set=0, clear=0;
+
+ while (p && *p) {
+ char *q; /* points to the separator */
+ int val;
+ uint8_t *which; /* mask we are working on */
+
+ if (*p == '!') {
+ p++;
+ which = &clear;
+ } else
+ which = &set;
+ q = strchr(p, ',');
+ if (q)
+ *q++ = '\0';
+ val = match_token(flags, p);
+ if (val <= 0)
+ errx(EX_DATAERR, "invalid flag %s", p);
+ *which |= (uint8_t)val;
+ p = q;
+ }
+ cmd->opcode = opcode;
+ cmd->len = (cmd->len & (F_NOT | F_OR)) | 1;
+ cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
+}
+
+
+static void
+delete(int ac, char *av[])
+{
+ uint32_t rulenum;
+ struct dn_pipe p;
+ int i;
+ int exitval = EX_OK;
+ int do_set = 0;
+
+ memset(&p, 0, sizeof p);
+
+ av++; ac--;
+ NEED1("missing rule specification");
+ if (ac > 0 && _substrcmp(*av, "set") == 0) {
+ do_set = 1; /* delete set */
+ ac--; av++;
+ }
+
+ /* Rule number */
+ while (ac && isdigit(**av)) {
+ i = atoi(*av); av++; ac--;
+ if (do_pipe) {
+ if (do_pipe == 1)
+ p.pipe_nr = i;
+ else
+ p.fs.fs_nr = i;
+ i = do_cmd(IP_DUMMYNET_DEL, &p, sizeof p);
+ if (i) {
+ exitval = 1;
+ warn("rule %u: setsockopt(IP_DUMMYNET_DEL)",
+ do_pipe == 1 ? p.pipe_nr : p.fs.fs_nr);
+ }
+ } else {
+ rulenum = (i & 0xffff) | (do_set << 24);
+ i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum);
+ if (i) {
+ exitval = EX_UNAVAILABLE;
+ warn("rule %u: setsockopt(IP_FW_DEL)",
+ rulenum);
+ }
+ }
+ }
+ if (exitval != EX_OK)
+ exit(exitval);
+}
+
+
+/*
+ * fill the interface structure. We do not check the name as we can
+ * create interfaces dynamically, so checking them at insert time
+ * makes relatively little sense.
+ * Interface names containing '*', '?', or '[' are assumed to be shell
+ * patterns which match interfaces.
+ */
+static void
+fill_iface(ipfw_insn_if *cmd, char *arg)
+{
+ cmd->name[0] = '\0';
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
+
+ /* Parse the interface or address */
+ if (strcmp(arg, "any") == 0)
+ cmd->o.len = 0; /* effectively ignore this command */
+ else if (!isdigit(*arg)) {
+ strlcpy(cmd->name, arg, sizeof(cmd->name));
+ cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
+ } else if (!inet_aton(arg, &cmd->p.ip))
+ errx(EX_DATAERR, "bad ip address ``%s''", arg);
+}
+
+static void
+config_pipe(int ac, char **av)
+{
+ struct dn_pipe p;
+ int i;
+ char *end;
+ void *par = NULL;
+
+ memset(&p, 0, sizeof p);
+
+ av++; ac--;
+ /* Pipe number */
+ if (ac && isdigit(**av)) {
+ i = atoi(*av); av++; ac--;
+ if (do_pipe == 1)
+ p.pipe_nr = i;
+ else
+ p.fs.fs_nr = i;
+ }
+ while (ac > 0) {
+ double d;
+ int tok = match_token(dummynet_params, *av);
+ ac--; av++;
+
+ switch(tok) {
+ case TOK_NOERROR:
+ p.fs.flags_fs |= DN_NOERROR;
+ break;
+
+ case TOK_PLR:
+ NEED1("plr needs argument 0..1\n");
+ d = strtod(av[0], NULL);
+ if (d > 1)
+ d = 1;
+ else if (d < 0)
+ d = 0;
+ p.fs.plr = (int)(d*0x7fffffff);
+ ac--; av++;
+ break;
+
+ case TOK_QUEUE:
+ NEED1("queue needs queue size\n");
+ end = NULL;
+ p.fs.qsize = strtoul(av[0], &end, 0);
+ if (*end == 'K' || *end == 'k') {
+ p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
+ p.fs.qsize *= 1024;
+ } else if (*end == 'B' ||
+ _substrcmp2(end, "by", "bytes") == 0) {
+ p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
+ }
+ ac--; av++;
+ break;
+
+ case TOK_BUCKETS:
+ NEED1("buckets needs argument\n");
+ p.fs.rq_size = strtoul(av[0], NULL, 0);
+ ac--; av++;
+ break;
+
+ case TOK_MASK:
+ NEED1("mask needs mask specifier\n");
+ /*
+ * per-flow queue, mask is dst_ip, dst_port,
+ * src_ip, src_port, proto measured in bits
+ */
+ par = NULL;
+
+ bzero(&p.fs.flow_mask, sizeof(p.fs.flow_mask));
+ end = NULL;
+
+ while (ac >= 1) {
+ uint32_t *p32 = NULL;
+ uint16_t *p16 = NULL;
+ uint32_t *p20 = NULL;
+ struct in6_addr *pa6 = NULL;
+ uint32_t a;
+
+ tok = match_token(dummynet_params, *av);
+ ac--; av++;
+ switch(tok) {
+ case TOK_ALL:
+ /*
+ * special case, all bits significant
+ */
+ p.fs.flow_mask.dst_ip = ~0;
+ p.fs.flow_mask.src_ip = ~0;
+ p.fs.flow_mask.dst_port = ~0;
+ p.fs.flow_mask.src_port = ~0;
+ p.fs.flow_mask.proto = ~0;
+ n2mask(&(p.fs.flow_mask.dst_ip6), 128);
+ n2mask(&(p.fs.flow_mask.src_ip6), 128);
+ p.fs.flow_mask.flow_id6 = ~0;
+ p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
+ goto end_mask;
+
+ case TOK_DSTIP:
+ p32 = &p.fs.flow_mask.dst_ip;
+ break;
+
+ case TOK_SRCIP:
+ p32 = &p.fs.flow_mask.src_ip;
+ break;
+
+ case TOK_DSTIP6:
+ pa6 = &(p.fs.flow_mask.dst_ip6);
+ break;
+
+ case TOK_SRCIP6:
+ pa6 = &(p.fs.flow_mask.src_ip6);
+ break;
+
+ case TOK_FLOWID:
+ p20 = &p.fs.flow_mask.flow_id6;
+ break;
+
+ case TOK_DSTPORT:
+ p16 = &p.fs.flow_mask.dst_port;
+ break;
+
+ case TOK_SRCPORT:
+ p16 = &p.fs.flow_mask.src_port;
+ break;
+
+ case TOK_PROTO:
+ break;
+
+ default:
+ ac++; av--; /* backtrack */
+ goto end_mask;
+ }
+ if (ac < 1)
+ errx(EX_USAGE, "mask: value missing");
+ if (*av[0] == '/') {
+ a = strtoul(av[0]+1, &end, 0);
+ if (pa6 == NULL)
+ a = (a == 32) ? ~0 : (1 << a) - 1;
+ } else
+ a = strtoul(av[0], &end, 0);
+ if (p32 != NULL)
+ *p32 = a;
+ else if (p16 != NULL) {
+ if (a > 0xFFFF)
+ errx(EX_DATAERR,
+ "port mask must be 16 bit");
+ *p16 = (uint16_t)a;
+ } else if (p20 != NULL) {
+ if (a > 0xfffff)
+ errx(EX_DATAERR,
+ "flow_id mask must be 20 bit");
+ *p20 = (uint32_t)a;
+ } else if (pa6 != NULL) {
+ if (a < 0 || a > 128)
+ errx(EX_DATAERR,
+ "in6addr invalid mask len");
+ else
+ n2mask(pa6, a);
+ } else {
+ if (a > 0xFF)
+ errx(EX_DATAERR,
+ "proto mask must be 8 bit");
+ p.fs.flow_mask.proto = (uint8_t)a;
+ }
+ if (a != 0)
+ p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
+ ac--; av++;
+ } /* end while, config masks */
+end_mask:
+ break;
+
+ case TOK_RED:
+ case TOK_GRED:
+ NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
+ p.fs.flags_fs |= DN_IS_RED;
+ if (tok == TOK_GRED)
+ p.fs.flags_fs |= DN_IS_GENTLE_RED;
+ /*
+ * the format for parameters is w_q/min_th/max_th/max_p
+ */
+ if ((end = strsep(&av[0], "/"))) {
+ double w_q = strtod(end, NULL);
+ if (w_q > 1 || w_q <= 0)
+ errx(EX_DATAERR, "0 < w_q <= 1");
+ p.fs.w_q = (int) (w_q * (1 << SCALE_RED));
+ }
+ if ((end = strsep(&av[0], "/"))) {
+ p.fs.min_th = strtoul(end, &end, 0);
+ if (*end == 'K' || *end == 'k')
+ p.fs.min_th *= 1024;
+ }
+ if ((end = strsep(&av[0], "/"))) {
+ p.fs.max_th = strtoul(end, &end, 0);
+ if (*end == 'K' || *end == 'k')
+ p.fs.max_th *= 1024;
+ }
+ if ((end = strsep(&av[0], "/"))) {
+ double max_p = strtod(end, NULL);
+ if (max_p > 1 || max_p <= 0)
+ errx(EX_DATAERR, "0 < max_p <= 1");
+ p.fs.max_p = (int)(max_p * (1 << SCALE_RED));
+ }
+ ac--; av++;
+ break;
+
+ case TOK_DROPTAIL:
+ p.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
+ break;
+
+ case TOK_BW:
+ NEED1("bw needs bandwidth or interface\n");
+ if (do_pipe != 1)
+ errx(EX_DATAERR, "bandwidth only valid for pipes");
+ /*
+ * set clocking interface or bandwidth value
+ */
+ if (av[0][0] >= 'a' && av[0][0] <= 'z') {
+ int l = sizeof(p.if_name)-1;
+ /* interface name */
+ strncpy(p.if_name, av[0], l);
+ p.if_name[l] = '\0';
+ p.bandwidth = 0;
+ } else {
+ p.if_name[0] = '\0';
+ p.bandwidth = strtoul(av[0], &end, 0);
+ if (*end == 'K' || *end == 'k') {
+ end++;
+ p.bandwidth *= 1000;
+ } else if (*end == 'M') {
+ end++;
+ p.bandwidth *= 1000000;
+ }
+ if (*end == 'B' ||
+ _substrcmp2(end, "by", "bytes") == 0)
+ p.bandwidth *= 8;
+ if (p.bandwidth < 0)
+ errx(EX_DATAERR, "bandwidth too large");
+ }
+ ac--; av++;
+ break;
+
+ case TOK_DELAY:
+ if (do_pipe != 1)
+ errx(EX_DATAERR, "delay only valid for pipes");
+ NEED1("delay needs argument 0..10000ms\n");
+ p.delay = strtoul(av[0], NULL, 0);
+ ac--; av++;
+ break;
+
+ case TOK_WEIGHT:
+ if (do_pipe == 1)
+ errx(EX_DATAERR,"weight only valid for queues");
+ NEED1("weight needs argument 0..100\n");
+ p.fs.weight = strtoul(av[0], &end, 0);
+ ac--; av++;
+ break;
+
+ case TOK_PIPE:
+ if (do_pipe == 1)
+ errx(EX_DATAERR,"pipe only valid for queues");
+ NEED1("pipe needs pipe_number\n");
+ p.fs.parent_nr = strtoul(av[0], &end, 0);
+ ac--; av++;
+ break;
+
+ default:
+ errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
+ }
+ }
+ if (do_pipe == 1) {
+ if (p.pipe_nr == 0)
+ errx(EX_DATAERR, "pipe_nr must be > 0");
+ if (p.delay > 10000)
+ errx(EX_DATAERR, "delay must be < 10000");
+ } else { /* do_pipe == 2, queue */
+ if (p.fs.parent_nr == 0)
+ errx(EX_DATAERR, "pipe must be > 0");
+ if (p.fs.weight >100)
+ errx(EX_DATAERR, "weight must be <= 100");
+ }
+ if (p.fs.flags_fs & DN_QSIZE_IS_BYTES) {
+ if (p.fs.qsize > 1024*1024)
+ errx(EX_DATAERR, "queue size must be < 1MB");
+ } else {
+ if (p.fs.qsize > 100)
+ errx(EX_DATAERR, "2 <= queue size <= 100");
+ }
+ if (p.fs.flags_fs & DN_IS_RED) {
+ size_t len;
+ int lookup_depth, avg_pkt_size;
+ double s, idle, weight, w_q;
+ struct clockinfo ck;
+ int t;
+
+ if (p.fs.min_th >= p.fs.max_th)
+ errx(EX_DATAERR, "min_th %d must be < than max_th %d",
+ p.fs.min_th, p.fs.max_th);
+ if (p.fs.max_th == 0)
+ errx(EX_DATAERR, "max_th must be > 0");
+
+ len = sizeof(int);
+ if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
+ &lookup_depth, &len, NULL, 0) == -1)
+
+ errx(1, "sysctlbyname(\"%s\")",
+ "net.inet.ip.dummynet.red_lookup_depth");
+ if (lookup_depth == 0)
+ errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth"
+ " must be greater than zero");
+
+ len = sizeof(int);
+ if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
+ &avg_pkt_size, &len, NULL, 0) == -1)
+
+ errx(1, "sysctlbyname(\"%s\")",
+ "net.inet.ip.dummynet.red_avg_pkt_size");
+ if (avg_pkt_size == 0)
+ errx(EX_DATAERR,
+ "net.inet.ip.dummynet.red_avg_pkt_size must"
+ " be greater than zero");
+
+ len = sizeof(struct clockinfo);
+ if (sysctlbyname("kern.clockrate", &ck, &len, NULL, 0) == -1)
+ errx(1, "sysctlbyname(\"%s\")", "kern.clockrate");
+
+ /*
+ * Ticks needed for sending a medium-sized packet.
+ * Unfortunately, when we are configuring a WF2Q+ queue, we
+ * do not have bandwidth information, because that is stored
+ * in the parent pipe, and also we have multiple queues
+ * competing for it. So we set s=0, which is not very
+ * correct. But on the other hand, why do we want RED with
+ * WF2Q+ ?
+ */
+ if (p.bandwidth==0) /* this is a WF2Q+ queue */
+ s = 0;
+ else
+ s = ck.hz * avg_pkt_size * 8 / p.bandwidth;
+
+ /*
+ * max idle time (in ticks) before avg queue size becomes 0.
+ * NOTA: (3/w_q) is approx the value x so that
+ * (1-w_q)^x < 10^-3.
+ */
+ w_q = ((double)p.fs.w_q) / (1 << SCALE_RED);
+ idle = s * 3. / w_q;
+ p.fs.lookup_step = (int)idle / lookup_depth;
+ if (!p.fs.lookup_step)
+ p.fs.lookup_step = 1;
+ weight = 1 - w_q;
+ for (t = p.fs.lookup_step; t > 0; --t)
+ weight *= weight;
+ p.fs.lookup_weight = (int)(weight * (1 << SCALE_RED));
+ }
+ i = do_cmd(IP_DUMMYNET_CONFIGURE, &p, sizeof p);
+ if (i)
+ err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
+}
+
+static void
+get_mac_addr_mask(char *p, uint8_t *addr, uint8_t *mask)
+{
+ int i, l;
+
+ for (i=0; i<6; i++)
+ addr[i] = mask[i] = 0;
+ if (strcmp(p, "any") == 0)
+ return;
+
+ for (i=0; *p && i<6;i++, p++) {
+ addr[i] = strtol(p, &p, 16);
+ if (*p != ':') /* we start with the mask */
+ break;
+ }
+ if (*p == '/') { /* mask len */
+ l = strtol(p+1, &p, 0);
+ for (i=0; l>0; l -=8, i++)
+ mask[i] = (l >=8) ? 0xff : (~0) << (8-l);
+ } else if (*p == '&') { /* mask */
+ for (i=0, p++; *p && i<6;i++, p++) {
+ mask[i] = strtol(p, &p, 16);
+ if (*p != ':')
+ break;
+ }
+ } else if (*p == '\0') {
+ for (i=0; i<6; i++)
+ mask[i] = 0xff;
+ }
+ for (i=0; i<6; i++)
+ addr[i] &= mask[i];
+}
+
+/*
+ * helper function, updates the pointer to cmd with the length
+ * of the current command, and also cleans up the first word of
+ * the new command in case it has been clobbered before.
+ */
+static ipfw_insn *
+next_cmd(ipfw_insn *cmd)
+{
+ cmd += F_LEN(cmd);
+ bzero(cmd, sizeof(*cmd));
+ return cmd;
+}
+
+/*
+ * Takes arguments and copies them into a comment
+ */
+static void
+fill_comment(ipfw_insn *cmd, int ac, char **av)
+{
+ int i, l;
+ char *p = (char *)(cmd + 1);
+
+ cmd->opcode = O_NOP;
+ cmd->len = (cmd->len & (F_NOT | F_OR));
+
+ /* Compute length of comment string. */
+ for (i = 0, l = 0; i < ac; i++)
+ l += strlen(av[i]) + 1;
+ if (l == 0)
+ return;
+ if (l > 84)
+ errx(EX_DATAERR,
+ "comment too long (max 80 chars)");
+ l = 1 + (l+3)/4;
+ cmd->len = (cmd->len & (F_NOT | F_OR)) | l;
+ for (i = 0; i < ac; i++) {
+ strcpy(p, av[i]);
+ p += strlen(av[i]);
+ *p++ = ' ';
+ }
+ *(--p) = '\0';
+}
+
+/*
+ * A function to fill simple commands of size 1.
+ * Existing flags are preserved.
+ */
+static void
+fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg)
+{
+ cmd->opcode = opcode;
+ cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | 1;
+ cmd->arg1 = arg;
+}
+
+/*
+ * Fetch and add the MAC address and type, with masks. This generates one or
+ * two microinstructions, and returns the pointer to the last one.
+ */
+static ipfw_insn *
+add_mac(ipfw_insn *cmd, int ac, char *av[])
+{
+ ipfw_insn_mac *mac;
+
+ if (ac < 2)
+ errx(EX_DATAERR, "MAC dst src");
+
+ cmd->opcode = O_MACADDR2;
+ cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
+
+ mac = (ipfw_insn_mac *)cmd;
+ get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */
+ get_mac_addr_mask(av[1], &(mac->addr[6]), &(mac->mask[6])); /* src */
+ return cmd;
+}
+
+static ipfw_insn *
+add_mactype(ipfw_insn *cmd, int ac, char *av)
+{
+ if (ac < 1)
+ errx(EX_DATAERR, "missing MAC type");
+ if (strcmp(av, "any") != 0) { /* we have a non-null type */
+ fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE);
+ cmd->opcode = O_MAC_TYPE;
+ return cmd;
+ } else
+ return NULL;
+}
+
+static ipfw_insn *
+add_proto0(ipfw_insn *cmd, char *av, u_char *protop)
+{
+ struct protoent *pe;
+ char *ep;
+ int proto;
+
+ proto = strtol(av, &ep, 0);
+ if (*ep != '\0' || proto < 0) {
+ if ((pe = getprotobyname(av)) == NULL)
+ return NULL;
+ proto = pe->p_proto;
+ }
+
+ fill_cmd(cmd, O_PROTO, 0, proto);
+ *protop = proto;
+ return cmd;
+}
+
+static ipfw_insn *
+add_proto(ipfw_insn *cmd, char *av, u_char *protop)
+{
+ u_char proto = IPPROTO_IP;
+
+ if (_substrcmp(av, "all") == 0)
+ ; /* do not set O_IP4 nor O_IP6 */
+ else if (strcmp(av, "ip4") == 0)
+ /* explicit "just IPv4" rule */
+ fill_cmd(cmd, O_IP4, 0, 0);
+ else if (strcmp(av, "ip6") == 0) {
+ /* explicit "just IPv6" rule */
+ proto = IPPROTO_IPV6;
+ fill_cmd(cmd, O_IP6, 0, 0);
+ } else
+ return add_proto0(cmd, av, protop);
+
+ *protop = proto;
+ return cmd;
+}
+
+static ipfw_insn *
+add_proto_compat(ipfw_insn *cmd, char *av, u_char *protop)
+{
+ u_char proto = IPPROTO_IP;
+
+ if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
+ ; /* do not set O_IP4 nor O_IP6 */
+ else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0)
+ /* explicit "just IPv4" rule */
+ fill_cmd(cmd, O_IP4, 0, 0);
+ else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) {
+ /* explicit "just IPv6" rule */
+ proto = IPPROTO_IPV6;
+ fill_cmd(cmd, O_IP6, 0, 0);
+ } else
+ return add_proto0(cmd, av, protop);
+
+ *protop = proto;
+ return cmd;
+}
+
+static ipfw_insn *
+add_srcip(ipfw_insn *cmd, char *av)
+{
+ fill_ip((ipfw_insn_ip *)cmd, av);
+ if (cmd->opcode == O_IP_DST_SET) /* set */
+ cmd->opcode = O_IP_SRC_SET;
+ else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
+ cmd->opcode = O_IP_SRC_LOOKUP;
+ else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */
+ cmd->opcode = O_IP_SRC_ME;
+ else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */
+ cmd->opcode = O_IP_SRC;
+ else /* addr/mask */
+ cmd->opcode = O_IP_SRC_MASK;
+ return cmd;
+}
+
+static ipfw_insn *
+add_dstip(ipfw_insn *cmd, char *av)
+{
+ fill_ip((ipfw_insn_ip *)cmd, av);
+ if (cmd->opcode == O_IP_DST_SET) /* set */
+ ;
+ else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
+ ;
+ else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */
+ cmd->opcode = O_IP_DST_ME;
+ else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */
+ cmd->opcode = O_IP_DST;
+ else /* addr/mask */
+ cmd->opcode = O_IP_DST_MASK;
+ return cmd;
+}
+
+static ipfw_insn *
+add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
+{
+ if (_substrcmp(av, "any") == 0) {
+ return NULL;
+ } else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) {
+ /* XXX todo: check that we have a protocol with ports */
+ cmd->opcode = opcode;
+ return cmd;
+ }
+ return NULL;
+}
+
+static ipfw_insn *
+add_src(ipfw_insn *cmd, char *av, u_char proto)
+{
+ struct in6_addr a;
+
+ if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 ||
+ inet_pton(AF_INET6, av, &a))
+ return add_srcip6(cmd, av);
+ /* XXX: should check for IPv4, not !IPv6 */
+ if (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
+ !inet_pton(AF_INET6, av, &a))
+ return add_srcip(cmd, av);
+ if (strcmp(av, "any") != 0)
+ return cmd;
+
+ return NULL;
+}
+
+static ipfw_insn *
+add_dst(ipfw_insn *cmd, char *av, u_char proto)
+{
+ struct in6_addr a;
+
+ if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 ||
+ inet_pton(AF_INET6, av, &a))
+ return add_dstip6(cmd, av);
+ /* XXX: should check for IPv4, not !IPv6 */
+ if (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
+ !inet_pton(AF_INET6, av, &a))
+ return add_dstip(cmd, av);
+ if (strcmp(av, "any") != 0)
+ return cmd;
+
+ return NULL;
+}
+
+/*
+ * Parse arguments and assemble the microinstructions which make up a rule.
+ * Rules are added into the 'rulebuf' and then copied in the correct order
+ * into the actual rule.
+ *
+ * The syntax for a rule starts with the action, followed by
+ * optional action parameters, and the various match patterns.
+ * In the assembled microcode, the first opcode must be an O_PROBE_STATE
+ * (generated if the rule includes a keep-state option), then the
+ * various match patterns, log/altq actions, and the actual action.
+ *
+ */
+static void
+add(int ac, char *av[])
+{
+ /*
+ * rules are added into the 'rulebuf' and then copied in
+ * the correct order into the actual rule.
+ * Some things that need to go out of order (prob, action etc.)
+ * go into actbuf[].
+ */
+ static uint32_t rulebuf[255], actbuf[255], cmdbuf[255];
+
+ ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
+ ipfw_insn *first_cmd; /* first match pattern */
+
+ struct ip_fw *rule;
+
+ /*
+ * various flags used to record that we entered some fields.
+ */
+ ipfw_insn *have_state = NULL; /* check-state or keep-state */
+ ipfw_insn *have_log = NULL, *have_altq = NULL;
+ size_t len;
+
+ int i;
+
+ int open_par = 0; /* open parenthesis ( */
+
+ /* proto is here because it is used to fetch ports */
+ u_char proto = IPPROTO_IP; /* default protocol */
+
+ double match_prob = 1; /* match probability, default is always match */
+
+ bzero(actbuf, sizeof(actbuf)); /* actions go here */
+ bzero(cmdbuf, sizeof(cmdbuf));
+ bzero(rulebuf, sizeof(rulebuf));
+
+ rule = (struct ip_fw *)rulebuf;
+ cmd = (ipfw_insn *)cmdbuf;
+ action = (ipfw_insn *)actbuf;
+
+ av++; ac--;
+
+ /* [rule N] -- Rule number optional */
+ if (ac && isdigit(**av)) {
+ rule->rulenum = atoi(*av);
+ av++;
+ ac--;
+ }
+
+ /* [set N] -- set number (0..RESVD_SET), optional */
+ if (ac > 1 && _substrcmp(*av, "set") == 0) {
+ int set = strtoul(av[1], NULL, 10);
+ if (set < 0 || set > RESVD_SET)
+ errx(EX_DATAERR, "illegal set %s", av[1]);
+ rule->set = set;
+ av += 2; ac -= 2;
+ }
+
+ /* [prob D] -- match probability, optional */
+ if (ac > 1 && _substrcmp(*av, "prob") == 0) {
+ match_prob = strtod(av[1], NULL);
+
+ if (match_prob <= 0 || match_prob > 1)
+ errx(EX_DATAERR, "illegal match prob. %s", av[1]);
+ av += 2; ac -= 2;
+ }
+
+ /* action -- mandatory */
+ NEED1("missing action");
+ i = match_token(rule_actions, *av);
+ ac--; av++;
+ action->len = 1; /* default */
+ switch(i) {
+ case TOK_CHECKSTATE:
+ have_state = action;
+ action->opcode = O_CHECK_STATE;
+ break;
+
+ case TOK_ACCEPT:
+ action->opcode = O_ACCEPT;
+ break;
+
+ case TOK_DENY:
+ action->opcode = O_DENY;
+ action->arg1 = 0;
+ break;
+
+ case TOK_REJECT:
+ action->opcode = O_REJECT;
+ action->arg1 = ICMP_UNREACH_HOST;
+ break;
+
+ case TOK_RESET:
+ action->opcode = O_REJECT;
+ action->arg1 = ICMP_REJECT_RST;
+ break;
+
+ case TOK_RESET6:
+ action->opcode = O_UNREACH6;
+ action->arg1 = ICMP6_UNREACH_RST;
+ break;
+
+ case TOK_UNREACH:
+ action->opcode = O_REJECT;
+ NEED1("missing reject code");
+ fill_reject_code(&action->arg1, *av);
+ ac--; av++;
+ break;
+
+ case TOK_UNREACH6:
+ action->opcode = O_UNREACH6;
+ NEED1("missing unreach code");
+ fill_unreach6_code(&action->arg1, *av);
+ ac--; av++;
+ break;
+
+ case TOK_COUNT:
+ action->opcode = O_COUNT;
+ break;
+
+ case TOK_QUEUE:
+ action->opcode = O_QUEUE;
+ goto chkarg;
+ case TOK_PIPE:
+ action->opcode = O_PIPE;
+ goto chkarg;
+ case TOK_SKIPTO:
+ action->opcode = O_SKIPTO;
+ goto chkarg;
+ case TOK_NETGRAPH:
+ action->opcode = O_NETGRAPH;
+ goto chkarg;
+ case TOK_NGTEE:
+ action->opcode = O_NGTEE;
+ goto chkarg;
+ case TOK_DIVERT:
+ action->opcode = O_DIVERT;
+ goto chkarg;
+ case TOK_TEE:
+ action->opcode = O_TEE;
+chkarg:
+ if (!ac)
+ errx(EX_USAGE, "missing argument for %s", *(av - 1));
+ if (isdigit(**av)) {
+ action->arg1 = strtoul(*av, NULL, 10);
+ if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG)
+ errx(EX_DATAERR, "illegal argument for %s",
+ *(av - 1));
+ } else if (_substrcmp(*av, TABLEARG) == 0) {
+ action->arg1 = IP_FW_TABLEARG;
+ } else if (i == TOK_DIVERT || i == TOK_TEE) {
+ struct servent *s;
+ setservent(1);
+ s = getservbyname(av[0], "divert");
+ if (s != NULL)
+ action->arg1 = ntohs(s->s_port);
+ else
+ errx(EX_DATAERR, "illegal divert/tee port");
+ } else
+ errx(EX_DATAERR, "illegal argument for %s", *(av - 1));
+ ac--; av++;
+ break;
+
+ case TOK_FORWARD: {
+ ipfw_insn_sa *p = (ipfw_insn_sa *)action;
+ char *s, *end;
+
+ NEED1("missing forward address[:port]");
+
+ action->opcode = O_FORWARD_IP;
+ action->len = F_INSN_SIZE(ipfw_insn_sa);
+
+ p->sa.sin_len = sizeof(struct sockaddr_in);
+ p->sa.sin_family = AF_INET;
+ p->sa.sin_port = 0;
+ /*
+ * locate the address-port separator (':' or ',')
+ */
+ s = strchr(*av, ':');
+ if (s == NULL)
+ s = strchr(*av, ',');
+ if (s != NULL) {
+ *(s++) = '\0';
+ i = strtoport(s, &end, 0 /* base */, 0 /* proto */);
+ if (s == end)
+ errx(EX_DATAERR,
+ "illegal forwarding port ``%s''", s);
+ p->sa.sin_port = (u_short)i;
+ }
+ lookup_host(*av, &(p->sa.sin_addr));
+ }
+ ac--; av++;
+ break;
+
+ case TOK_COMMENT:
+ /* pretend it is a 'count' rule followed by the comment */
+ action->opcode = O_COUNT;
+ ac++; av--; /* go back... */
+ break;
+
+ default:
+ errx(EX_DATAERR, "invalid action %s\n", av[-1]);
+ }
+ action = next_cmd(action);
+
+ /*
+ * [altq queuename] -- altq tag, optional
+ * [log [logamount N]] -- log, optional
+ *
+ * If they exist, it go first in the cmdbuf, but then it is
+ * skipped in the copy section to the end of the buffer.
+ */
+ while (ac != 0 && (i = match_token(rule_action_params, *av)) != -1) {
+ ac--; av++;
+ switch (i) {
+ case TOK_LOG:
+ {
+ ipfw_insn_log *c = (ipfw_insn_log *)cmd;
+ int l;
+
+ if (have_log)
+ errx(EX_DATAERR,
+ "log cannot be specified more than once");
+ have_log = (ipfw_insn *)c;
+ cmd->len = F_INSN_SIZE(ipfw_insn_log);
+ cmd->opcode = O_LOG;
+ if (ac && _substrcmp(*av, "logamount") == 0) {
+ ac--; av++;
+ NEED1("logamount requires argument");
+ l = atoi(*av);
+ if (l < 0)
+ errx(EX_DATAERR,
+ "logamount must be positive");
+ c->max_log = l;
+ ac--; av++;
+ } else {
+ len = sizeof(c->max_log);
+ if (sysctlbyname("net.inet.ip.fw.verbose_limit",
+ &c->max_log, &len, NULL, 0) == -1)
+ errx(1, "sysctlbyname(\"%s\")",
+ "net.inet.ip.fw.verbose_limit");
+ }
+ }
+ break;
+
+ case TOK_ALTQ:
+ {
+ ipfw_insn_altq *a = (ipfw_insn_altq *)cmd;
+
+ NEED1("missing altq queue name");
+ if (have_altq)
+ errx(EX_DATAERR,
+ "altq cannot be specified more than once");
+ have_altq = (ipfw_insn *)a;
+ cmd->len = F_INSN_SIZE(ipfw_insn_altq);
+ cmd->opcode = O_ALTQ;
+ fill_altq_qid(&a->qid, *av);
+ ac--; av++;
+ }
+ break;
+
+ default:
+ abort();
+ }
+ cmd = next_cmd(cmd);
+ }
+
+ if (have_state) /* must be a check-state, we are done */
+ goto done;
+
+#define OR_START(target) \
+ if (ac && (*av[0] == '(' || *av[0] == '{')) { \
+ if (open_par) \
+ errx(EX_USAGE, "nested \"(\" not allowed\n"); \
+ prev = NULL; \
+ open_par = 1; \
+ if ( (av[0])[1] == '\0') { \
+ ac--; av++; \
+ } else \
+ (*av)++; \
+ } \
+ target: \
+
+
+#define CLOSE_PAR \
+ if (open_par) { \
+ if (ac && ( \
+ strcmp(*av, ")") == 0 || \
+ strcmp(*av, "}") == 0)) { \
+ prev = NULL; \
+ open_par = 0; \
+ ac--; av++; \
+ } else \
+ errx(EX_USAGE, "missing \")\"\n"); \
+ }
+
+#define NOT_BLOCK \
+ if (ac && _substrcmp(*av, "not") == 0) { \
+ if (cmd->len & F_NOT) \
+ errx(EX_USAGE, "double \"not\" not allowed\n"); \
+ cmd->len |= F_NOT; \
+ ac--; av++; \
+ }
+
+#define OR_BLOCK(target) \
+ if (ac && _substrcmp(*av, "or") == 0) { \
+ if (prev == NULL || open_par == 0) \
+ errx(EX_DATAERR, "invalid OR block"); \
+ prev->len |= F_OR; \
+ ac--; av++; \
+ goto target; \
+ } \
+ CLOSE_PAR;
+
+ first_cmd = cmd;
+
+#if 0
+ /*
+ * MAC addresses, optional.
+ * If we have this, we skip the part "proto from src to dst"
+ * and jump straight to the option parsing.
+ */
+ NOT_BLOCK;
+ NEED1("missing protocol");
+ if (_substrcmp(*av, "MAC") == 0 ||
+ _substrcmp(*av, "mac") == 0) {
+ ac--; av++; /* the "MAC" keyword */
+ add_mac(cmd, ac, av); /* exits in case of errors */
+ cmd = next_cmd(cmd);
+ ac -= 2; av += 2; /* dst-mac and src-mac */
+ NOT_BLOCK;
+ NEED1("missing mac type");
+ if (add_mactype(cmd, ac, av[0]))
+ cmd = next_cmd(cmd);
+ ac--; av++; /* any or mac-type */
+ goto read_options;
+ }
+#endif
+
+ /*
+ * protocol, mandatory
+ */
+ OR_START(get_proto);
+ NOT_BLOCK;
+ NEED1("missing protocol");
+ if (add_proto_compat(cmd, *av, &proto)) {
+ av++; ac--;
+ if (F_LEN(cmd) != 0) {
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+ } else if (first_cmd != cmd) {
+ errx(EX_DATAERR, "invalid protocol ``%s''", *av);
+ } else
+ goto read_options;
+ OR_BLOCK(get_proto);
+
+ /*
+ * "from", mandatory
+ */
+ if (!ac || _substrcmp(*av, "from") != 0)
+ errx(EX_USAGE, "missing ``from''");
+ ac--; av++;
+
+ /*
+ * source IP, mandatory
+ */
+ OR_START(source_ip);
+ NOT_BLOCK; /* optional "not" */
+ NEED1("missing source address");
+ if (add_src(cmd, *av, proto)) {
+ ac--; av++;
+ if (F_LEN(cmd) != 0) { /* ! any */
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+ } else
+ errx(EX_USAGE, "bad source address %s", *av);
+ OR_BLOCK(source_ip);
+
+ /*
+ * source ports, optional
+ */
+ NOT_BLOCK; /* optional "not" */
+ if (ac) {
+ if (_substrcmp(*av, "any") == 0 ||
+ add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
+ ac--; av++;
+ if (F_LEN(cmd) != 0)
+ cmd = next_cmd(cmd);
+ }
+ }
+
+ /*
+ * "to", mandatory
+ */
+ if (!ac || _substrcmp(*av, "to") != 0)
+ errx(EX_USAGE, "missing ``to''");
+ av++; ac--;
+
+ /*
+ * destination, mandatory
+ */
+ OR_START(dest_ip);
+ NOT_BLOCK; /* optional "not" */
+ NEED1("missing dst address");
+ if (add_dst(cmd, *av, proto)) {
+ ac--; av++;
+ if (F_LEN(cmd) != 0) { /* ! any */
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+ } else
+ errx( EX_USAGE, "bad destination address %s", *av);
+ OR_BLOCK(dest_ip);
+
+ /*
+ * dest. ports, optional
+ */
+ NOT_BLOCK; /* optional "not" */
+ if (ac) {
+ if (_substrcmp(*av, "any") == 0 ||
+ add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
+ ac--; av++;
+ if (F_LEN(cmd) != 0)
+ cmd = next_cmd(cmd);
+ }
+ }
+
+read_options:
+ if (ac && first_cmd == cmd) {
+ /*
+ * nothing specified so far, store in the rule to ease
+ * printout later.
+ */
+ rule->_pad = 1;
+ }
+ prev = NULL;
+ while (ac) {
+ char *s;
+ ipfw_insn_u32 *cmd32; /* alias for cmd */
+
+ s = *av;
+ cmd32 = (ipfw_insn_u32 *)cmd;
+
+ if (*s == '!') { /* alternate syntax for NOT */
+ if (cmd->len & F_NOT)
+ errx(EX_USAGE, "double \"not\" not allowed\n");
+ cmd->len = F_NOT;
+ s++;
+ }
+ i = match_token(rule_options, s);
+ ac--; av++;
+ switch(i) {
+ case TOK_NOT:
+ if (cmd->len & F_NOT)
+ errx(EX_USAGE, "double \"not\" not allowed\n");
+ cmd->len = F_NOT;
+ break;
+
+ case TOK_OR:
+ if (open_par == 0 || prev == NULL)
+ errx(EX_USAGE, "invalid \"or\" block\n");
+ prev->len |= F_OR;
+ break;
+
+ case TOK_STARTBRACE:
+ if (open_par)
+ errx(EX_USAGE, "+nested \"(\" not allowed\n");
+ open_par = 1;
+ break;
+
+ case TOK_ENDBRACE:
+ if (!open_par)
+ errx(EX_USAGE, "+missing \")\"\n");
+ open_par = 0;
+ prev = NULL;
+ break;
+
+ case TOK_IN:
+ fill_cmd(cmd, O_IN, 0, 0);
+ break;
+
+ case TOK_OUT:
+ cmd->len ^= F_NOT; /* toggle F_NOT */
+ fill_cmd(cmd, O_IN, 0, 0);
+ break;
+
+ case TOK_DIVERTED:
+ fill_cmd(cmd, O_DIVERTED, 0, 3);
+ break;
+
+ case TOK_DIVERTEDLOOPBACK:
+ fill_cmd(cmd, O_DIVERTED, 0, 1);
+ break;
+
+ case TOK_DIVERTEDOUTPUT:
+ fill_cmd(cmd, O_DIVERTED, 0, 2);
+ break;
+
+ case TOK_FRAG:
+ fill_cmd(cmd, O_FRAG, 0, 0);
+ break;
+
+ case TOK_LAYER2:
+ fill_cmd(cmd, O_LAYER2, 0, 0);
+ break;
+
+ case TOK_XMIT:
+ case TOK_RECV:
+ case TOK_VIA:
+ NEED1("recv, xmit, via require interface name"
+ " or address");
+ fill_iface((ipfw_insn_if *)cmd, av[0]);
+ ac--; av++;
+ if (F_LEN(cmd) == 0) /* not a valid address */
+ break;
+ if (i == TOK_XMIT)
+ cmd->opcode = O_XMIT;
+ else if (i == TOK_RECV)
+ cmd->opcode = O_RECV;
+ else if (i == TOK_VIA)
+ cmd->opcode = O_VIA;
+ break;
+
+ case TOK_ICMPTYPES:
+ NEED1("icmptypes requires list of types");
+ fill_icmptypes((ipfw_insn_u32 *)cmd, *av);
+ av++; ac--;
+ break;
+
+ case TOK_ICMP6TYPES:
+ NEED1("icmptypes requires list of types");
+ fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av);
+ av++; ac--;
+ break;
+
+ case TOK_IPTTL:
+ NEED1("ipttl requires TTL");
+ if (strpbrk(*av, "-,")) {
+ if (!add_ports(cmd, *av, 0, O_IPTTL))
+ errx(EX_DATAERR, "invalid ipttl %s", *av);
+ } else
+ fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
+ ac--; av++;
+ break;
+
+ case TOK_IPID:
+ NEED1("ipid requires id");
+ if (strpbrk(*av, "-,")) {
+ if (!add_ports(cmd, *av, 0, O_IPID))
+ errx(EX_DATAERR, "invalid ipid %s", *av);
+ } else
+ fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0));
+ ac--; av++;
+ break;
+
+ case TOK_IPLEN:
+ NEED1("iplen requires length");
+ if (strpbrk(*av, "-,")) {
+ if (!add_ports(cmd, *av, 0, O_IPLEN))
+ errx(EX_DATAERR, "invalid ip len %s", *av);
+ } else
+ fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0));
+ ac--; av++;
+ break;
+
+ case TOK_IPVER:
+ NEED1("ipver requires version");
+ fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0));
+ ac--; av++;
+ break;
+
+ case TOK_IPPRECEDENCE:
+ NEED1("ipprecedence requires value");
+ fill_cmd(cmd, O_IPPRECEDENCE, 0,
+ (strtoul(*av, NULL, 0) & 7) << 5);
+ ac--; av++;
+ break;
+
+ case TOK_IPOPTS:
+ NEED1("missing argument for ipoptions");
+ fill_flags(cmd, O_IPOPT, f_ipopts, *av);
+ ac--; av++;
+ break;
+
+ case TOK_IPTOS:
+ NEED1("missing argument for iptos");
+ fill_flags(cmd, O_IPTOS, f_iptos, *av);
+ ac--; av++;
+ break;
+
+ case TOK_UID:
+ NEED1("uid requires argument");
+ {
+ char *end;
+ uid_t uid;
+ struct passwd *pwd;
+
+ cmd->opcode = O_UID;
+ uid = strtoul(*av, &end, 0);
+ pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av);
+ if (pwd == NULL)
+ errx(EX_DATAERR, "uid \"%s\" nonexistent", *av);
+ cmd32->d[0] = pwd->pw_uid;
+ cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
+ ac--; av++;
+ }
+ break;
+
+ case TOK_GID:
+ NEED1("gid requires argument");
+ {
+ char *end;
+ gid_t gid;
+ struct group *grp;
+
+ cmd->opcode = O_GID;
+ gid = strtoul(*av, &end, 0);
+ grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av);
+ if (grp == NULL)
+ errx(EX_DATAERR, "gid \"%s\" nonexistent", *av);
+ cmd32->d[0] = grp->gr_gid;
+ cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
+ ac--; av++;
+ }
+ break;
+
+ case TOK_JAIL:
+ NEED1("jail requires argument");
+ {
+ char *end;
+ int jid;
+
+ cmd->opcode = O_JAIL;
+ jid = (int)strtol(*av, &end, 0);
+ if (jid < 0 || *end != '\0')
+ errx(EX_DATAERR, "jail requires prison ID");
+ cmd32->d[0] = (uint32_t)jid;
+ cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
+ ac--; av++;
+ }
+ break;
+
+ case TOK_ESTAB:
+ fill_cmd(cmd, O_ESTAB, 0, 0);
+ break;
+
+ case TOK_SETUP:
+ fill_cmd(cmd, O_TCPFLAGS, 0,
+ (TH_SYN) | ( (TH_ACK) & 0xff) <<8 );
+ break;
+
+ case TOK_TCPDATALEN:
+ NEED1("tcpdatalen requires length");
+ if (strpbrk(*av, "-,")) {
+ if (!add_ports(cmd, *av, 0, O_TCPDATALEN))
+ errx(EX_DATAERR, "invalid tcpdata len %s", *av);
+ } else
+ fill_cmd(cmd, O_TCPDATALEN, 0,
+ strtoul(*av, NULL, 0));
+ ac--; av++;
+ break;
+
+ case TOK_TCPOPTS:
+ NEED1("missing argument for tcpoptions");
+ fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av);
+ ac--; av++;
+ break;
+
+ case TOK_TCPSEQ:
+ case TOK_TCPACK:
+ NEED1("tcpseq/tcpack requires argument");
+ cmd->len = F_INSN_SIZE(ipfw_insn_u32);
+ cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK;
+ cmd32->d[0] = htonl(strtoul(*av, NULL, 0));
+ ac--; av++;
+ break;
+
+ case TOK_TCPWIN:
+ NEED1("tcpwin requires length");
+ fill_cmd(cmd, O_TCPWIN, 0,
+ htons(strtoul(*av, NULL, 0)));
+ ac--; av++;
+ break;
+
+ case TOK_TCPFLAGS:
+ NEED1("missing argument for tcpflags");
+ cmd->opcode = O_TCPFLAGS;
+ fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av);
+ ac--; av++;
+ break;
+
+ case TOK_KEEPSTATE:
+ if (open_par)
+ errx(EX_USAGE, "keep-state cannot be part "
+ "of an or block");
+ if (have_state)
+ errx(EX_USAGE, "only one of keep-state "
+ "and limit is allowed");
+ have_state = cmd;
+ fill_cmd(cmd, O_KEEP_STATE, 0, 0);
+ break;
+
+ case TOK_LIMIT:
+ if (open_par)
+ errx(EX_USAGE, "limit cannot be part "
+ "of an or block");
+ if (have_state)
+ errx(EX_USAGE, "only one of keep-state "
+ "and limit is allowed");
+ NEED1("limit needs mask and # of connections");
+ have_state = cmd;
+ {
+ ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
+
+ cmd->len = F_INSN_SIZE(ipfw_insn_limit);
+ cmd->opcode = O_LIMIT;
+ c->limit_mask = 0;
+ c->conn_limit = 0;
+ for (; ac >1 ;) {
+ int val;
+
+ val = match_token(limit_masks, *av);
+ if (val <= 0)
+ break;
+ c->limit_mask |= val;
+ ac--; av++;
+ }
+ c->conn_limit = atoi(*av);
+ if (c->conn_limit == 0)
+ errx(EX_USAGE, "limit: limit must be >0");
+ if (c->limit_mask == 0)
+ errx(EX_USAGE, "missing limit mask");
+ ac--; av++;
+ }
+ break;
+
+ case TOK_PROTO:
+ NEED1("missing protocol");
+ if (add_proto(cmd, *av, &proto)) {
+ ac--; av++;
+ } else
+ errx(EX_DATAERR, "invalid protocol ``%s''",
+ *av);
+ break;
+
+ case TOK_SRCIP:
+ NEED1("missing source IP");
+ if (add_srcip(cmd, *av)) {
+ ac--; av++;
+ }
+ break;
+
+ case TOK_DSTIP:
+ NEED1("missing destination IP");
+ if (add_dstip(cmd, *av)) {
+ ac--; av++;
+ }
+ break;
+
+ case TOK_SRCIP6:
+ NEED1("missing source IP6");
+ if (add_srcip6(cmd, *av)) {
+ ac--; av++;
+ }
+ break;
+
+ case TOK_DSTIP6:
+ NEED1("missing destination IP6");
+ if (add_dstip6(cmd, *av)) {
+ ac--; av++;
+ }
+ break;
+
+ case TOK_SRCPORT:
+ NEED1("missing source port");
+ if (_substrcmp(*av, "any") == 0 ||
+ add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
+ ac--; av++;
+ } else
+ errx(EX_DATAERR, "invalid source port %s", *av);
+ break;
+
+ case TOK_DSTPORT:
+ NEED1("missing destination port");
+ if (_substrcmp(*av, "any") == 0 ||
+ add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
+ ac--; av++;
+ } else
+ errx(EX_DATAERR, "invalid destination port %s",
+ *av);
+ break;
+
+ case TOK_MAC:
+ if (add_mac(cmd, ac, av)) {
+ ac -= 2; av += 2;
+ }
+ break;
+
+ case TOK_MACTYPE:
+ NEED1("missing mac type");
+ if (!add_mactype(cmd, ac, *av))
+ errx(EX_DATAERR, "invalid mac type %s", *av);
+ ac--; av++;
+ break;
+
+ case TOK_VERREVPATH:
+ fill_cmd(cmd, O_VERREVPATH, 0, 0);
+ break;
+
+ case TOK_VERSRCREACH:
+ fill_cmd(cmd, O_VERSRCREACH, 0, 0);
+ break;
+
+ case TOK_ANTISPOOF:
+ fill_cmd(cmd, O_ANTISPOOF, 0, 0);
+ break;
+
+ case TOK_IPSEC:
+ fill_cmd(cmd, O_IPSEC, 0, 0);
+ break;
+
+ case TOK_IPV6:
+ fill_cmd(cmd, O_IP6, 0, 0);
+ break;
+
+ case TOK_IPV4:
+ fill_cmd(cmd, O_IP4, 0, 0);
+ break;
+
+ case TOK_EXT6HDR:
+ fill_ext6hdr( cmd, *av );
+ ac--; av++;
+ break;
+
+ case TOK_FLOWID:
+ if (proto != IPPROTO_IPV6 )
+ errx( EX_USAGE, "flow-id filter is active "
+ "only for ipv6 protocol\n");
+ fill_flow6( (ipfw_insn_u32 *) cmd, *av );
+ ac--; av++;
+ break;
+
+ case TOK_COMMENT:
+ fill_comment(cmd, ac, av);
+ av += ac;
+ ac = 0;
+ break;
+
+ default:
+ errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
+ }
+ if (F_LEN(cmd) > 0) { /* prepare to advance */
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+ }
+
+done:
+ /*
+ * Now copy stuff into the rule.
+ * If we have a keep-state option, the first instruction
+ * must be a PROBE_STATE (which is generated here).
+ * If we have a LOG option, it was stored as the first command,
+ * and now must be moved to the top of the action part.
+ */
+ dst = (ipfw_insn *)rule->cmd;
+
+ /*
+ * First thing to write into the command stream is the match probability.
+ */
+ if (match_prob != 1) { /* 1 means always match */
+ dst->opcode = O_PROB;
+ dst->len = 2;
+ *((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff);
+ dst += dst->len;
+ }
+
+ /*
+ * generate O_PROBE_STATE if necessary
+ */
+ if (have_state && have_state->opcode != O_CHECK_STATE) {
+ fill_cmd(dst, O_PROBE_STATE, 0, 0);
+ dst = next_cmd(dst);
+ }
+ /*
+ * copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ
+ */
+ for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
+ i = F_LEN(src);
+
+ switch (src->opcode) {
+ case O_LOG:
+ case O_KEEP_STATE:
+ case O_LIMIT:
+ case O_ALTQ:
+ break;
+ default:
+ bcopy(src, dst, i * sizeof(uint32_t));
+ dst += i;
+ }
+ }
+
+ /*
+ * put back the have_state command as last opcode
+ */
+ if (have_state && have_state->opcode != O_CHECK_STATE) {
+ i = F_LEN(have_state);
+ bcopy(have_state, dst, i * sizeof(uint32_t));
+ dst += i;
+ }
+ /*
+ * start action section
+ */
+ rule->act_ofs = dst - rule->cmd;
+
+ /*
+ * put back O_LOG, O_ALTQ if necessary
+ */
+ if (have_log) {
+ i = F_LEN(have_log);
+ bcopy(have_log, dst, i * sizeof(uint32_t));
+ dst += i;
+ }
+ if (have_altq) {
+ i = F_LEN(have_altq);
+ bcopy(have_altq, dst, i * sizeof(uint32_t));
+ dst += i;
+ }
+ /*
+ * copy all other actions
+ */
+ for (src = (ipfw_insn *)actbuf; src != action; src += i) {
+ i = F_LEN(src);
+ bcopy(src, dst, i * sizeof(uint32_t));
+ dst += i;
+ }
+
+ rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd);
+ i = (char *)dst - (char *)rule;
+ if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1)
+ err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
+ if (!do_quiet)
+ show_ipfw(rule, 0, 0);
+}
+
+static void
+zero(int ac, char *av[], int optname /* IP_FW_ZERO or IP_FW_RESETLOG */)
+{
+ int rulenum;
+ int failed = EX_OK;
+ char const *name = optname == IP_FW_ZERO ? "ZERO" : "RESETLOG";
+
+ av++; ac--;
+
+ if (!ac) {
+ /* clear all entries */
+ if (do_cmd(optname, NULL, 0) < 0)
+ err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name);
+ if (!do_quiet)
+ printf("%s.\n", optname == IP_FW_ZERO ?
+ "Accounting cleared":"Logging counts reset");
+
+ return;
+ }
+
+ while (ac) {
+ /* Rule number */
+ if (isdigit(**av)) {
+ rulenum = atoi(*av);
+ av++;
+ ac--;
+ if (do_cmd(optname, &rulenum, sizeof rulenum)) {
+ warn("rule %u: setsockopt(IP_FW_%s)",
+ rulenum, name);
+ failed = EX_UNAVAILABLE;
+ } else if (!do_quiet)
+ printf("Entry %d %s.\n", rulenum,
+ optname == IP_FW_ZERO ?
+ "cleared" : "logging count reset");
+ } else {
+ errx(EX_USAGE, "invalid rule number ``%s''", *av);
+ }
+ }
+ if (failed != EX_OK)
+ exit(failed);
+}
+
+static void
+flush(int force)
+{
+ int cmd = do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH;
+
+ if (!force && !do_quiet) { /* need to ask user */
+ int c;
+
+ printf("Are you sure? [yn] ");
+ fflush(stdout);
+ do {
+ c = toupper(getc(stdin));
+ while (c != '\n' && getc(stdin) != '\n')
+ if (feof(stdin))
+ return; /* and do not flush */
+ } while (c != 'Y' && c != 'N');
+ printf("\n");
+ if (c == 'N') /* user said no */
+ return;
+ }
+ if (do_cmd(cmd, NULL, 0) < 0)
+ err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
+ do_pipe ? "DUMMYNET" : "FW");
+ if (!do_quiet)
+ printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules");
+}
+
+/*
+ * Free a the (locally allocated) copy of command line arguments.
+ */
+static void
+free_args(int ac, char **av)
+{
+ int i;
+
+ for (i=0; i < ac; i++)
+ free(av[i]);
+ free(av);
+}
+
+/*
+ * This one handles all table-related commands
+ * ipfw table N add addr[/masklen] [value]
+ * ipfw table N delete addr[/masklen]
+ * ipfw table N flush
+ * ipfw table N list
+ */
+static void
+table_handler(int ac, char *av[])
+{
+ ipfw_table_entry ent;
+ ipfw_table *tbl;
+ int do_add;
+ char *p;
+ socklen_t l;
+ uint32_t a;
+
+ ac--; av++;
+ if (ac && isdigit(**av)) {
+ ent.tbl = atoi(*av);
+ ac--; av++;
+ } else
+ errx(EX_USAGE, "table number required");
+ NEED1("table needs command");
+ if (_substrcmp(*av, "add") == 0 ||
+ _substrcmp(*av, "delete") == 0) {
+ do_add = **av == 'a';
+ ac--; av++;
+ if (!ac)
+ errx(EX_USAGE, "IP address required");
+ p = strchr(*av, '/');
+ if (p) {
+ *p++ = '\0';
+ ent.masklen = atoi(p);
+ if (ent.masklen > 32)
+ errx(EX_DATAERR, "bad width ``%s''", p);
+ } else
+ ent.masklen = 32;
+ if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0)
+ errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
+ ac--; av++;
+ if (do_add && ac)
+ ent.value = strtoul(*av, NULL, 0);
+ else
+ ent.value = 0;
+ if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL,
+ &ent, sizeof(ent)) < 0)
+ err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
+ do_add ? "ADD" : "DEL");
+ } else if (_substrcmp(*av, "flush") == 0) {
+ if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl, sizeof(ent.tbl)) < 0)
+ err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
+ } else if (_substrcmp(*av, "list") == 0) {
+ a = ent.tbl;
+ l = sizeof(a);
+ if (do_cmd(IP_FW_TABLE_GETSIZE, &a, (uintptr_t)&l) < 0)
+ err(EX_OSERR, "getsockopt(IP_FW_TABLE_GETSIZE)");
+ l = sizeof(*tbl) + a * sizeof(ipfw_table_entry);
+ tbl = malloc(l);
+ if (tbl == NULL)
+ err(EX_OSERR, "malloc");
+ tbl->tbl = ent.tbl;
+ if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0)
+ err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)");
+ for (a = 0; a < tbl->cnt; a++) {
+ printf("%s/%u %u\n",
+ inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
+ tbl->ent[a].masklen, tbl->ent[a].value);
+ }
+ } else
+ errx(EX_USAGE, "invalid table command %s", *av);
+}
+
+/*
+ * Called with the arguments (excluding program name).
+ * Returns 0 if successful, 1 if empty command, errx() in case of errors.
+ */
+static int
+ipfw_main(int oldac, char **oldav)
+{
+ int ch, ac, save_ac;
+ char **av, **save_av;
+ int do_acct = 0; /* Show packet/byte count */
+
+#define WHITESP " \t\f\v\n\r"
+ if (oldac == 0)
+ return 1;
+ else if (oldac == 1) {
+ /*
+ * If we are called with a single string, try to split it into
+ * arguments for subsequent parsing.
+ * But first, remove spaces after a ',', by copying the string
+ * in-place.
+ */
+ char *arg = oldav[0]; /* The string... */
+ int l = strlen(arg);
+ int copy = 0; /* 1 if we need to copy, 0 otherwise */
+ int i, j;
+ for (i = j = 0; i < l; i++) {
+ if (arg[i] == '#') /* comment marker */
+ break;
+ if (copy) {
+ arg[j++] = arg[i];
+ copy = !index("," WHITESP, arg[i]);
+ } else {
+ copy = !index(WHITESP, arg[i]);
+ if (copy)
+ arg[j++] = arg[i];
+ }
+ }
+ if (!copy && j > 0) /* last char was a 'blank', remove it */
+ j--;
+ l = j; /* the new argument length */
+ arg[j++] = '\0';
+ if (l == 0) /* empty string! */
+ return 1;
+
+ /*
+ * First, count number of arguments. Because of the previous
+ * processing, this is just the number of blanks plus 1.
+ */
+ for (i = 0, ac = 1; i < l; i++)
+ if (index(WHITESP, arg[i]) != NULL)
+ ac++;
+
+ av = calloc(ac, sizeof(char *));
+
+ /*
+ * Second, copy arguments from cmd[] to av[]. For each one,
+ * j is the initial character, i is the one past the end.
+ */
+ for (ac = 0, i = j = 0; i < l; i++)
+ if (index(WHITESP, arg[i]) != NULL || i == l-1) {
+ if (i == l-1)
+ i++;
+ av[ac] = calloc(i-j+1, 1);
+ bcopy(arg+j, av[ac], i-j);
+ ac++;
+ j = i + 1;
+ }
+ } else {
+ /*
+ * If an argument ends with ',' join with the next one.
+ */
+ int first, i, l;
+
+ av = calloc(oldac, sizeof(char *));
+ for (first = i = ac = 0, l = 0; i < oldac; i++) {
+ char *arg = oldav[i];
+ int k = strlen(arg);
+
+ l += k;
+ if (arg[k-1] != ',' || i == oldac-1) {
+ /* Time to copy. */
+ av[ac] = calloc(l+1, 1);
+ for (l=0; first <= i; first++) {
+ strcat(av[ac]+l, oldav[first]);
+ l += strlen(oldav[first]);
+ }
+ ac++;
+ l = 0;
+ first = i+1;
+ }
+ }
+ }
+
+ /* Set the force flag for non-interactive processes */
+ if (!do_force)
+ do_force = !isatty(STDIN_FILENO);
+
+ /* Save arguments for final freeing of memory. */
+ save_ac = ac;
+ save_av = av;
+
+ optind = optreset = 0;
+ while ((ch = getopt(ac, av, "abcdefhnNqs:STtv")) != -1)
+ switch (ch) {
+ case 'a':
+ do_acct = 1;
+ break;
+
+ case 'b':
+ comment_only = 1;
+ do_compact = 1;
+ break;
+
+ case 'c':
+ do_compact = 1;
+ break;
+
+ case 'd':
+ do_dynamic = 1;
+ break;
+
+ case 'e':
+ do_expired = 1;
+ break;
+
+ case 'f':
+ do_force = 1;
+ break;
+
+ case 'h': /* help */
+ free_args(save_ac, save_av);
+ help();
+ break; /* NOTREACHED */
+
+ case 'n':
+ test_only = 1;
+ break;
+
+ case 'N':
+ do_resolv = 1;
+ break;
+
+ case 'q':
+ do_quiet = 1;
+ break;
+
+ case 's': /* sort */
+ do_sort = atoi(optarg);
+ break;
+
+ case 'S':
+ show_sets = 1;
+ break;
+
+ case 't':
+ do_time = 1;
+ break;
+
+ case 'T':
+ do_time = 2; /* numeric timestamp */
+ break;
+
+ case 'v': /* verbose */
+ verbose = 1;
+ break;
+
+ default:
+ free_args(save_ac, save_av);
+ return 1;
+ }
+
+ ac -= optind;
+ av += optind;
+ NEED1("bad arguments, for usage summary ``ipfw''");
+
+ /*
+ * An undocumented behaviour of ipfw1 was to allow rule numbers first,
+ * e.g. "100 add allow ..." instead of "add 100 allow ...".
+ * In case, swap first and second argument to get the normal form.
+ */
+ if (ac > 1 && isdigit(*av[0])) {
+ char *p = av[0];
+
+ av[0] = av[1];
+ av[1] = p;
+ }
+
+ /*
+ * optional: pipe or queue
+ */
+ do_pipe = 0;
+ if (_substrcmp(*av, "pipe") == 0)
+ do_pipe = 1;
+ else if (_substrcmp(*av, "queue") == 0)
+ do_pipe = 2;
+ if (do_pipe) {
+ ac--;
+ av++;
+ }
+ NEED1("missing command");
+
+ /*
+ * For pipes and queues we normally say 'pipe NN config'
+ * but the code is easier to parse as 'pipe config NN'
+ * so we swap the two arguments.
+ */
+ if (do_pipe > 0 && ac > 1 && isdigit(*av[0])) {
+ char *p = av[0];
+
+ av[0] = av[1];
+ av[1] = p;
+ }
+
+ if (_substrcmp(*av, "add") == 0)
+ add(ac, av);
+ else if (do_pipe && _substrcmp(*av, "config") == 0)
+ config_pipe(ac, av);
+ else if (_substrcmp(*av, "delete") == 0)
+ delete(ac, av);
+ else if (_substrcmp(*av, "flush") == 0)
+ flush(do_force);
+ else if (_substrcmp(*av, "zero") == 0)
+ zero(ac, av, IP_FW_ZERO);
+ else if (_substrcmp(*av, "resetlog") == 0)
+ zero(ac, av, IP_FW_RESETLOG);
+ else if (_substrcmp(*av, "print") == 0 ||
+ _substrcmp(*av, "list") == 0)
+ list(ac, av, do_acct);
+ else if (_substrcmp(*av, "set") == 0)
+ sets_handler(ac, av);
+ else if (_substrcmp(*av, "table") == 0)
+ table_handler(ac, av);
+ else if (_substrcmp(*av, "enable") == 0)
+ sysctl_handler(ac, av, 1);
+ else if (_substrcmp(*av, "disable") == 0)
+ sysctl_handler(ac, av, 0);
+ else if (_substrcmp(*av, "show") == 0)
+ list(ac, av, 1 /* show counters */);
+ else
+ errx(EX_USAGE, "bad command `%s'", *av);
+
+ /* Free memory allocated in the argument parsing. */
+ free_args(save_ac, save_av);
+ return 0;
+}
+
+
+static void
+ipfw_readfile(int ac, char *av[])
+{
+#define MAX_ARGS 32
+ char buf[BUFSIZ];
+ char *cmd = NULL, *filename = av[ac-1];
+ int c, lineno=0;
+ FILE *f = NULL;
+ pid_t preproc = 0;
+
+ filename = av[ac-1];
+
+ while ((c = getopt(ac, av, "cfNnp:qS")) != -1) {
+ switch(c) {
+ case 'c':
+ do_compact = 1;
+ break;
+
+ case 'f':
+ do_force = 1;
+ break;
+
+ case 'N':
+ do_resolv = 1;
+ break;
+
+ case 'n':
+ test_only = 1;
+ break;
+
+ case 'p':
+ cmd = optarg;
+ /*
+ * Skip previous args and delete last one, so we
+ * pass all but the last argument to the preprocessor
+ * via av[optind-1]
+ */
+ av += optind - 1;
+ ac -= optind - 1;
+ av[ac-1] = NULL;
+ fprintf(stderr, "command is %s\n", av[0]);
+ break;
+
+ case 'q':
+ do_quiet = 1;
+ break;
+
+ case 'S':
+ show_sets = 1;
+ break;
+
+ default:
+ errx(EX_USAGE, "bad arguments, for usage"
+ " summary ``ipfw''");
+ }
+
+ if (cmd != NULL)
+ break;
+ }
+
+ if (cmd == NULL && ac != optind + 1) {
+ fprintf(stderr, "ac %d, optind %d\n", ac, optind);
+ errx(EX_USAGE, "extraneous filename arguments");
+ }
+
+ if ((f = fopen(filename, "r")) == NULL)
+ err(EX_UNAVAILABLE, "fopen: %s", filename);
+
+ if (cmd != NULL) { /* pipe through preprocessor */
+ int pipedes[2];
+
+ if (pipe(pipedes) == -1)
+ err(EX_OSERR, "cannot create pipe");
+
+ preproc = fork();
+ if (preproc == -1)
+ err(EX_OSERR, "cannot fork");
+
+ if (preproc == 0) {
+ /*
+ * Child, will run the preprocessor with the
+ * file on stdin and the pipe on stdout.
+ */
+ if (dup2(fileno(f), 0) == -1
+ || dup2(pipedes[1], 1) == -1)
+ err(EX_OSERR, "dup2()");
+ fclose(f);
+ close(pipedes[1]);
+ close(pipedes[0]);
+ execvp(cmd, av);
+ err(EX_OSERR, "execvp(%s) failed", cmd);
+ } else { /* parent, will reopen f as the pipe */
+ fclose(f);
+ close(pipedes[1]);
+ if ((f = fdopen(pipedes[0], "r")) == NULL) {
+ int savederrno = errno;
+
+ (void)kill(preproc, SIGTERM);
+ errno = savederrno;
+ err(EX_OSERR, "fdopen()");
+ }
+ }
+ }
+
+ while (fgets(buf, BUFSIZ, f)) { /* read commands */
+ char linename[10];
+ char *args[1];
+
+ lineno++;
+ sprintf(linename, "Line %d", lineno);
+ setprogname(linename); /* XXX */
+ args[0] = buf;
+ ipfw_main(1, args);
+ }
+ fclose(f);
+ if (cmd != NULL) {
+ int status;
+
+ if (waitpid(preproc, &status, 0) == -1)
+ errx(EX_OSERR, "waitpid()");
+ if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
+ errx(EX_UNAVAILABLE,
+ "preprocessor exited with status %d",
+ WEXITSTATUS(status));
+ else if (WIFSIGNALED(status))
+ errx(EX_UNAVAILABLE,
+ "preprocessor exited with signal %d",
+ WTERMSIG(status));
+ }
+}
+
+int
+main(int ac, char *av[])
+{
+ /*
+ * If the last argument is an absolute pathname, interpret it
+ * as a file to be preprocessed.
+ */
+
+ if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0)
+ ipfw_readfile(ac, av);
+ else {
+ if (ipfw_main(ac-1, av+1))
+ show_usage();
+ }
+ return EX_OK;
+}
diff --git a/sbin/kldconfig/Makefile b/sbin/kldconfig/Makefile
new file mode 100644
index 0000000..e92daf6
--- /dev/null
+++ b/sbin/kldconfig/Makefile
@@ -0,0 +1,33 @@
+#
+# Copyright (c) 2001 Peter Pentchev
+# 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.
+#
+# $FreeBSD$
+#
+
+PROG= kldconfig
+MAN= kldconfig.8
+WARNS?= 5
+
+.include <bsd.prog.mk>
diff --git a/sbin/kldconfig/kldconfig.8 b/sbin/kldconfig/kldconfig.8
new file mode 100644
index 0000000..18fbaf6
--- /dev/null
+++ b/sbin/kldconfig/kldconfig.8
@@ -0,0 +1,108 @@
+.\"
+.\" Copyright (c) 2001 Peter Pentchev
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 15, 2001
+.Dt KLDCONFIG 8
+.Os
+.Sh NAME
+.Nm kldconfig
+.Nd display or modify the kernel module search path
+.Sh SYNOPSIS
+.Nm
+.Op Fl dfimnUv
+.Op Fl S Ar sysctlname
+.Op Ar path ...
+.Nm
+.Fl r
+.Sh DESCRIPTION
+The
+.Nm
+utility
+displays or modifies the search path used by the kernel when loading modules
+using the
+.Xr kldload 8
+utility or the
+.Xr kldload 2
+syscall.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Remove the specified paths from the module search path.
+.It Fl f
+Do not display a diagnostic message if a path specified for adding is
+already present in the search path, or if a path specified for removing
+is not present in the search path.
+This may be useful in startup/shutdown scripts for adding a path to
+a file system which is still not mounted, or in shutdown scripts for
+unconditionally removing a path that may have been added during startup.
+.It Fl i
+Add the specified paths to the beginning of the search path, not to the end.
+This option can only be used when adding paths.
+.It Fl m
+Instead of replacing the module search path with the set of paths
+specified,
+.Dq merge
+in the new entries.
+.It Fl n
+Do not actually change the module search path.
+.It Fl r
+Display the current search path.
+This option cannot be used if any paths are also specified.
+.It Fl S Ar sysctlname
+Specify the sysctl name to use instead of the default
+.Va kern.module_path .
+.It Fl U
+.Dq Unique-ify
+the current search path - if any of the directories is repeated one
+or more times, only the first occurrence remains.
+This option implies
+.Fl m .
+.It Fl v
+Verbose output: display the new module search path.
+If the path has been changed, and the
+.Fl v
+flag is specified more than once, the old path is displayed as well.
+.El
+.Sh FILES
+.Bl -tag -width indent
+.It Pa /boot/kernel , /boot/modules , /modules
+The default module search path used by the kernel.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr kldload 2 ,
+.Xr kldload 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 4.4 .
+.Sh AUTHORS
+.An Peter Pentchev Aq roam@FreeBSD.org
diff --git a/sbin/kldconfig/kldconfig.c b/sbin/kldconfig/kldconfig.c
new file mode 100644
index 0000000..403251d
--- /dev/null
+++ b/sbin/kldconfig/kldconfig.c
@@ -0,0 +1,443 @@
+/*
+ * Copyright (c) 2001 Peter Pentchev
+ * 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#if defined(__FreeBSD_version)
+#if __FreeBSD_version < 500000
+#define NEED_SLASHTERM
+#endif /* < 500000 */
+#else /* defined(__FreeBSD_version) */
+/* just in case.. */
+#define NEED_SLASHTERM
+#endif /* defined(__FreeBSD_version) */
+
+/* the default sysctl name */
+#define PATHCTL "kern.module_path"
+
+/* queue structure for the module path broken down into components */
+TAILQ_HEAD(pathhead, pathentry);
+struct pathentry {
+ char *path;
+ TAILQ_ENTRY(pathentry) next;
+};
+
+/* the Management Information Base entries for the search path sysctl */
+static int mib[5];
+static size_t miblen;
+/* the sysctl name, defaults to PATHCTL */
+static char *pathctl;
+/* the sysctl value - the current module search path */
+static char *modpath;
+/* flag whether user actions require changing the sysctl value */
+static int changed;
+
+/* Top-level path management functions */
+static void addpath(struct pathhead *, char *, int, int);
+static void rempath(struct pathhead *, char *, int, int);
+static void showpath(struct pathhead *);
+
+/* Low-level path management functions */
+static char *qstring(struct pathhead *);
+
+/* sysctl-related functions */
+static void getmib(void);
+static void getpath(void);
+static void parsepath(struct pathhead *, char *, int);
+static void setpath(struct pathhead *);
+
+static void usage(void);
+
+/* Get the MIB entry for our sysctl */
+static void
+getmib(void)
+{
+
+ /* have we already fetched it? */
+ if (miblen != 0)
+ return;
+
+ miblen = sizeof(mib) / sizeof(mib[0]);
+ if (sysctlnametomib(pathctl, mib, &miblen) != 0)
+ err(1, "sysctlnametomib(%s)", pathctl);
+}
+
+/* Get the current module search path */
+static void
+getpath(void)
+{
+ char *path;
+ size_t sz;
+
+ if (modpath != NULL) {
+ free(modpath);
+ modpath = NULL;
+ }
+
+ if (miblen == 0)
+ getmib();
+ if (sysctl(mib, miblen, NULL, &sz, NULL, 0) == -1)
+ err(1, "getting path: sysctl(%s) - size only", pathctl);
+ if ((path = malloc(sz + 1)) == NULL) {
+ errno = ENOMEM;
+ err(1, "allocating %lu bytes for the path",
+ (unsigned long)sz+1);
+ }
+ if (sysctl(mib, miblen, path, &sz, NULL, 0) == -1)
+ err(1, "getting path: sysctl(%s)", pathctl);
+ modpath = path;
+}
+
+/* Set the module search path after changing it */
+static void
+setpath(struct pathhead *pathq)
+{
+ char *newpath;
+
+ if (miblen == 0)
+ getmib();
+ if ((newpath = qstring(pathq)) == NULL) {
+ errno = ENOMEM;
+ err(1, "building path string");
+ }
+ if (sysctl(mib, miblen, NULL, NULL, newpath, strlen(newpath)+1) == -1)
+ err(1, "setting path: sysctl(%s)", pathctl);
+
+ if (modpath != NULL)
+ free(modpath);
+ modpath = newpath;
+}
+
+/* Add/insert a new component to the module search path */
+static void
+addpath(struct pathhead *pathq, char *path, int force, int insert)
+{
+ struct pathentry *pe, *pskip;
+ char pathbuf[MAXPATHLEN+1];
+ size_t len;
+ static unsigned added = 0;
+ unsigned i;
+
+ /*
+ * If the path exists, use it; otherwise, take the user-specified
+ * path at face value - may be a removed directory.
+ */
+ if (realpath(path, pathbuf) == NULL)
+ strlcpy(pathbuf, path, sizeof(pathbuf));
+
+ len = strlen(pathbuf);
+#ifdef NEED_SLASHTERM
+ /* slash-terminate, because the kernel linker said so. */
+ if ((len == 0) || (pathbuf[len-1] != '/')) {
+ if (len == sizeof(pathbuf) - 1)
+ errx(1, "path too long: %s", pathbuf);
+ pathbuf[len] = '/';
+ }
+#else /* NEED_SLASHTERM */
+ /* remove a terminating slash if present */
+ if ((len > 0) && (pathbuf[len-1] == '/'))
+ pathbuf[--len] = '\0';
+#endif /* NEED_SLASHTERM */
+
+ /* is it already in there? */
+ TAILQ_FOREACH(pe, pathq, next)
+ if (!strcmp(pe->path, pathbuf))
+ break;
+ if (pe != NULL) {
+ if (force)
+ return;
+ errx(1, "already in the module search path: %s", pathbuf);
+ }
+
+ /* OK, allocate and add it. */
+ if (((pe = malloc(sizeof(*pe))) == NULL) ||
+ ((pe->path = strdup(pathbuf)) == NULL)) {
+ errno = ENOMEM;
+ err(1, "allocating path component");
+ }
+ if (!insert) {
+ TAILQ_INSERT_TAIL(pathq, pe, next);
+ } else {
+ for (i = 0, pskip = TAILQ_FIRST(pathq); i < added; i++)
+ pskip = TAILQ_NEXT(pskip, next);
+ if (pskip != NULL)
+ TAILQ_INSERT_BEFORE(pskip, pe, next);
+ else
+ TAILQ_INSERT_TAIL(pathq, pe, next);
+ added++;
+ }
+ changed = 1;
+}
+
+/* Remove a path component from the module search path */
+static void
+rempath(struct pathhead *pathq, char *path, int force, int insert __unused)
+{
+ char pathbuf[MAXPATHLEN+1];
+ struct pathentry *pe;
+ size_t len;
+
+ /* same logic as in addpath() */
+ if (realpath(path, pathbuf) == NULL)
+ strlcpy(pathbuf, path, sizeof(pathbuf));
+
+ len = strlen(pathbuf);
+#ifdef NEED_SLASHTERM
+ /* slash-terminate, because the kernel linker said so. */
+ if ((len == 0) || (pathbuf[len-1] != '/')) {
+ if (len == sizeof(pathbuf) - 1)
+ errx(1, "path too long: %s", pathbuf);
+ pathbuf[len] = '/';
+ }
+#else /* NEED_SLASHTERM */
+ /* remove a terminating slash if present */
+ if ((len > 0) && (pathbuf[len-1] == '/'))
+ pathbuf[--len] = '\0';
+#endif /* NEED_SLASHTERM */
+
+ /* Is it in there? */
+ TAILQ_FOREACH(pe, pathq, next)
+ if (!strcmp(pe->path, pathbuf))
+ break;
+ if (pe == NULL) {
+ if (force)
+ return;
+ errx(1, "not in module search path: %s", pathbuf);
+ }
+
+ /* OK, remove it now.. */
+ TAILQ_REMOVE(pathq, pe, next);
+ changed = 1;
+}
+
+/* Display the retrieved module search path */
+static void
+showpath(struct pathhead *pathq)
+{
+ char *s;
+
+ if ((s = qstring(pathq)) == NULL) {
+ errno = ENOMEM;
+ err(1, "building path string");
+ }
+ printf("%s\n", s);
+ free(s);
+}
+
+/* Break a string down into path components, store them into a queue */
+static void
+parsepath(struct pathhead *pathq, char *path, int uniq)
+{
+ char *p;
+ struct pathentry *pe;
+
+ while ((p = strsep(&path, ";")) != NULL)
+ if (!uniq) {
+ if (((pe = malloc(sizeof(*pe))) == NULL) ||
+ ((pe->path = strdup(p)) == NULL)) {
+ errno = ENOMEM;
+ err(1, "allocating path element");
+ }
+ TAILQ_INSERT_TAIL(pathq, pe, next);
+ } else {
+ addpath(pathq, p, 1, 0);
+ }
+}
+
+/* Recreate a path string from a components queue */
+static char *
+qstring(struct pathhead *pathq)
+{
+ char *s, *p;
+ struct pathentry *pe;
+
+ s = strdup("");
+ TAILQ_FOREACH(pe, pathq, next) {
+ asprintf(&p, "%s%s%s",
+ s, pe->path, (TAILQ_NEXT(pe, next) != NULL? ";": ""));
+ free(s);
+ if (p == NULL)
+ return (NULL);
+ s = p;
+ }
+
+ return (s);
+}
+
+/* Usage message */
+static void
+usage(void)
+{
+
+ fprintf(stderr, "%s\n%s\n",
+ "usage:\tkldconfig [-dfimnUv] [-S sysctlname] [path ...]",
+ "\tkldconfig -r");
+ exit(1);
+}
+
+/* Main function */
+int
+main(int argc, char *argv[])
+{
+ /* getopt() iterator */
+ int c;
+ /* iterator over argv[] path components */
+ int i;
+ /* Command-line flags: */
+ /* "-f" - no diagnostic messages */
+ int fflag;
+ /* "-i" - insert before the first element */
+ int iflag;
+ /* "-m" - merge into the existing path, do not replace it */
+ int mflag;
+ /* "-n" - do not actually set the new module path */
+ int nflag;
+ /* "-r" - print out the current search path */
+ int rflag;
+ /* "-U" - remove duplicate values from the path */
+ int uniqflag;
+ /* "-v" - verbose operation (currently a no-op) */
+ int vflag;
+ /* The higher-level function to call - add/remove */
+ void (*act)(struct pathhead *, char *, int, int);
+ /* The original path */
+ char *origpath;
+ /* The module search path broken down into components */
+ struct pathhead pathq;
+
+ fflag = iflag = mflag = nflag = rflag = uniqflag = vflag = 0;
+ act = addpath;
+ origpath = NULL;
+ if ((pathctl = strdup(PATHCTL)) == NULL) {
+ /* this is just too paranoid ;) */
+ errno = ENOMEM;
+ err(1, "initializing sysctl name %s", PATHCTL);
+ }
+
+ /* If no arguments and no options are specified, force '-m' */
+ if (argc == 1)
+ mflag = 1;
+
+ while ((c = getopt(argc, argv, "dfimnrS:Uv")) != -1)
+ switch (c) {
+ case 'd':
+ if (iflag || mflag)
+ usage();
+ act = rempath;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'i':
+ if (act != addpath)
+ usage();
+ iflag = 1;
+ break;
+ case 'm':
+ if (act != addpath)
+ usage();
+ mflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 'S':
+ free(pathctl);
+ if ((pathctl = strdup(optarg)) == NULL) {
+ errno = ENOMEM;
+ err(1, "sysctl name %s", optarg);
+ }
+ break;
+ case 'U':
+ uniqflag = 1;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* The '-r' flag cannot be used when paths are also specified */
+ if (rflag && (argc > 0))
+ usage();
+
+ TAILQ_INIT(&pathq);
+
+ /* Retrieve and store the path from the sysctl value */
+ getpath();
+ if ((origpath = strdup(modpath)) == NULL) {
+ errno = ENOMEM;
+ err(1, "saving the original search path");
+ }
+
+ /*
+ * Break down the path into the components queue if:
+ * - we are NOT adding paths, OR
+ * - the 'merge' flag is specified, OR
+ * - the 'print only' flag is specified, OR
+ * - the 'unique' flag is specified.
+ */
+ if ((act != addpath) || mflag || rflag || uniqflag)
+ parsepath(&pathq, modpath, uniqflag);
+ else if (modpath[0] != '\0')
+ changed = 1;
+
+ /* Process the path arguments */
+ for (i = 0; i < argc; i++)
+ act(&pathq, argv[i], fflag, iflag);
+
+ if (changed && !nflag)
+ setpath(&pathq);
+
+ if (rflag || (changed && vflag)) {
+ if (changed && (vflag > 1))
+ printf("%s -> ", origpath);
+ showpath(&pathq);
+ }
+
+ return (0);
+}
diff --git a/sbin/kldload/Makefile b/sbin/kldload/Makefile
new file mode 100644
index 0000000..da597d4
--- /dev/null
+++ b/sbin/kldload/Makefile
@@ -0,0 +1,33 @@
+#
+# Copyright (c) 1997 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.
+#
+# $FreeBSD$
+#
+
+PROG= kldload
+MAN= kldload.8
+WARNS?= 5
+
+.include <bsd.prog.mk>
diff --git a/sbin/kldload/kldload.8 b/sbin/kldload/kldload.8
new file mode 100644
index 0000000..aaba5f7
--- /dev/null
+++ b/sbin/kldload/kldload.8
@@ -0,0 +1,90 @@
+.\"
+.\" Copyright (c) 1997 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 25, 1997
+.Dt KLDLOAD 8
+.Os
+.Sh NAME
+.Nm kldload
+.Nd load a file into the kernel
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility loads
+.Ar file Ns Pa .ko
+into the kernel using the kernel linker.
+Note that if multiple modules are specified then an attempt will
+be made to load them all, even if some fail.
+The
+.Pa .ko
+extension name is not mandatory when loading a given module
+using
+.Nm .
+It does not hurt to specify it though.
+.Pp
+The following option is available:
+.Bl -tag -width indent
+.It Fl v
+Be more verbose.
+.El
+.Sh FILES
+.Bl -tag -width /boot/kernel -compact
+.It Pa /boot/kernel
+directory containing loadable modules.
+Modules must have an extension of
+.Pa .ko .
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh AUTOMATICALLY LOADING MODULES
+Some modules (lomac, pf, ipfw, ipf, etc.) may be automatically loaded at boot
+time when the corresponding
+.Xr rc.conf 5
+statement is used.
+Modules may also be auto-loaded through their addition to
+.Xr loader.conf 5 .
+.Sh SEE ALSO
+.Xr kldload 2 ,
+.Xr loader.conf 5 ,
+.Xr rc.conf 5 ,
+.Xr kldconfig 8 ,
+.Xr kldstat 8 ,
+.Xr kldunload 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 3.0 ,
+replacing the
+.Xr lkm 4
+interface.
+.Sh AUTHORS
+.An Doug Rabson Aq dfr@FreeBSD.org
diff --git a/sbin/kldload/kldload.c b/sbin/kldload/kldload.c
new file mode 100644
index 0000000..9e841bc
--- /dev/null
+++ b/sbin/kldload/kldload.c
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 1997 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: kldload [-v] file ...\n");
+ exit(1);
+}
+
+int
+main(int argc, char** argv)
+{
+ int c;
+ int errors;
+ int fileid;
+ int verbose;
+
+ errors = 0;
+ verbose = 0;
+
+ while ((c = getopt(argc, argv, "v")) != -1)
+ switch (c) {
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage();
+
+ while (argc-- != 0) {
+ fileid = kldload(argv[0]);
+ if (fileid < 0) {
+ warn("can't load %s", argv[0]);
+ errors++;
+ } else
+ if (verbose)
+ printf("Loaded %s, id=%d\n", argv[0], fileid);
+ argv++;
+ }
+
+ return errors ? 1 : 0;
+}
diff --git a/sbin/kldstat/Makefile b/sbin/kldstat/Makefile
new file mode 100644
index 0000000..b602a3e
--- /dev/null
+++ b/sbin/kldstat/Makefile
@@ -0,0 +1,33 @@
+#
+# Copyright (c) 1997 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.
+#
+# $FreeBSD$
+#
+
+PROG= kldstat
+MAN= kldstat.8
+WARNS?= 5
+
+.include <bsd.prog.mk>
diff --git a/sbin/kldstat/kldstat.8 b/sbin/kldstat/kldstat.8
new file mode 100644
index 0000000..3500839
--- /dev/null
+++ b/sbin/kldstat/kldstat.8
@@ -0,0 +1,76 @@
+.\"
+.\" Copyright (c) 1997 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 23, 2005
+.Dt KLDSTAT 8
+.Os
+.Sh NAME
+.Nm kldstat
+.Nd display status of dynamic kernel linker
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Op Fl i Ar id
+.Op Fl n Ar filename
+.Nm
+.Op Fl q
+.Op Fl m Ar modname
+.Sh DESCRIPTION
+The
+.Nm
+utility displays the status of any files dynamically linked into the
+kernel.
+.Pp
+The following options are available:
+.Bl -tag -width indentXX
+.It Fl v
+Be more verbose.
+.It Fl i Ar id
+Display the status of only the file with this ID.
+.It Fl n Ar filename
+Display the status of only the file with this filename.
+.It Fl q
+Only check if module is loaded or compiled into the kernel.
+.It Fl m Ar modname
+Display the status of only the module with this modname.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr kldstat 2 ,
+.Xr kldload 8 ,
+.Xr kldunload 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 3.0 ,
+replacing the
+.Xr lkm 4
+interface.
+.Sh AUTHORS
+.An Doug Rabson Aq dfr@FreeBSD.org
diff --git a/sbin/kldstat/kldstat.c b/sbin/kldstat/kldstat.c
new file mode 100644
index 0000000..74f375e
--- /dev/null
+++ b/sbin/kldstat/kldstat.c
@@ -0,0 +1,159 @@
+/*-
+ * Copyright (c) 1997 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/module.h>
+#include <sys/linker.h>
+
+#define POINTER_WIDTH ((int)(sizeof(void *) * 2 + 2))
+
+static void
+printmod(int modid)
+{
+ struct module_stat stat;
+
+ stat.version = sizeof(struct module_stat);
+ if (modstat(modid, &stat) < 0)
+ warn("can't stat module id %d", modid);
+ else
+ printf("\t\t%2d %s\n", stat.id, stat.name);
+}
+
+static void printfile(int fileid, int verbose)
+{
+ struct kld_file_stat stat;
+ int modid;
+
+ stat.version = sizeof(struct kld_file_stat);
+ if (kldstat(fileid, &stat) < 0)
+ warn("can't stat file id %d", fileid);
+ else
+ printf("%2d %4d %p %-8jx %s\n",
+ stat.id, stat.refs, stat.address, (uintmax_t)stat.size,
+ stat.name);
+
+ if (verbose) {
+ printf("\tContains modules:\n");
+ printf("\t\tId Name\n");
+ for (modid = kldfirstmod(fileid); modid > 0;
+ modid = modfnext(modid))
+ printmod(modid);
+ }
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: kldstat [-v] [-i id] [-n filename]\n");
+ fprintf(stderr, " kldstat [-q] [-m modname]\n");
+ exit(1);
+}
+
+int
+main(int argc, char** argv)
+{
+ int c;
+ int verbose = 0;
+ int fileid = 0;
+ int quiet = 0;
+ char* filename = NULL;
+ char* modname = NULL;
+ char* p;
+
+ while ((c = getopt(argc, argv, "i:m:n:qv")) != -1)
+ switch (c) {
+ case 'i':
+ fileid = (int)strtoul(optarg, &p, 10);
+ if (*p != '\0')
+ usage();
+ break;
+ case 'm':
+ modname = optarg;
+ break;
+ case 'n':
+ filename = optarg;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 0)
+ usage();
+
+ if (modname != NULL) {
+ int modid;
+ struct module_stat stat;
+
+ if ((modid = modfind(modname)) < 0) {
+ if (!quiet)
+ warn("can't find module %s", modname);
+ return 1;
+ } else if (quiet) {
+ return 0;
+ }
+
+ stat.version = sizeof(struct module_stat);
+ if (modstat(modid, &stat) < 0)
+ warn("can't stat module id %d", modid);
+ else {
+ printf("Id Refs Name\n");
+ printf("%3d %4d %s\n", stat.id, stat.refs, stat.name);
+ }
+
+ return 0;
+ }
+
+ if (filename != NULL) {
+ if ((fileid = kldfind(filename)) < 0)
+ err(1, "can't find file %s", filename);
+ }
+
+ printf("Id Refs Address%*c Size Name\n", POINTER_WIDTH - 7, ' ');
+ if (fileid != 0)
+ printfile(fileid, verbose);
+ else
+ for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid))
+ printfile(fileid, verbose);
+
+ return 0;
+}
diff --git a/sbin/kldunload/Makefile b/sbin/kldunload/Makefile
new file mode 100644
index 0000000..1f71a8f
--- /dev/null
+++ b/sbin/kldunload/Makefile
@@ -0,0 +1,33 @@
+#
+# Copyright (c) 1997 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.
+#
+# $FreeBSD$
+#
+
+PROG= kldunload
+MAN= kldunload.8
+WARNS?= 5
+
+.include <bsd.prog.mk>
diff --git a/sbin/kldunload/kldunload.8 b/sbin/kldunload/kldunload.8
new file mode 100644
index 0000000..3cd8de7
--- /dev/null
+++ b/sbin/kldunload/kldunload.8
@@ -0,0 +1,81 @@
+.\"
+.\" Copyright (c) 1997 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 13, 2004
+.Dt KLDUNLOAD 8
+.Os
+.Sh NAME
+.Nm kldunload
+.Nd unload a file from the kernel
+.Sh SYNOPSIS
+.Nm
+.Op Fl fv
+.Fl i Ar id
+.Nm
+.Op Fl fv
+.Op Fl n
+.Ar name
+.Sh DESCRIPTION
+The
+.Nm
+utility unloads a file which was previously loaded with
+.Xr kldload 8 .
+.Pp
+The following options are available:
+.Bl -tag -width ".Fl n Ar name"
+.It Fl f
+Force the unload.
+This ignores error returns to
+.Dv MOD_QUIESCE
+from the module and implies
+that the module should be unloaded even if it is currently in use.
+The users are left to cope as best they can.
+.It Fl v
+Be more verbose.
+.It Fl i Ar id
+Unload the file with this ID.
+.It Fl n Ar name
+Unload the file with this name.
+.It Ar name
+Unload the file with this name.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr kldunload 2 ,
+.Xr kldload 8 ,
+.Xr kldstat 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 3.0 ,
+replacing the
+.Xr lkm 4
+interface.
+.Sh AUTHORS
+.An Doug Rabson Aq dfr@FreeBSD.org
diff --git a/sbin/kldunload/kldunload.c b/sbin/kldunload/kldunload.c
new file mode 100644
index 0000000..cdf8f04
--- /dev/null
+++ b/sbin/kldunload/kldunload.c
@@ -0,0 +1,104 @@
+/*-
+ * Copyright (c) 1997 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: kldunload [-fv] -i id\n");
+ fprintf(stderr, " kldunload [-fv] [-n] name\n");
+ exit(1);
+}
+
+int
+main(int argc, char** argv)
+{
+ int c;
+ int verbose = 0;
+ int fileid = 0;
+ int force = LINKER_UNLOAD_NORMAL;
+ char* filename = NULL;
+
+ while ((c = getopt(argc, argv, "fi:n:v")) != -1)
+ switch (c) {
+ case 'f':
+ force = LINKER_UNLOAD_FORCE;
+ break;
+ case 'i':
+ fileid = atoi(optarg);
+ if (!fileid)
+ errx(1, "Invalid ID %s", optarg);
+ break;
+ case 'n':
+ filename = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (fileid == 0 && filename == NULL && (argc == 1)) {
+ filename = *argv;
+ argc--;
+ }
+
+ if (argc != 0 || (fileid != 0 && filename != NULL))
+ usage();
+
+ if (fileid == 0 && filename == NULL)
+ usage();
+
+ if (filename != NULL) {
+ if ((fileid = kldfind(filename)) < 0)
+ err(1, "can't find file %s", filename);
+ }
+
+ if (verbose) {
+ struct kld_file_stat stat;
+ stat.version = sizeof stat;
+ if (kldstat(fileid, &stat) < 0)
+ err(1, "can't stat file");
+ printf("Unloading %s, id=%d\n", stat.name, fileid);
+ }
+
+ if (kldunloadf(fileid, force) < 0)
+ err(1, "can't unload file");
+
+ return 0;
+}
diff --git a/sbin/ldconfig/Makefile b/sbin/ldconfig/Makefile
new file mode 100644
index 0000000..442ae63
--- /dev/null
+++ b/sbin/ldconfig/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= ldconfig
+SRCS= elfhints.c ldconfig.c shlib.c support.c
+LDDIR?= ${.CURDIR}/../../libexec/rtld-aout
+WARNS?= 6
+CFLAGS+=-I${LDDIR} -DFREEBSD_AOUT
+MAN= ldconfig.8
+
+.PATH: ${LDDIR}
+
+.include <bsd.prog.mk>
diff --git a/sbin/ldconfig/elfhints.c b/sbin/ldconfig/elfhints.c
new file mode 100644
index 0000000..9bdf56e
--- /dev/null
+++ b/sbin/ldconfig/elfhints.c
@@ -0,0 +1,302 @@
+/*-
+ * Copyright (c) 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <elf-hints.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ldconfig.h"
+
+#define MAXDIRS 1024 /* Maximum directories in path */
+#define MAXFILESIZE (16*1024) /* Maximum hints file size */
+
+static void add_dir(const char *, const char *, int);
+static void read_dirs_from_file(const char *, const char *);
+static void read_elf_hints(const char *, int);
+static void write_elf_hints(const char *);
+
+static const char *dirs[MAXDIRS];
+static int ndirs;
+int insecure;
+
+static void
+add_dir(const char *hintsfile, const char *name, int trusted)
+{
+ struct stat stbuf;
+ int i;
+
+ /* Do some security checks */
+ if (!trusted && !insecure) {
+ if (stat(name, &stbuf) == -1) {
+ warn("%s", name);
+ return;
+ }
+ if (stbuf.st_uid != 0) {
+ warnx("%s: ignoring directory not owned by root", name);
+ return;
+ }
+ if ((stbuf.st_mode & S_IWOTH) != 0) {
+ warnx("%s: ignoring world-writable directory", name);
+ return;
+ }
+ if ((stbuf.st_mode & S_IWGRP) != 0) {
+ warnx("%s: ignoring group-writable directory", name);
+ return;
+ }
+ }
+
+ for (i = 0; i < ndirs; i++)
+ if (strcmp(dirs[i], name) == 0)
+ return;
+ if (ndirs >= MAXDIRS)
+ errx(1, "\"%s\": Too many directories in path", hintsfile);
+ dirs[ndirs++] = name;
+}
+
+void
+list_elf_hints(const char *hintsfile)
+{
+ int i;
+ int nlibs;
+
+ read_elf_hints(hintsfile, 1);
+ printf("%s:\n", hintsfile);
+ printf("\tsearch directories:");
+ for (i = 0; i < ndirs; i++)
+ printf("%c%s", i == 0 ? ' ' : ':', dirs[i]);
+ printf("\n");
+
+ nlibs = 0;
+ for (i = 0; i < ndirs; i++) {
+ DIR *dirp;
+ struct dirent *dp;
+
+ if ((dirp = opendir(dirs[i])) == NULL)
+ continue;
+ while ((dp = readdir(dirp)) != NULL) {
+ int len;
+ int namelen;
+ const char *name;
+ const char *vers;
+
+ /* Name can't be shorter than "libx.so.0" */
+ if ((len = strlen(dp->d_name)) < 9 ||
+ strncmp(dp->d_name, "lib", 3) != 0)
+ continue;
+ name = dp->d_name + 3;
+ vers = dp->d_name + len;
+ while (vers > dp->d_name && isdigit(*(vers-1)))
+ vers--;
+ if (vers == dp->d_name + len)
+ continue;
+ if (vers < dp->d_name + 4 ||
+ strncmp(vers - 4, ".so.", 4) != 0)
+ continue;
+
+ /* We have a valid shared library name. */
+ namelen = (vers - 4) - name;
+ printf("\t%d:-l%.*s.%s => %s/%s\n", nlibs,
+ namelen, name, vers, dirs[i], dp->d_name);
+ nlibs++;
+ }
+ closedir(dirp);
+ }
+}
+
+static void
+read_dirs_from_file(const char *hintsfile, const char *listfile)
+{
+ FILE *fp;
+ char buf[MAXPATHLEN];
+ int linenum;
+
+ if ((fp = fopen(listfile, "r")) == NULL)
+ err(1, "%s", listfile);
+
+ linenum = 0;
+ while (fgets(buf, sizeof buf, fp) != NULL) {
+ char *cp, *sp;
+
+ linenum++;
+ cp = buf;
+ /* Skip leading white space. */
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '#' || *cp == '\0')
+ continue;
+ sp = cp;
+ /* Advance over the directory name. */
+ while (!isspace(*cp) && *cp != '\0')
+ cp++;
+ /* Terminate the string and skip trailing white space. */
+ if (*cp != '\0') {
+ *cp++ = '\0';
+ while (isspace(*cp))
+ cp++;
+ }
+ /* Now we had better be at the end of the line. */
+ if (*cp != '\0')
+ warnx("%s:%d: trailing characters ignored",
+ listfile, linenum);
+
+ if ((sp = strdup(sp)) == NULL)
+ errx(1, "Out of memory");
+ add_dir(hintsfile, sp, 0);
+ }
+
+ fclose(fp);
+}
+
+static void
+read_elf_hints(const char *hintsfile, int must_exist)
+{
+ int fd;
+ struct stat s;
+ void *mapbase;
+ struct elfhints_hdr *hdr;
+ char *strtab;
+ char *dirlist;
+ char *p;
+
+ if ((fd = open(hintsfile, O_RDONLY)) == -1) {
+ if (errno == ENOENT && !must_exist)
+ return;
+ err(1, "Cannot open \"%s\"", hintsfile);
+ }
+ if (fstat(fd, &s) == -1)
+ err(1, "Cannot stat \"%s\"", hintsfile);
+ if (s.st_size > MAXFILESIZE)
+ errx(1, "\"%s\" is unreasonably large", hintsfile);
+ /*
+ * We use a read-write, private mapping so that we can null-terminate
+ * some strings in it without affecting the underlying file.
+ */
+ mapbase = mmap(NULL, s.st_size, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE, fd, 0);
+ if (mapbase == MAP_FAILED)
+ err(1, "Cannot mmap \"%s\"", hintsfile);
+ close(fd);
+
+ hdr = (struct elfhints_hdr *)mapbase;
+ if (hdr->magic != ELFHINTS_MAGIC)
+ errx(1, "\"%s\": invalid file format", hintsfile);
+ if (hdr->version != 1)
+ errx(1, "\"%s\": unrecognized file version (%d)", hintsfile,
+ hdr->version);
+
+ strtab = (char *)mapbase + hdr->strtab;
+ dirlist = strtab + hdr->dirlist;
+
+ if (*dirlist != '\0')
+ while ((p = strsep(&dirlist, ":")) != NULL)
+ add_dir(hintsfile, p, 1);
+}
+
+void
+update_elf_hints(const char *hintsfile, int argc, char **argv, int merge)
+{
+ int i;
+
+ if (merge)
+ read_elf_hints(hintsfile, 0);
+ for (i = 0; i < argc; i++) {
+ struct stat s;
+
+ if (stat(argv[i], &s) == -1)
+ warn("warning: %s", argv[i]);
+ else if (S_ISREG(s.st_mode))
+ read_dirs_from_file(hintsfile, argv[i]);
+ else
+ add_dir(hintsfile, argv[i], 0);
+ }
+ write_elf_hints(hintsfile);
+}
+
+static void
+write_elf_hints(const char *hintsfile)
+{
+ struct elfhints_hdr hdr;
+ char *tempname;
+ int fd;
+ FILE *fp;
+ int i;
+
+ if (asprintf(&tempname, "%s.XXXXXX", hintsfile) == -1)
+ errx(1, "Out of memory");
+ if ((fd = mkstemp(tempname)) == -1)
+ err(1, "mkstemp(%s)", tempname);
+ if (fchmod(fd, 0444) == -1)
+ err(1, "fchmod(%s)", tempname);
+ if ((fp = fdopen(fd, "wb")) == NULL)
+ err(1, "fdopen(%s)", tempname);
+
+ hdr.magic = ELFHINTS_MAGIC;
+ hdr.version = 1;
+ hdr.strtab = sizeof hdr;
+ hdr.strsize = 0;
+ hdr.dirlist = 0;
+ memset(hdr.spare, 0, sizeof hdr.spare);
+
+ /* Count up the size of the string table. */
+ if (ndirs > 0) {
+ hdr.strsize += strlen(dirs[0]);
+ for (i = 1; i < ndirs; i++)
+ hdr.strsize += 1 + strlen(dirs[i]);
+ }
+ hdr.dirlistlen = hdr.strsize;
+ hdr.strsize++; /* For the null terminator */
+
+ /* Write the header. */
+ if (fwrite(&hdr, 1, sizeof hdr, fp) != sizeof hdr)
+ err(1, "%s: write error", tempname);
+ /* Write the strings. */
+ if (ndirs > 0) {
+ if (fputs(dirs[0], fp) == EOF)
+ err(1, "%s: write error", tempname);
+ for (i = 1; i < ndirs; i++)
+ if (fprintf(fp, ":%s", dirs[i]) < 0)
+ err(1, "%s: write error", tempname);
+ }
+ if (putc('\0', fp) == EOF || fclose(fp) == EOF)
+ err(1, "%s: write error", tempname);
+
+ if (rename(tempname, hintsfile) == -1)
+ err(1, "rename %s to %s", tempname, hintsfile);
+ free(tempname);
+}
diff --git a/sbin/ldconfig/ldconfig.8 b/sbin/ldconfig/ldconfig.8
new file mode 100644
index 0000000..8824590
--- /dev/null
+++ b/sbin/ldconfig/ldconfig.8
@@ -0,0 +1,224 @@
+.\"
+.\" Copyright (c) 1993 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 acknowledgement:
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 11, 2005
+.Dt LDCONFIG 8
+.Os
+.Sh NAME
+.Nm ldconfig
+.Nd configure the shared library cache
+.Sh SYNOPSIS
+.Nm
+.Op Fl 32
+.Op Fl aout | Fl elf
+.Op Fl Rimrsv
+.Op Fl f Ar hints_file
+.Op Ar directory | Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to prepare a set of
+.Dq hints
+for use by the dynamic linker
+to facilitate quick lookup of shared libraries available in multiple
+directories.
+It scans a set of built-in system directories and any
+.Ar directories
+specified on the command line (in the given order) looking for
+shared libraries and stores the results in a system file to forestall
+the overhead that would otherwise result from the directory search
+operations the dynamic linker would have to perform to load the
+required shared libraries.
+.Pp
+Files named on the command line are expected to contain directories
+to scan for shared libraries.
+Each directory's pathname must start on a new
+line.
+Blank lines and lines starting with the comment character
+.Ql \&#
+are ignored.
+Filenames must conform to the
+.Pa lib*.so.[0-9]
+pattern in order to be added to the hints file.
+.Pp
+For security reasons, directories which are world or group-writable or which
+are not owned by root produce warning messages and are skipped, unless
+the
+.Fl i
+option is present.
+.Pp
+The shared libraries which are found will be automatically available for loading
+if needed by the program being prepared for execution.
+This obviates the need
+for storing search paths within the executable.
+.Pp
+The
+.Ev LD_LIBRARY_PATH
+environment variable can be used to override the use of
+directories (or the order thereof) from the cache or to specify additional
+directories where shared libraries might be found.
+.Ev LD_LIBRARY_PATH
+is a
+.Sq \&:
+separated list of directory paths which are searched by
+the dynamic linker
+when it needs to load a shared library.
+It can be viewed as the run-time
+equivalent of the
+.Fl L
+switch of
+.Xr ld 1 .
+.Pp
+The
+.Nm
+utility is typically run as part of the boot sequence.
+.Pp
+The following options are recognized by
+.Nm :
+.Bl -tag -width indent
+.It Fl 32
+Generate the hints for 32-bit ABI shared libraries
+on 64-bit systems that support running 32-bit binaries.
+.It Fl aout
+Generate the hints for a.out format shared libraries.
+.It Fl elf
+Generate the hints for ELF format shared libraries.
+.It Fl R
+Rescan the previously configured directories.
+This opens the previous hints
+file and fetches the directory list from the header.
+Any additional pathnames
+on the command line are also processed.
+This is the default action when no parameters are given.
+.It Fl f Ar hints_file
+Read and/or update the specified hints file, instead of the standard file.
+This option is provided primarily for testing.
+.It Fl i
+Run in insecure mode.
+The security checks will not be performed.
+.It Fl m
+Instead of replacing the contents of the hints file
+with those found in the directories specified,
+.Dq merge
+in new entries.
+Directories recorded in the hints file by previous runs of
+.Nm
+are also rescanned for new shared libraries.
+.It Fl r
+List the current contents of the hints file
+on the standard output.
+The hints file is not modified.
+The list of
+directories stored in the hints file is included.
+.It Fl s
+Do not scan the built-in system directory
+.Pq Dq /usr/lib
+for shared libraries.
+.It Fl v
+Switch on verbose mode.
+.El
+.Sh SECURITY
+Special care must be taken when loading shared libraries into the address
+space of
+.Ev set-user-Id
+programs.
+Whenever such a program is run by any user except the owner of the program,
+the dynamic linker
+will only load shared libraries from the hints
+file.
+In particular, the
+.Ev LD_LIBRARY_PATH
+is not used to search for libraries.
+Thus, the role of ldconfig is dual.
+In
+addition to building a set of hints for quick lookup, it also serves to
+specify the trusted collection of directories from which shared objects can
+be safely loaded.
+.Sh ENVIRONMENT
+.Bl -tag -width OBJFORMATxxx -compact
+.It Ev OBJFORMAT
+Overrides
+.Pa /etc/objformat
+(see below) to determine whether
+.Fl aout
+or
+.Fl elf
+is the default.
+If set, its value should be either
+.Ql aout
+or
+.Ql elf .
+.El
+.Sh FILES
+.Bl -tag -width /var/run/ld-elf.so.hintsxxx -compact
+.It Pa /var/run/ld.so.hints
+Standard hints file for the a.out dynamic linker.
+.It Pa /var/run/ld-elf.so.hints
+Standard hints file for the ELF dynamic linker.
+.It Pa /etc/ld.so.conf
+Conventional configuration file containing directory names for
+invocations with
+.Fl aout .
+.It Pa /etc/ld-elf.so.conf
+Conventional configuration file containing directory names for
+invocations with
+.Fl elf .
+.It Pa /var/run/ld-elf32.so.hints
+.It Pa /var/run/ld32.so.hints
+Conventional configuration files containing directory names for
+invocations with
+.Fl 32 .
+.It Pa /etc/objformat
+Determines whether
+.Fl aout
+or
+.Fl elf
+is the default.
+If present, it must consist of a single line
+containing either
+.Ql OBJFORMAT=aout
+or
+.Ql OBJFORMAT=elf .
+.El
+.Sh SEE ALSO
+.Xr ld 1 ,
+.Xr link 5
+.Sh HISTORY
+A
+.Nm
+utility first appeared in SunOS 4.0, it appeared in its current form
+in
+.Fx 1.1 .
+.Sh BUGS
+Some security checks (for example, verifying root ownership of
+added directories) are not performed when
+.Fl aout
+is specified.
diff --git a/sbin/ldconfig/ldconfig.c b/sbin/ldconfig/ldconfig.c
new file mode 100644
index 0000000..5ef2176
--- /dev/null
+++ b/sbin/ldconfig/ldconfig.c
@@ -0,0 +1,640 @@
+/*
+ * Copyright (c) 1993,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 acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <a.out.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <elf-hints.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/link_aout.h>
+#include <objformat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ldconfig.h"
+#include "shlib.h"
+#include "support.h"
+
+#if DEBUG
+/* test */
+#undef _PATH_LD_HINTS
+#define _PATH_LD_HINTS "./ld.so.hints"
+#undef _PATH_ELF_HINTS
+#define _PATH_ELF_HINTS "./ld-elf.so.hints"
+#endif
+
+#define _PATH_LD32_HINTS "/var/run/ld32.so.hints"
+#define _PATH_ELF32_HINTS "/var/run/ld-elf32.so.hints"
+
+#undef major
+#undef minor
+
+static int verbose;
+static int nostd;
+static int justread;
+static int merge;
+static int rescan;
+static const char *hints_file;
+
+struct shlib_list {
+ /* Internal list of shared libraries found */
+ char *name;
+ char *path;
+ int dewey[MAXDEWEY];
+ int ndewey;
+#define major dewey[0]
+#define minor dewey[1]
+ struct shlib_list *next;
+};
+
+static struct shlib_list *shlib_head = NULL, **shlib_tail = &shlib_head;
+static char *dir_list;
+
+static int buildhints(void);
+static int dodir(char *, int);
+int dofile(char *, int);
+static void enter(char *, char *, char *, int *, int);
+static void listhints(void);
+static int readhints(void);
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ int i, c;
+ int rval = 0;
+ int is_aout = 0;
+ int is_32 = 0;
+
+ while (argc > 1) {
+ if (strcmp(argv[1], "-aout") == 0) {
+ is_aout = 1;
+ argc--;
+ argv++;
+ } else if (strcmp(argv[1], "-elf") == 0) {
+ is_aout = 0;
+ argc--;
+ argv++;
+ } else if (strcmp(argv[1], "-32") == 0) {
+ is_32 = 1;
+ argc--;
+ argv++;
+ } else {
+ break;
+ }
+ }
+
+ if (is_32)
+ hints_file = is_aout ? _PATH_LD32_HINTS : _PATH_ELF32_HINTS;
+ else
+ hints_file = is_aout ? _PATH_LD_HINTS : _PATH_ELF_HINTS;
+ if (argc == 1)
+ rescan = 1;
+ else while((c = getopt(argc, argv, "Rf:imrsv")) != -1) {
+ switch (c) {
+ case 'R':
+ rescan = 1;
+ break;
+ case 'f':
+ hints_file = optarg;
+ break;
+ case 'i':
+ insecure = 1;
+ break;
+ case 'm':
+ merge = 1;
+ break;
+ case 'r':
+ justread = 1;
+ break;
+ case 's':
+ nostd = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (!is_aout) {
+ if (justread)
+ list_elf_hints(hints_file);
+ else
+ update_elf_hints(hints_file, argc - optind,
+ argv + optind, merge || rescan);
+ return 0;
+ }
+
+ /* Here begins the aout libs processing */
+ dir_list = strdup("");
+
+ if (justread || merge || rescan) {
+ if ((rval = readhints()) != 0)
+ return rval;
+ }
+
+ if (!nostd && !merge && !rescan)
+ std_search_path();
+
+ /* Add any directories/files from the command line */
+ if (!justread) {
+ for (i = optind; i < argc; i++) {
+ struct stat stbuf;
+
+ if (stat(argv[i], &stbuf) == -1) {
+ warn("%s", argv[i]);
+ rval = -1;
+ } else if (strcmp(argv[i], "/usr/lib") == 0) {
+ warnx("WARNING! '%s' can not be used", argv[i]);
+ rval = -1;
+ } else {
+ /*
+ * See if this is a directory-containing
+ * file instead of a directory
+ */
+ if (S_ISREG(stbuf.st_mode))
+ rval |= dofile(argv[i], 0);
+ else
+ add_search_path(argv[i]);
+ }
+ }
+ }
+
+ for (i = 0; i < n_search_dirs; i++) {
+ char *cp = concat(dir_list, *dir_list?":":"", search_dirs[i]);
+ free(dir_list);
+ dir_list = cp;
+ }
+
+ if (justread) {
+ listhints();
+ return 0;
+ }
+
+ for (i = 0; i < n_search_dirs; i++)
+ rval |= dodir(search_dirs[i], 1);
+
+ rval |= buildhints();
+
+ return rval;
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: ldconfig [-32] [-aout | -elf] [-Rimrsv] [-f hints_file] [directory | file ...]\n");
+ exit(1);
+}
+
+int
+dofile(fname, silent)
+char *fname;
+int silent;
+{
+ FILE *hfp;
+ char buf[MAXPATHLEN];
+ int rval = 0;
+ char *cp, *sp;
+
+ if ((hfp = fopen(fname, "r")) == NULL) {
+ warn("%s", fname);
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), hfp)) {
+ cp = buf;
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '#' || *cp == '\0')
+ continue;
+ sp = cp;
+ while (!isspace(*cp) && *cp != '\0')
+ cp++;
+
+ if (*cp != '\n') {
+ *cp = '\0';
+ warnx("%s: trailing characters ignored", sp);
+ }
+
+ *cp = '\0';
+
+ rval |= dodir(sp, silent);
+ }
+
+ (void)fclose(hfp);
+ return rval;
+}
+
+int
+dodir(dir, silent)
+char *dir;
+int silent;
+{
+ DIR *dd;
+ struct dirent *dp;
+ char name[MAXPATHLEN];
+ int dewey[MAXDEWEY], ndewey;
+
+ if ((dd = opendir(dir)) == NULL) {
+ if (silent && errno == ENOENT) /* Ignore the error */
+ return 0;
+ warn("%s", dir);
+ return -1;
+ }
+
+ while ((dp = readdir(dd)) != NULL) {
+ int n;
+ char *cp;
+
+ /* Check for `lib' prefix */
+ if (dp->d_name[0] != 'l' ||
+ dp->d_name[1] != 'i' ||
+ dp->d_name[2] != 'b')
+ continue;
+
+ /* Copy the entry minus prefix */
+ (void)strcpy(name, dp->d_name + 3);
+ n = strlen(name);
+ if (n < 4)
+ continue;
+
+ /* Find ".so." in name */
+ for (cp = name + n - 4; cp > name; --cp) {
+ if (cp[0] == '.' &&
+ cp[1] == 's' &&
+ cp[2] == 'o' &&
+ cp[3] == '.')
+ break;
+ }
+ if (cp <= name)
+ continue;
+
+ *cp = '\0';
+ if (!isdigit(*(cp+4)))
+ continue;
+
+ bzero((caddr_t)dewey, sizeof(dewey));
+ ndewey = getdewey(dewey, cp + 4);
+ if (ndewey < 2)
+ continue;
+ enter(dir, dp->d_name, name, dewey, ndewey);
+ }
+
+ closedir(dd);
+ return 0;
+}
+
+static void
+enter(dir, file, name, dewey, ndewey)
+char *dir, *file, *name;
+int dewey[], ndewey;
+{
+ struct shlib_list *shp;
+
+ for (shp = shlib_head; shp; shp = shp->next) {
+ if (strcmp(name, shp->name) != 0 || major != shp->major)
+ continue;
+
+ /* Name matches existing entry */
+ if (cmpndewey(dewey, ndewey, shp->dewey, shp->ndewey) > 0) {
+
+ /* Update this entry with higher versioned lib */
+ if (verbose)
+ printf("Updating lib%s.%d.%d to %s/%s\n",
+ shp->name, shp->major, shp->minor,
+ dir, file);
+
+ free(shp->name);
+ shp->name = strdup(name);
+ free(shp->path);
+ shp->path = concat(dir, "/", file);
+ bcopy(dewey, shp->dewey, sizeof(shp->dewey));
+ shp->ndewey = ndewey;
+ }
+ break;
+ }
+
+ if (shp)
+ /* Name exists: older version or just updated */
+ return;
+
+ /* Allocate new list element */
+ if (verbose)
+ printf("Adding %s/%s\n", dir, file);
+
+ shp = (struct shlib_list *)xmalloc(sizeof *shp);
+ shp->name = strdup(name);
+ shp->path = concat(dir, "/", file);
+ bcopy(dewey, shp->dewey, sizeof(shp->dewey));
+ shp->ndewey = ndewey;
+ shp->next = NULL;
+
+ *shlib_tail = shp;
+ shlib_tail = &shp->next;
+}
+
+
+static int
+hinthash(char *cp, int vmajor)
+{
+ int k = 0;
+
+ while (*cp)
+ k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
+
+ k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
+
+ return k;
+}
+
+int
+buildhints()
+{
+ struct hints_header hdr;
+ struct hints_bucket *blist;
+ struct shlib_list *shp;
+ char *strtab;
+ int i, n, str_index = 0;
+ int strtab_sz = 0; /* Total length of strings */
+ int nhints = 0; /* Total number of hints */
+ int fd;
+ char *tmpfilename;
+
+ for (shp = shlib_head; shp; shp = shp->next) {
+ strtab_sz += 1 + strlen(shp->name);
+ strtab_sz += 1 + strlen(shp->path);
+ nhints++;
+ }
+
+ /* Fill hints file header */
+ hdr.hh_magic = HH_MAGIC;
+ hdr.hh_version = LD_HINTS_VERSION_2;
+ hdr.hh_nbucket = 1 * nhints;
+ n = hdr.hh_nbucket * sizeof(struct hints_bucket);
+ hdr.hh_hashtab = sizeof(struct hints_header);
+ hdr.hh_strtab = hdr.hh_hashtab + n;
+ hdr.hh_dirlist = strtab_sz;
+ strtab_sz += 1 + strlen(dir_list);
+ hdr.hh_strtab_sz = strtab_sz;
+ hdr.hh_ehints = hdr.hh_strtab + hdr.hh_strtab_sz;
+
+ if (verbose)
+ printf("Totals: entries %d, buckets %ld, string size %d\n",
+ nhints, (long)hdr.hh_nbucket, strtab_sz);
+
+ /* Allocate buckets and string table */
+ blist = (struct hints_bucket *)xmalloc(n);
+ bzero((char *)blist, n);
+ for (i = 0; i < hdr.hh_nbucket; i++)
+ /* Empty all buckets */
+ blist[i].hi_next = -1;
+
+ strtab = (char *)xmalloc(strtab_sz);
+
+ /* Enter all */
+ for (shp = shlib_head; shp; shp = shp->next) {
+ struct hints_bucket *bp;
+
+ bp = blist +
+ (hinthash(shp->name, shp->major) % hdr.hh_nbucket);
+
+ if (bp->hi_pathx) {
+ int j;
+
+ for (j = 0; j < hdr.hh_nbucket; j++) {
+ if (blist[j].hi_pathx == 0)
+ break;
+ }
+ if (j == hdr.hh_nbucket) {
+ warnx("bummer!");
+ return -1;
+ }
+ while (bp->hi_next != -1)
+ bp = &blist[bp->hi_next];
+ bp->hi_next = j;
+ bp = blist + j;
+ }
+
+ /* Insert strings in string table */
+ bp->hi_namex = str_index;
+ strcpy(strtab + str_index, shp->name);
+ str_index += 1 + strlen(shp->name);
+
+ bp->hi_pathx = str_index;
+ strcpy(strtab + str_index, shp->path);
+ str_index += 1 + strlen(shp->path);
+
+ /* Copy versions */
+ bcopy(shp->dewey, bp->hi_dewey, sizeof(bp->hi_dewey));
+ bp->hi_ndewey = shp->ndewey;
+ }
+
+ /* Copy search directories */
+ strcpy(strtab + str_index, dir_list);
+ str_index += 1 + strlen(dir_list);
+
+ /* Sanity check */
+ if (str_index != strtab_sz) {
+ errx(1, "str_index(%d) != strtab_sz(%d)", str_index, strtab_sz);
+ }
+
+ tmpfilename = concat(hints_file, ".XXXXXXXXXX", "");
+ umask(0); /* Create with exact permissions */
+ if ((fd = mkstemp(tmpfilename)) == -1) {
+ warn("%s", tmpfilename);
+ return -1;
+ }
+ fchmod(fd, 0444);
+
+ if (write(fd, &hdr, sizeof(struct hints_header)) !=
+ sizeof(struct hints_header)) {
+ warn("%s", hints_file);
+ return -1;
+ }
+ if (write(fd, blist, hdr.hh_nbucket * sizeof(*blist)) !=
+ (ssize_t)(hdr.hh_nbucket * sizeof(*blist))) {
+ warn("%s", hints_file);
+ return -1;
+ }
+ if (write(fd, strtab, strtab_sz) != strtab_sz) {
+ warn("%s", hints_file);
+ return -1;
+ }
+ if (close(fd) != 0) {
+ warn("%s", hints_file);
+ return -1;
+ }
+
+ /* Install it */
+ if (unlink(hints_file) != 0 && errno != ENOENT) {
+ warn("%s", hints_file);
+ return -1;
+ }
+
+ if (rename(tmpfilename, hints_file) != 0) {
+ warn("%s", hints_file);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+readhints()
+{
+ int fd;
+ void *addr;
+ long fsize;
+ long msize;
+ struct hints_header *hdr;
+ struct hints_bucket *blist;
+ char *strtab;
+ struct shlib_list *shp;
+ int i;
+
+ if ((fd = open(hints_file, O_RDONLY, 0)) == -1) {
+ warn("%s", hints_file);
+ return -1;
+ }
+
+ msize = PAGE_SIZE;
+ addr = mmap(0, msize, PROT_READ, MAP_COPY, fd, 0);
+
+ if (addr == MAP_FAILED) {
+ warn("%s", hints_file);
+ return -1;
+ }
+
+ hdr = (struct hints_header *)addr;
+ if (HH_BADMAG(*hdr)) {
+ warnx("%s: bad magic: %lo", hints_file,
+ (unsigned long)hdr->hh_magic);
+ return -1;
+ }
+
+ if (hdr->hh_version != LD_HINTS_VERSION_1 &&
+ hdr->hh_version != LD_HINTS_VERSION_2) {
+ warnx("unsupported version: %ld", (long)hdr->hh_version);
+ return -1;
+ }
+
+ if (hdr->hh_ehints > msize) {
+ fsize = hdr->hh_ehints;
+ munmap(addr, msize);
+ addr = mmap(0, fsize, PROT_READ, MAP_COPY, fd, 0);
+ if (addr == MAP_FAILED) {
+ warn("%s", hints_file);
+ return -1;
+ }
+ hdr = (struct hints_header *)addr;
+ }
+ close(fd);
+
+ strtab = (char *)addr + hdr->hh_strtab;
+
+ if (hdr->hh_version >= LD_HINTS_VERSION_2)
+ add_search_path(strtab + hdr->hh_dirlist);
+ else if (rescan)
+ errx(1, "%s too old and does not contain the search path",
+ hints_file);
+
+ if (rescan)
+ return 0;
+
+ blist = malloc(sizeof(*blist) * hdr->hh_nbucket);
+ if (blist == NULL)
+ err(1, "readhints");
+ memcpy(blist, (char *)addr + hdr->hh_hashtab,
+ sizeof(*blist) * hdr->hh_nbucket);
+
+
+ for (i = 0; i < hdr->hh_nbucket; i++) {
+ struct hints_bucket *bp = &blist[i];
+
+ /* Sanity check */
+ if (bp->hi_namex >= hdr->hh_strtab_sz) {
+ warnx("bad name index: %#x", bp->hi_namex);
+ free(blist);
+ return -1;
+ }
+ if (bp->hi_pathx >= hdr->hh_strtab_sz) {
+ warnx("bad path index: %#x", bp->hi_pathx);
+ free(blist);
+ return -1;
+ }
+
+ /* Allocate new list element */
+ shp = (struct shlib_list *)xmalloc(sizeof *shp);
+ shp->name = strdup(strtab + bp->hi_namex);
+ shp->path = strdup(strtab + bp->hi_pathx);
+ bcopy(bp->hi_dewey, shp->dewey, sizeof(shp->dewey));
+ shp->ndewey = bp->hi_ndewey;
+ shp->next = NULL;
+
+ *shlib_tail = shp;
+ shlib_tail = &shp->next;
+ }
+
+ free(blist);
+ return 0;
+}
+
+static void
+listhints()
+{
+ struct shlib_list *shp;
+ int i;
+
+ printf("%s:\n", hints_file);
+ printf("\tsearch directories: %s\n", dir_list);
+
+ for (i = 0, shp = shlib_head; shp; i++, shp = shp->next)
+ printf("\t%d:-l%s.%d.%d => %s\n",
+ i, shp->name, shp->major, shp->minor, shp->path);
+
+ return;
+}
diff --git a/sbin/ldconfig/ldconfig.h b/sbin/ldconfig/ldconfig.h
new file mode 100644
index 0000000..859bcbd
--- /dev/null
+++ b/sbin/ldconfig/ldconfig.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef LDCONFIG_H
+#define LDCONFIG_H 1
+
+#include <sys/cdefs.h>
+
+extern int insecure; /* -i flag, needed here for elfhints.c */
+
+__BEGIN_DECLS
+void list_elf_hints(const char *);
+void update_elf_hints(const char *, int, char **, int);
+__END_DECLS
+
+#endif
diff --git a/sbin/mca/Makefile b/sbin/mca/Makefile
new file mode 100644
index 0000000..25199d3
--- /dev/null
+++ b/sbin/mca/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+PROG= mca
+WARNS?= 4
+NO_MAN=
+
+.include <bsd.prog.mk>
diff --git a/sbin/mca/mca.c b/sbin/mca/mca.c
new file mode 100644
index 0000000..0c4e1a0
--- /dev/null
+++ b/sbin/mca/mca.c
@@ -0,0 +1,529 @@
+/*
+ * Copyright (c) 2002 Marcel Moolenaar
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/sysctl.h>
+#include <sys/uuid.h>
+
+/*
+ * Hack to make this compile on non-ia64 machines.
+ */
+#ifdef __ia64__
+#include <machine/mca.h>
+#else
+#include "../../sys/ia64/include/mca.h"
+#endif
+
+#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 <uuid.h>
+
+#define BCD(x) ((x >> 4) * 10 + (x & 15))
+
+static char hw_mca_count[] = "hw.mca.count";
+static char hw_mca_first[] = "hw.mca.first";
+static char hw_mca_last[] = "hw.mca.last";
+static char hw_mca_recid[] = "hw.mca.%d";
+
+static char default_dumpfile[] = "/var/log/mca.log";
+
+int fl_dump;
+char *file;
+
+static const char *
+severity(int error)
+{
+
+ switch (error) {
+ case MCA_RH_ERROR_RECOVERABLE:
+ return ("recoverable");
+ case MCA_RH_ERROR_FATAL:
+ return ("fatal");
+ case MCA_RH_ERROR_CORRECTED:
+ return ("corrected");
+ }
+
+ return ("unknown");
+}
+
+static const char *
+uuid(uuid_t *id)
+{
+ static char buffer[64];
+ char *s;
+
+ uuid_to_string(id, &s, NULL);
+ strcpy(buffer, s);
+ free(s);
+ return (buffer);
+}
+
+static int
+show_value(int indent, const char *var, const char *fmt, ...)
+{
+ va_list ap;
+ int len;
+
+ len = indent;
+ while (indent--)
+ putchar(' ');
+ len += printf("<%s>", var);
+ va_start(ap, fmt);
+ len += vprintf(fmt, ap);
+ len += printf("</%s>\n", var);
+ return (len);
+}
+
+static size_t
+show_header(struct mca_record_header *rh)
+{
+
+ printf(" <header>\n");
+ show_value(4, "seqnr", "%lld", (long long)rh->rh_seqnr);
+ show_value(4, "revision", "%d.%d", BCD(rh->rh_major),
+ BCD(rh->rh_minor));
+ show_value(4, "severity", "%s", severity(rh->rh_error));
+ show_value(4, "length", "%lld", (long long)rh->rh_length);
+ show_value(4, "date", "%d%02d/%02d/%02d",
+ BCD(rh->rh_time[MCA_RH_TIME_CENT]),
+ BCD(rh->rh_time[MCA_RH_TIME_YEAR]),
+ BCD(rh->rh_time[MCA_RH_TIME_MON]),
+ BCD(rh->rh_time[MCA_RH_TIME_MDAY]));
+ show_value(4, "time", "%02d:%02d:%02d",
+ BCD(rh->rh_time[MCA_RH_TIME_HOUR]),
+ BCD(rh->rh_time[MCA_RH_TIME_MIN]),
+ BCD(rh->rh_time[MCA_RH_TIME_SEC]));
+ if (rh->rh_flags & MCA_RH_FLAGS_PLATFORM_ID)
+ show_value(4, "platform", "%s", uuid(&rh->rh_platform));
+ printf(" </header>\n");
+ return (rh->rh_length);
+}
+
+static void
+show_cpu_mod(const char *what, int idx, struct mca_cpu_mod *cpu_mod)
+{
+ printf(" <%s-%d>\n", what, idx);
+ if (cpu_mod->cpu_mod_flags & MCA_CPU_MOD_FLAGS_INFO)
+ show_value(8, "info", "0x%016llx",
+ (long long)cpu_mod->cpu_mod_info);
+ if (cpu_mod->cpu_mod_flags & MCA_CPU_MOD_FLAGS_REQID)
+ show_value(8, "requester", "0x%016llx",
+ (long long)cpu_mod->cpu_mod_reqid);
+ if (cpu_mod->cpu_mod_flags & MCA_CPU_MOD_FLAGS_RSPID)
+ show_value(8, "responder", "0x%016llx",
+ (long long)cpu_mod->cpu_mod_rspid);
+ if (cpu_mod->cpu_mod_flags & MCA_CPU_MOD_FLAGS_TGTID)
+ show_value(8, "target", "0x%016llx",
+ (long long)cpu_mod->cpu_mod_tgtid);
+ if (cpu_mod->cpu_mod_flags & MCA_CPU_MOD_FLAGS_IP)
+ show_value(8, "ip", "0x%016llx",
+ (long long)cpu_mod->cpu_mod_ip);
+ printf(" </%s-%d>\n", what, idx);
+}
+
+static void
+show_cpu(struct mca_cpu_record *cpu)
+{
+ char var[16];
+ struct mca_cpu_mod *mod;
+ struct mca_cpu_cpuid *cpuid;
+ struct mca_cpu_psi *psi;
+ int i, n;
+
+ printf(" <cpu>\n");
+
+ if (cpu->cpu_flags & MCA_CPU_FLAGS_ERRMAP)
+ show_value(6, "errmap", "0x%016llx",
+ (long long)cpu->cpu_errmap);
+ if (cpu->cpu_flags & MCA_CPU_FLAGS_STATE)
+ show_value(6, "state", "0x%016llx",
+ (long long)cpu->cpu_state);
+ if (cpu->cpu_flags & MCA_CPU_FLAGS_CR_LID)
+ show_value(6, "cr_lid", "0x%016llx",
+ (long long)cpu->cpu_cr_lid);
+
+ mod = (struct mca_cpu_mod*)(cpu + 1);
+ n = MCA_CPU_FLAGS_CACHE(cpu->cpu_flags);
+ for (i = 0; i < n; i++)
+ show_cpu_mod("cache", i, mod++);
+ n = MCA_CPU_FLAGS_TLB(cpu->cpu_flags);
+ for (i = 0; i < n; i++)
+ show_cpu_mod("tlb", i, mod++);
+ n = MCA_CPU_FLAGS_BUS(cpu->cpu_flags);
+ for (i = 0; i < n; i++)
+ show_cpu_mod("bus", i, mod++);
+ n = MCA_CPU_FLAGS_REG(cpu->cpu_flags);
+ for (i = 0; i < n; i++)
+ show_cpu_mod("reg", i, mod++);
+ n = MCA_CPU_FLAGS_MS(cpu->cpu_flags);
+ for (i = 0; i < n; i++)
+ show_cpu_mod("ms", i, mod++);
+
+ cpuid = (struct mca_cpu_cpuid*)mod;
+ for (i = 0; i < 6; i++) {
+ sprintf(var, "cpuid-%d", i);
+ show_value(6, var, "0x%016llx", (long long)cpuid->cpuid[i]);
+ }
+
+ psi = (struct mca_cpu_psi*)(cpuid + 1);
+ /* TODO: Dump PSI */
+
+ printf(" </cpu>\n");
+}
+
+static void
+show_memory(struct mca_mem_record *mem)
+{
+ printf(" <memory>\n");
+
+ if (mem->mem_flags & MCA_MEM_FLAGS_STATUS)
+ show_value(6, "status", "0x%016llx",
+ (long long)mem->mem_status);
+ if (mem->mem_flags & MCA_MEM_FLAGS_ADDR)
+ show_value(6, "address", "0x%016llx",
+ (long long)mem->mem_addr);
+ if (mem->mem_flags & MCA_MEM_FLAGS_ADDRMASK)
+ show_value(6, "mask", "0x%016llx",
+ (long long)mem->mem_addrmask);
+ if (mem->mem_flags & MCA_MEM_FLAGS_NODE)
+ show_value(6, "node", "0x%04x", mem->mem_node);
+ if (mem->mem_flags & MCA_MEM_FLAGS_CARD)
+ show_value(6, "card", "0x%04x", mem->mem_card);
+ if (mem->mem_flags & MCA_MEM_FLAGS_MODULE)
+ show_value(6, "module", "0x%04x", mem->mem_module);
+ if (mem->mem_flags & MCA_MEM_FLAGS_BANK)
+ show_value(6, "bank", "0x%04x", mem->mem_bank);
+ if (mem->mem_flags & MCA_MEM_FLAGS_DEVICE)
+ show_value(6, "device", "0x%04x", mem->mem_device);
+ if (mem->mem_flags & MCA_MEM_FLAGS_ROW)
+ show_value(6, "row", "0x%04x", mem->mem_row);
+ if (mem->mem_flags & MCA_MEM_FLAGS_COLUMN)
+ show_value(6, "column", "0x%04x", mem->mem_column);
+ if (mem->mem_flags & MCA_MEM_FLAGS_BITPOS)
+ show_value(6, "bit", "0x%04x", mem->mem_bitpos);
+ if (mem->mem_flags & MCA_MEM_FLAGS_REQID)
+ show_value(6, "requester", "0x%016llx",
+ (long long)mem->mem_reqid);
+ if (mem->mem_flags & MCA_MEM_FLAGS_RSPID)
+ show_value(6, "responder", "0x%016llx",
+ (long long)mem->mem_rspid);
+ if (mem->mem_flags & MCA_MEM_FLAGS_TGTID)
+ show_value(6, "target", "0x%016llx",
+ (long long)mem->mem_tgtid);
+ if (mem->mem_flags & MCA_MEM_FLAGS_BUSDATA)
+ show_value(6, "status", "0x%016llx",
+ (long long)mem->mem_busdata);
+ if (mem->mem_flags & MCA_MEM_FLAGS_OEM_ID)
+ show_value(6, "oem", "%s", uuid(&mem->mem_oem_id));
+ /* TODO: Dump OEM data */
+
+ printf(" </memory>\n");
+}
+
+static void
+show_sel(void)
+{
+ printf(" # SEL\n");
+}
+
+static void
+show_pci_bus(struct mca_pcibus_record *pcibus)
+{
+ printf(" <pci-bus>\n");
+
+ if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_STATUS)
+ show_value(6, "status", "0x%016llx",
+ (long long)pcibus->pcibus_status);
+ if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_ERROR)
+ show_value(6, "error", "0x%04x", pcibus->pcibus_error);
+ if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_BUS)
+ show_value(6, "bus", "0x%04x", pcibus->pcibus_bus);
+ if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_ADDR)
+ show_value(6, "address", "0x%016llx",
+ (long long)pcibus->pcibus_addr);
+ if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_DATA)
+ show_value(6, "data", "0x%016llx",
+ (long long)pcibus->pcibus_data);
+ if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_CMD)
+ show_value(6, "cmd", "0x%016llx",
+ (long long)pcibus->pcibus_cmd);
+ if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_REQID)
+ show_value(6, "requester", "0x%016llx",
+ (long long)pcibus->pcibus_reqid);
+ if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_RSPID)
+ show_value(6, "responder", "0x%016llx",
+ (long long)pcibus->pcibus_rspid);
+ if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_TGTID)
+ show_value(6, "target", "0x%016llx",
+ (long long)pcibus->pcibus_tgtid);
+ if (pcibus->pcibus_flags & MCA_PCIBUS_FLAGS_OEM_ID)
+ show_value(6, "oem", "%s", uuid(&pcibus->pcibus_oem_id));
+ /* TODO: Dump OEM data */
+
+ printf(" </pci-bus>\n");
+}
+
+static void
+show_smbios(void)
+{
+ printf(" # SMBIOS\n");
+}
+
+static void
+show_pci_dev(struct mca_pcidev_record *pcidev)
+{
+ printf(" <pci-dev>\n");
+
+ if (pcidev->pcidev_flags & MCA_PCIDEV_FLAGS_STATUS)
+ show_value(6, "status", "0x%016llx",
+ (long long)pcidev->pcidev_status);
+ if (pcidev->pcidev_flags & MCA_PCIDEV_FLAGS_INFO) {
+ show_value(6, "vendor", "0x%04x",
+ pcidev->pcidev_info.info_vendor);
+ show_value(6, "device", "0x%04x",
+ pcidev->pcidev_info.info_device);
+ show_value(6, "class", "0x%06x",
+ MCA_PCIDEV_INFO_CLASS(pcidev->pcidev_info.info_ccfn));
+ show_value(6, "function", "0x%02x",
+ MCA_PCIDEV_INFO_FUNCTION(pcidev->pcidev_info.info_ccfn));
+ show_value(6, "slot", "0x%02x", pcidev->pcidev_info.info_slot);
+ show_value(6, "bus", "0x%04x", pcidev->pcidev_info.info_bus);
+ show_value(6, "segment", "0x%04x",
+ pcidev->pcidev_info.info_segment);
+ }
+ /* TODO: dump registers */
+ /* TODO: Dump OEM data */
+
+ printf(" </pci-dev>\n");
+}
+
+static void
+show_generic(void)
+{
+ printf(" # GENERIC\n");
+}
+
+static size_t
+show_section(struct mca_section_header *sh)
+{
+ static uuid_t uuid_cpu = MCA_UUID_CPU;
+ static uuid_t uuid_memory = MCA_UUID_MEMORY;
+ static uuid_t uuid_sel = MCA_UUID_SEL;
+ static uuid_t uuid_pci_bus = MCA_UUID_PCI_BUS;
+ static uuid_t uuid_smbios = MCA_UUID_SMBIOS;
+ static uuid_t uuid_pci_dev = MCA_UUID_PCI_DEV;
+ static uuid_t uuid_generic = MCA_UUID_GENERIC;
+
+ printf(" <section>\n");
+ show_value(4, "uuid", "%s", uuid(&sh->sh_uuid));
+ show_value(4, "revision", "%d.%d", BCD(sh->sh_major),
+ BCD(sh->sh_minor));
+
+ if (uuid_equal(&sh->sh_uuid, &uuid_cpu, NULL))
+ show_cpu((void*)(sh + 1));
+ else if (uuid_equal(&sh->sh_uuid, &uuid_memory, NULL))
+ show_memory((void*)(sh + 1));
+ else if (uuid_equal(&sh->sh_uuid, &uuid_sel, NULL))
+ show_sel();
+ else if (uuid_equal(&sh->sh_uuid, &uuid_pci_bus, NULL))
+ show_pci_bus((void*)(sh + 1));
+ else if (uuid_equal(&sh->sh_uuid, &uuid_smbios, NULL))
+ show_smbios();
+ else if (uuid_equal(&sh->sh_uuid, &uuid_pci_dev, NULL))
+ show_pci_dev((void*)(sh + 1));
+ else if (uuid_equal(&sh->sh_uuid, &uuid_generic, NULL))
+ show_generic();
+
+ printf(" </section>\n");
+ return (sh->sh_length);
+}
+
+static void
+show(char *data)
+{
+ size_t reclen, seclen;
+
+ printf("<record>\n");
+ reclen = show_header((void*)data) - sizeof(struct mca_record_header);
+ data += sizeof(struct mca_record_header);
+ while (reclen > sizeof(struct mca_section_header)) {
+ seclen = show_section((void*)data);
+ reclen -= seclen;
+ data += seclen;
+ }
+ printf("</record>\n");
+}
+
+static void
+showall(char *buf, size_t buflen)
+{
+ struct mca_record_header *rh;
+ size_t reclen;
+
+ do {
+ if (buflen < sizeof(struct mca_record_header))
+ return;
+
+ rh = (void*)buf;
+ reclen = rh->rh_length;
+ if (buflen < reclen)
+ return;
+
+ show(buf);
+
+ buf += reclen;
+ buflen -= reclen;
+ }
+ while (1);
+}
+
+static void
+dump(char *data)
+{
+ struct mca_record_header *rh;
+ const char *fn;
+ int fd;
+
+ rh = (void*)data;
+ fn = (file) ? file : default_dumpfile;
+ fd = open(fn, O_WRONLY|O_CREAT|O_APPEND, 0660);
+ if (fd == -1)
+ err(2, "open(%s)", fn);
+ if (write(fd, (void*)rh, rh->rh_length) == -1)
+ err(2, "write(%s)", fn);
+ close(fd);
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: mca [-df]\n");
+ exit (1);
+}
+
+int
+main(int argc, char **argv)
+{
+ char mib[32];
+ char *buf;
+ size_t len;
+ int ch, error, fd;
+ int count, first, last;
+
+ while ((ch = getopt(argc, argv, "df:")) != -1) {
+ switch(ch) {
+ case 'd': /* dump */
+ fl_dump = 1;
+ break;
+ case 'f':
+ if (file)
+ free(file); /* XXX complain! */
+ file = strdup(optarg);
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (file == NULL || fl_dump) {
+ len = sizeof(count);
+ error = sysctlbyname(hw_mca_count, &count, &len, NULL, 0);
+ if (error)
+ err(1, hw_mca_count);
+
+ if (count == 0)
+ errx(0, "no error records found");
+
+ len = sizeof(first);
+ error = sysctlbyname(hw_mca_first, &first, &len, NULL, 0);
+ if (error)
+ err(1, hw_mca_first);
+
+ len = sizeof(last);
+ error = sysctlbyname(hw_mca_last, &last, &len, NULL, 0);
+ if (error)
+ err(1, hw_mca_last);
+
+ while (count && first <= last) {
+ sprintf(mib, hw_mca_recid, first);
+ len = 0;
+ error = sysctlbyname(mib, NULL, &len, NULL, 0);
+ if (error == ENOENT) {
+ first++;
+ continue;
+ }
+ if (error)
+ err(1, "%s(1)", mib);
+
+ buf = malloc(len);
+ if (buf == NULL)
+ err(1, "buffer");
+
+ error = sysctlbyname(mib, buf, &len, NULL, 0);
+ if (error)
+ err(1, "%s(2)", mib);
+
+ if (fl_dump)
+ dump(buf);
+ else
+ show(buf);
+
+ free(buf);
+ first++;
+ count--;
+ }
+ } else {
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ err(1, "open(%s)", file);
+
+ len = lseek(fd, 0LL, SEEK_END);
+ buf = mmap(NULL, len, PROT_READ, 0U, fd, 0LL);
+ if (buf == MAP_FAILED)
+ err(1, "mmap(%s)", file);
+
+ showall(buf, len);
+
+ munmap(buf, len);
+ close(fd);
+ }
+
+ return (0);
+}
diff --git a/sbin/md5/Makefile b/sbin/md5/Makefile
new file mode 100644
index 0000000..07c4109
--- /dev/null
+++ b/sbin/md5/Makefile
@@ -0,0 +1,20 @@
+# @(#)Makefile 8.1 (Berkeley) 6/9/93
+# $FreeBSD$
+
+PROG= md5
+
+LINKS= ${BINDIR}/md5 ${BINDIR}/rmd160 \
+ ${BINDIR}/md5 ${BINDIR}/sha1 \
+ ${BINDIR}/md5 ${BINDIR}/sha256
+
+MLINKS= md5.1 rmd160.1 \
+ md5.1 sha1.1 \
+ md5.1 sha256.1
+
+WARNS?= 6
+WFORMAT?= 1
+
+DPADD= ${LIBMD}
+LDADD= -lmd
+
+.include <bsd.prog.mk>
diff --git a/sbin/md5/md5.1 b/sbin/md5/md5.1
new file mode 100644
index 0000000..a771fb7
--- /dev/null
+++ b/sbin/md5/md5.1
@@ -0,0 +1,132 @@
+.\" $FreeBSD$
+.Dd June 6, 2004
+.Dt MD5 1
+.Os
+.Sh NAME
+.Nm md5 , sha1 , sha256, rmd160
+.Nd calculate a message-digest fingerprint (checksum) for a file
+.Sh SYNOPSIS
+.Nm md5
+.Op Fl pqrtx
+.Op Fl s Ar string
+.Op Ar
+.Nm sha1
+.Op Fl pqrtx
+.Op Fl s Ar string
+.Op Ar
+.Nm sha256
+.Op Fl pqrtx
+.Op Fl s Ar string
+.Op Ar
+.Nm rmd160
+.Op Fl pqrtx
+.Op Fl s Ar string
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm md5 , sha1 , sha256
+and
+.Nm rmd160
+utilities take as input a message of arbitrary length and produce as
+output a
+.Dq fingerprint
+or
+.Dq message digest
+of the input.
+It is conjectured that it is computationally infeasible to
+produce two messages having the same message digest, or to produce any
+message having a given prespecified target message digest.
+The
+.Tn MD5 , SHA-1 , SHA-256
+and
+.Tn RIPEMD-160
+algorithms are intended for digital signature applications, where a
+large file must be
+.Dq compressed
+in a secure manner before being encrypted with a private
+(secret)
+key under a public-key cryptosystem such as
+.Tn RSA .
+.Pp
+.Tn MD5
+has not yet (2001-09-03) been broken, but sufficient attacks have been
+made that its security is in some doubt.
+The attacks on
+.Tn MD5
+are in the nature of finding
+.Dq collisions
+\(em that is, multiple
+inputs which hash to the same value; it is still unlikely for an attacker
+to be able to determine the exact original input given a hash value.
+.Pp
+The following options may be used in any combination and must
+precede any files named on the command line.
+The hexadecimal checksum of each file listed on the command line is printed
+after the options are processed.
+.Bl -tag -width indent
+.It Fl s Ar string
+Print a checksum of the given
+.Ar string .
+.It Fl p
+Echo stdin to stdout and append the checksum to stdout.
+.It Fl q
+Quiet mode - only the checksum is printed out.
+Overrides the
+.Fl r
+option.
+.It Fl r
+Reverses the format of the output.
+This helps with visual diffs.
+Does nothing
+when combined with the
+.Fl ptx
+options.
+.It Fl t
+Run a built-in time trial.
+.It Fl x
+Run a built-in test script.
+.El
+.Sh EXIT STATUS
+The
+.Nm md5 , sha1 , sha256
+and
+.Nm rmd160
+utilities exit 0 on success,
+and 1 if at least one of the input files could not be read.
+.Sh SEE ALSO
+.Xr cksum 1 ,
+.Xr md5 3 ,
+.Xr ripemd 3 ,
+.Xr sha 3 ,
+.Xr sha256 3
+.Rs
+.%A R. Rivest
+.%T The MD5 Message-Digest Algorithm
+.%O RFC1321
+.Re
+.Rs
+.%A J. Burrows
+.%T The Secure Hash Standard
+.%O FIPS PUB 180-2
+.Re
+.Rs
+.%A D. Eastlake and P. Jones
+.%T US Secure Hash Algorithm 1
+.%O RFC 3174
+.Re
+.Pp
+RIPEMD-160 is part of the ISO draft standard
+.Qq ISO/IEC DIS 10118-3
+on dedicated hash functions.
+.Pp
+Secure Hash Standard (SHS):
+.Pa http://csrc.nist.gov/cryptval/shs.html .
+.Pp
+The RIPEMD-160 page:
+.Pa http://www.esat.kuleuven.ac.be/~bosselae/ripemd160.html .
+.Sh ACKNOWLEDGMENTS
+This program is placed in the public domain for free general use by
+RSA Data Security.
+.Pp
+Support for SHA-1 and RIPEMD-160 has been added by
+.An Oliver Eikemeier Aq eik@FreeBSD.org .
diff --git a/sbin/md5/md5.c b/sbin/md5/md5.c
new file mode 100644
index 0000000..fa853fb
--- /dev/null
+++ b/sbin/md5/md5.c
@@ -0,0 +1,354 @@
+/*
+ * Derived from:
+ *
+ * MDDRIVER.C - test driver for MD2, MD4 and MD5
+ */
+
+/*
+ * Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All
+ * rights reserved.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <err.h>
+#include <md5.h>
+#include <ripemd.h>
+#include <sha.h>
+#include <sha256.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+/*
+ * Length of test block, number of test blocks.
+ */
+#define TEST_BLOCK_LEN 10000
+#define TEST_BLOCK_COUNT 100000
+#define MDTESTCOUNT 8
+
+int qflag;
+int rflag;
+int sflag;
+
+typedef void (DIGEST_Init)(void *);
+typedef void (DIGEST_Update)(void *, const unsigned char *, size_t);
+typedef char *(DIGEST_End)(void *, char *);
+
+extern const char *MD5TestOutput[MDTESTCOUNT];
+extern const char *SHA1_TestOutput[MDTESTCOUNT];
+extern const char *SHA256_TestOutput[MDTESTCOUNT];
+extern const char *RIPEMD160_TestOutput[MDTESTCOUNT];
+
+typedef struct Algorithm_t {
+ const char *progname;
+ const char *name;
+ const char *(*TestOutput)[MDTESTCOUNT];
+ DIGEST_Init *Init;
+ DIGEST_Update *Update;
+ DIGEST_End *End;
+ char *(*Data)(const void *, unsigned int, char *);
+ char *(*File)(const char *, char *);
+} Algorithm_t;
+
+static void MD5_Update(MD5_CTX *, const unsigned char *, size_t);
+static void MDString(Algorithm_t *, const char *);
+static void MDTimeTrial(Algorithm_t *);
+static void MDTestSuite(Algorithm_t *);
+static void MDFilter(Algorithm_t *, int);
+static void usage(Algorithm_t *);
+
+typedef union {
+ MD5_CTX md5;
+ SHA1_CTX sha1;
+ SHA256_CTX sha256;
+ RIPEMD160_CTX ripemd160;
+} DIGEST_CTX;
+
+/* max(MD5_DIGEST_LENGTH, SHA_DIGEST_LENGTH,
+ SHA256_DIGEST_LENGTH, RIPEMD160_DIGEST_LENGTH)*2+1 */
+#define HEX_DIGEST_LENGTH 65
+
+/* algorithm function table */
+
+struct Algorithm_t Algorithm[] = {
+ { "md5", "MD5", &MD5TestOutput, (DIGEST_Init*)&MD5Init,
+ (DIGEST_Update*)&MD5_Update, (DIGEST_End*)&MD5End,
+ &MD5Data, &MD5File },
+ { "sha1", "SHA1", &SHA1_TestOutput, (DIGEST_Init*)&SHA1_Init,
+ (DIGEST_Update*)&SHA1_Update, (DIGEST_End*)&SHA1_End,
+ &SHA1_Data, &SHA1_File },
+ { "sha256", "SHA256", &SHA256_TestOutput, (DIGEST_Init*)&SHA256_Init,
+ (DIGEST_Update*)&SHA256_Update, (DIGEST_End*)&SHA256_End,
+ &SHA256_Data, &SHA256_File },
+ { "rmd160", "RMD160", &RIPEMD160_TestOutput,
+ (DIGEST_Init*)&RIPEMD160_Init, (DIGEST_Update*)&RIPEMD160_Update,
+ (DIGEST_End*)&RIPEMD160_End, &RIPEMD160_Data, &RIPEMD160_File }
+};
+
+static void
+MD5_Update(MD5_CTX *c, const unsigned char *data, size_t len)
+{
+ MD5Update(c, data, len);
+}
+
+/* Main driver.
+
+Arguments (may be any combination):
+ -sstring - digests string
+ -t - runs time trial
+ -x - runs test script
+ filename - digests file
+ (none) - digests standard input
+ */
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ char *p;
+ char buf[HEX_DIGEST_LENGTH];
+ int failed;
+ unsigned digest;
+ const char* progname;
+
+ if ((progname = strrchr(argv[0], '/')) == NULL)
+ progname = argv[0];
+ else
+ progname++;
+
+ for (digest = 0; digest < sizeof(Algorithm)/sizeof(*Algorithm); digest++)
+ if (strcasecmp(Algorithm[digest].progname, progname) == 0)
+ break;
+
+ if (digest == sizeof(Algorithm)/sizeof(*Algorithm))
+ digest = 0;
+
+ failed = 0;
+ while ((ch = getopt(argc, argv, "pqrs:tx")) != -1)
+ switch (ch) {
+ case 'p':
+ MDFilter(&Algorithm[digest], 1);
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ MDString(&Algorithm[digest], optarg);
+ break;
+ case 't':
+ MDTimeTrial(&Algorithm[digest]);
+ break;
+ case 'x':
+ MDTestSuite(&Algorithm[digest]);
+ break;
+ default:
+ usage(&Algorithm[digest]);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (*argv) {
+ do {
+ p = Algorithm[digest].File(*argv, buf);
+ if (!p) {
+ warn("%s", *argv);
+ failed++;
+ } else {
+ if (qflag)
+ printf("%s\n", p);
+ else if (rflag)
+ printf("%s %s\n", p, *argv);
+ else
+ printf("%s (%s) = %s\n", Algorithm[digest].name, *argv, p);
+ }
+ } while (*++argv);
+ } else if (!sflag && (optind == 1 || qflag || rflag))
+ MDFilter(&Algorithm[digest], 0);
+
+ if (failed != 0)
+ return (1);
+
+ return (0);
+}
+/*
+ * Digests a string and prints the result.
+ */
+static void
+MDString(Algorithm_t *alg, const char *string)
+{
+ size_t len = strlen(string);
+ char buf[HEX_DIGEST_LENGTH];
+
+ if (qflag)
+ printf("%s\n", alg->Data(string, len, buf));
+ else if (rflag)
+ printf("%s \"%s\"\n", alg->Data(string, len, buf), string);
+ else
+ printf("%s (\"%s\") = %s\n", alg->name, string, alg->Data(string, len, buf));
+}
+/*
+ * Measures the time to digest TEST_BLOCK_COUNT TEST_BLOCK_LEN-byte blocks.
+ */
+static void
+MDTimeTrial(Algorithm_t *alg)
+{
+ DIGEST_CTX context;
+ struct rusage before, after;
+ struct timeval total;
+ float seconds;
+ unsigned char block[TEST_BLOCK_LEN];
+ unsigned int i;
+ char *p, buf[HEX_DIGEST_LENGTH];
+
+ printf
+ ("%s time trial. Digesting %d %d-byte blocks ...",
+ alg->name, TEST_BLOCK_COUNT, TEST_BLOCK_LEN);
+ fflush(stdout);
+
+ /* Initialize block */
+ for (i = 0; i < TEST_BLOCK_LEN; i++)
+ block[i] = (unsigned char) (i & 0xff);
+
+ /* Start timer */
+ getrusage(0, &before);
+
+ /* Digest blocks */
+ alg->Init(&context);
+ for (i = 0; i < TEST_BLOCK_COUNT; i++)
+ alg->Update(&context, block, TEST_BLOCK_LEN);
+ p = alg->End(&context, buf);
+
+ /* Stop timer */
+ getrusage(0, &after);
+ timersub(&after.ru_utime, &before.ru_utime, &total);
+ seconds = total.tv_sec + (float) total.tv_usec / 1000000;
+
+ printf(" done\n");
+ printf("Digest = %s", p);
+ printf("\nTime = %f seconds\n", seconds);
+ printf
+ ("Speed = %f bytes/second\n",
+ (float) TEST_BLOCK_LEN * (float) TEST_BLOCK_COUNT / seconds);
+}
+/*
+ * Digests a reference suite of strings and prints the results.
+ */
+
+const char *MDTestInput[MDTESTCOUNT] = {
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ "MD5 has not yet (2001-09-03) been broken, but sufficient attacks have been made \
+that its security is in some doubt"
+};
+
+const char *MD5TestOutput[MDTESTCOUNT] = {
+ "d41d8cd98f00b204e9800998ecf8427e",
+ "0cc175b9c0f1b6a831c399e269772661",
+ "900150983cd24fb0d6963f7d28e17f72",
+ "f96b697d7cb7938d525a2f31aaf161d0",
+ "c3fcd3d76192e4007dfb496cca67e13b",
+ "d174ab98d277d9f5a5611c2c9f419d9f",
+ "57edf4a22be3c955ac49da2e2107b67a",
+ "b50663f41d44d92171cb9976bc118538"
+};
+
+const char *SHA1_TestOutput[MDTESTCOUNT] = {
+ "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+ "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
+ "a9993e364706816aba3e25717850c26c9cd0d89d",
+ "c12252ceda8be8994d5fa0290a47231c1d16aae3",
+ "32d10c7b8cf96570ca04ce37f2a19d84240d3a89",
+ "761c457bf73b14d27e9e9265c46f4b4dda11f940",
+ "50abf5706a150990a08b2c5ea40fa0e585554732",
+ "18eca4333979c4181199b7b4fab8786d16cf2846"
+};
+
+const char *SHA256_TestOutput[MDTESTCOUNT] = {
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
+ "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
+ "f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650",
+ "71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73",
+ "db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0",
+ "f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e",
+ "e6eae09f10ad4122a0e2a4075761d185a272ebd9f5aa489e998ff2f09cbfdd9f"
+};
+
+const char *RIPEMD160_TestOutput[MDTESTCOUNT] = {
+ "9c1185a5c5e9fc54612808977ee8f548b2258d31",
+ "0bdc9d2d256b3ee9daae347be6f4dc835a467ffe",
+ "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc",
+ "5d0689ef49d2fae572b881b123a85ffa21595f36",
+ "f71c27109c692c1b56bbdceb5b9d2865b3708dbc",
+ "b0e20b6e3116640286ed3a87a5713079b21f5189",
+ "9b752e45573d4b39f4dbd3323cab82bf63326bfb",
+ "5feb69c6bf7c29d95715ad55f57d8ac5b2b7dd32"
+};
+
+static void
+MDTestSuite(Algorithm_t *alg)
+{
+ int i;
+ char buffer[HEX_DIGEST_LENGTH];
+
+ printf("%s test suite:\n", alg->name);
+ for (i = 0; i < MDTESTCOUNT; i++) {
+ (*alg->Data)(MDTestInput[i], strlen(MDTestInput[i]), buffer);
+ printf("%s (\"%s\") = %s", alg->name, MDTestInput[i], buffer);
+ if (strcmp(buffer, (*alg->TestOutput)[i]) == 0)
+ printf(" - verified correct\n");
+ else
+ printf(" - INCORRECT RESULT!\n");
+ }
+}
+
+/*
+ * Digests the standard input and prints the result.
+ */
+static void
+MDFilter(Algorithm_t *alg, int tee)
+{
+ DIGEST_CTX context;
+ unsigned int len;
+ unsigned char buffer[BUFSIZ];
+ char buf[HEX_DIGEST_LENGTH];
+
+ alg->Init(&context);
+ while ((len = fread(buffer, 1, BUFSIZ, stdin))) {
+ if (tee && len != fwrite(buffer, 1, len, stdout))
+ err(1, "stdout");
+ alg->Update(&context, buffer, len);
+ }
+ printf("%s\n", alg->End(&context, buf));
+}
+
+static void
+usage(Algorithm_t *alg)
+{
+
+ fprintf(stderr, "usage: %s [-pqrtx] [-s string] [files ...]\n", alg->progname);
+ exit(1);
+}
diff --git a/sbin/mdconfig/Makefile b/sbin/mdconfig/Makefile
new file mode 100644
index 0000000..1d5aff2
--- /dev/null
+++ b/sbin/mdconfig/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= mdconfig
+MAN= mdconfig.8
+MLINKS= mdconfig.8 vnconfig.8
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+.include <bsd.prog.mk>
diff --git a/sbin/mdconfig/mdconfig.8 b/sbin/mdconfig/mdconfig.8
new file mode 100644
index 0000000..7e72275
--- /dev/null
+++ b/sbin/mdconfig/mdconfig.8
@@ -0,0 +1,227 @@
+.\" Copyright (c) 1993 University of Utah.
+.\" Copyright (c) 1980, 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\" Copyright (c) 2000
+.\" Poul-Henning Kamp All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Systems Programming Group of the University of Utah Computer
+.\" Science Department.
+.\"
+.\" 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 the University of
+.\" California, Berkeley and its contributors.
+.\" 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 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.
+.\"
+.\" @(#)vnconfig.8 8.1 (Berkeley) 6/5/93
+.\" from: src/usr.sbin/vnconfig/vnconfig.8,v 1.19 2000/12/27 15:30:29
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 6, 2004
+.Dt MDCONFIG 8
+.Os
+.Sh NAME
+.Nm mdconfig
+.Nd configure and enable memory disks
+.Sh SYNOPSIS
+.Nm
+.Fl a
+.Fl t Ar type
+.Op Fl n
+.Oo Fl o Oo Cm no Oc Ns Ar option Oc ...
+.Op Fl f Ar file
+.Op Fl s Ar size
+.Op Fl S Ar sectorsize
+.Op Fl u Ar unit
+.Op Fl x Ar sectors/track
+.Op Fl y Ar heads/cyl
+.Nm
+.Fl d
+.Fl u Ar unit
+.Nm
+.Fl l
+.Op Fl n
+.Op Fl u Ar unit
+.Sh DESCRIPTION
+The
+.Nm
+utility configures and enables
+.Xr md 4
+devices.
+.Pp
+Options indicate an action to be performed:
+.Bl -tag -width indent
+.It Fl a
+Attach a memory disk.
+This will configure and attach a memory disk with the
+parameters specified and attach it to the system.
+.It Fl d
+Detach a memory disk from the system and release all resources.
+.It Fl t Ar type
+Select the type of the memory disk.
+.Bl -tag -width "preload"
+.It Cm malloc
+Storage for this type of memory disk is allocated with
+.Xr malloc 9 .
+This limits the size to the malloc bucket limit in the kernel.
+If the
+.Fl o Cm reserve
+option is not set, creating and filling a large
+malloc-backed memory disk is a very easy way to
+panic a system.
+.It Cm vnode
+A file specified with
+.Fl f Ar file
+becomes the backingstore for this memory disk.
+.It Cm swap
+Swap space is used to back this memory disk.
+.El
+.It Fl f Ar file
+Filename to use for the vnode type memory disk.
+.It Fl l
+List configured devices.
+If given with
+.Fl u ,
+display details about that particular device.
+.It Fl n
+When printing md device names, print only the unit number without the
+md prefix.
+.It Fl s Ar size
+Size of the memory disk.
+.Ar Size
+is the number of 512 byte sectors unless suffixed with a
+.Cm b , k , m , g ,
+or
+.Cm t
+which
+denotes byte, kilobyte, megabyte, gigabyte and terabyte respectively.
+.It Fl S Ar sectorsize
+Sectorsize to use for malloc backed device.
+.It Fl x Ar sectors/track
+See the description of the
+.Fl y
+option below.
+.It Fl y Ar heads/cylinder
+For
+.Cm malloc
+or
+.Cm vnode
+backed devices, the
+.Fl x
+and
+.Fl y
+options can be used to specify a synthetic geometry.
+This is useful for constructing bootable images for later download to
+other devices.
+.It Fl o Oo Cm no Oc Ns Ar option
+Set or reset options.
+.Bl -tag -width indent
+.It Oo Cm no Oc Ns Cm async
+For
+.Cm vnode
+backed devices: avoid
+.Dv IO_SYNC
+for increased performance but
+at the risk of deadlocking the entire kernel.
+.It Oo Cm no Oc Ns Cm reserve
+Allocate and reserve all needed storage from the start, rather than as needed.
+.It Oo Cm no Oc Ns Cm cluster
+Enable clustering on this disk.
+.It Oo Cm no Oc Ns Cm compress
+Enable/Disable compression features to reduce memory usage.
+.It Oo Cm no Oc Ns Cm force
+Disable/Enable extra sanity checks to prevent the user from doing something
+that might adversely affect the system.
+.It Oo Cm no Oc Ns Cm readonly
+Enable/Disable readonly mode.
+.El
+.It Fl u Ar unit
+Request a specific unit number for the
+.Xr md 4
+device instead of automatic allocation.
+.El
+.Sh EXAMPLES
+To create a 4 megabyte
+.Xr malloc 9
+backed memory disk.
+The name of the allocated unit will be output on stdout like
+.Dq Li md3 :
+.Pp
+.Dl mdconfig -a -t malloc -s 4m
+.Pp
+To create a disk named
+.Pa /dev/md4
+with
+.Pa /tmp/boot.flp
+as backing:
+.Pp
+.Dl mdconfig -a -t vnode -f /tmp/boot.flp -u 4
+.Pp
+To detach and free all resources used by
+.Pa /dev/md4 :
+.Pp
+.Dl mdconfig -d -u 4
+.Pp
+To create and mount a 128MByte swap backed file system on
+.Pa /tmp :
+.Bd -literal -offset indent
+mdconfig -a -t swap -s 128M -u 10
+newfs -U /dev/md10
+mount /dev/md10 /tmp
+chmod 1777 /tmp
+.Ed
+.Pp
+To create a 5MB file-backed disk:
+.Bd -literal -offset indent
+dd if=/dev/zero of=somebackingfile bs=1k count=5k
+mdconfig -a -t vnode -f somebackingfile -u 0
+bsdlabel -w md0 auto
+newfs md0c
+mount /dev/md0c /mnt
+.Ed
+.Sh SEE ALSO
+.Xr md 4 ,
+.Xr bsdlabel 8 ,
+.Xr fdisk 8 ,
+.Xr mdmfs 8 ,
+.Xr malloc 9
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.0
+as a cleaner replacement for the
+.Xr vn 4
+and
+.Xr vnconfig 8
+combo.
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Poul-Henning Kamp
+.Aq phk@FreeBSD.org .
diff --git a/sbin/mdconfig/mdconfig.c b/sbin/mdconfig/mdconfig.c
new file mode 100644
index 0000000..2064adf
--- /dev/null
+++ b/sbin/mdconfig/mdconfig.c
@@ -0,0 +1,360 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <libutil.h>
+#include <string.h>
+#include <err.h>
+#include <assert.h>
+
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/module.h>
+#include <sys/linker.h>
+#include <sys/mdioctl.h>
+#include <sys/stat.h>
+
+int list(const int);
+void mdmaybeload(void);
+int query(const int, const int);
+void usage(void);
+
+struct md_ioctl mdio;
+
+enum {UNSET, ATTACH, DETACH, LIST} action = UNSET;
+
+int nflag;
+
+void
+usage()
+{
+ fprintf(stderr,
+"usage: mdconfig -a -t type [-n] [-o [no]option] ... [-f file]\n"
+" [-s size] [-S sectorsize] [-u unit]\n"
+" [-x sectors/track] [-y heads/cyl]\n"
+" mdconfig -d -u unit\n"
+" mdconfig -l [-n] [-u unit]\n");
+ fprintf(stderr, "\t\ttype = {malloc, preload, vnode, swap}\n");
+ fprintf(stderr, "\t\toption = {cluster, compress, reserve}\n");
+ fprintf(stderr, "\t\tsize = %%d (512 byte blocks), %%db (B),\n");
+ fprintf(stderr, "\t\t %%dk (kB), %%dm (MB), %%dg (GB) or\n");
+ fprintf(stderr, "\t\t %%dt (TB)\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch, fd, i;
+ char *p;
+ int cmdline = 0;
+
+ bzero(&mdio, sizeof(mdio));
+ mdio.md_file = malloc(PATH_MAX);
+ if (mdio.md_file == NULL)
+ err(1, "could not allocate memory");
+ bzero(mdio.md_file, PATH_MAX);
+ for (;;) {
+ ch = getopt(argc, argv, "ab:df:lno:s:S:t:u:x:y:");
+ if (ch == -1)
+ break;
+ switch (ch) {
+ case 'a':
+ if (cmdline != 0)
+ usage();
+ action = ATTACH;
+ cmdline = 1;
+ break;
+ case 'd':
+ if (cmdline != 0)
+ usage();
+ action = DETACH;
+ mdio.md_options = MD_AUTOUNIT;
+ cmdline = 3;
+ break;
+ case 'l':
+ if (cmdline != 0)
+ usage();
+ action = LIST;
+ mdio.md_options = MD_AUTOUNIT;
+ cmdline = 3;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 't':
+ if (cmdline != 1)
+ usage();
+ if (!strcmp(optarg, "malloc")) {
+ mdio.md_type = MD_MALLOC;
+ mdio.md_options = MD_AUTOUNIT | MD_COMPRESS;
+ } else if (!strcmp(optarg, "preload")) {
+ mdio.md_type = MD_PRELOAD;
+ mdio.md_options = 0;
+ } else if (!strcmp(optarg, "vnode")) {
+ mdio.md_type = MD_VNODE;
+ mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
+ } else if (!strcmp(optarg, "swap")) {
+ mdio.md_type = MD_SWAP;
+ mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
+ } else {
+ usage();
+ }
+ cmdline=2;
+ break;
+ case 'f':
+ if (cmdline != 1 && cmdline != 2)
+ usage();
+ if (cmdline == 1) {
+ /* Imply ``-t vnode'' */
+ mdio.md_type = MD_VNODE;
+ mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
+ cmdline = 2;
+ }
+ if (realpath(optarg, mdio.md_file) == NULL) {
+ err(1, "could not find full path for %s",
+ optarg);
+ }
+ fd = open(mdio.md_file, O_RDONLY);
+ if (fd < 0)
+ err(1, "could not open %s", optarg);
+ else if (mdio.md_mediasize == 0) {
+ struct stat sb;
+
+ if (fstat(fd, &sb) == -1)
+ err(1, "could not stat %s", optarg);
+ mdio.md_mediasize = sb.st_size;
+ }
+ close(fd);
+ break;
+ case 'o':
+ if (cmdline != 2)
+ usage();
+ if (!strcmp(optarg, "async"))
+ mdio.md_options |= MD_ASYNC;
+ else if (!strcmp(optarg, "noasync"))
+ mdio.md_options &= ~MD_ASYNC;
+ else if (!strcmp(optarg, "cluster"))
+ mdio.md_options |= MD_CLUSTER;
+ else if (!strcmp(optarg, "nocluster"))
+ mdio.md_options &= ~MD_CLUSTER;
+ else if (!strcmp(optarg, "compress"))
+ mdio.md_options |= MD_COMPRESS;
+ else if (!strcmp(optarg, "nocompress"))
+ mdio.md_options &= ~MD_COMPRESS;
+ else if (!strcmp(optarg, "force"))
+ mdio.md_options |= MD_FORCE;
+ else if (!strcmp(optarg, "noforce"))
+ mdio.md_options &= ~MD_FORCE;
+ else if (!strcmp(optarg, "readonly"))
+ mdio.md_options |= MD_READONLY;
+ else if (!strcmp(optarg, "noreadonly"))
+ mdio.md_options &= ~MD_READONLY;
+ else if (!strcmp(optarg, "reserve"))
+ mdio.md_options |= MD_RESERVE;
+ else if (!strcmp(optarg, "noreserve"))
+ mdio.md_options &= ~MD_RESERVE;
+ else
+ errx(1, "Unknown option: %s.", optarg);
+ break;
+ case 'S':
+ if (cmdline != 2)
+ usage();
+ mdio.md_sectorsize = strtoul(optarg, &p, 0);
+ break;
+ case 's':
+ if (cmdline != 2)
+ usage();
+ mdio.md_mediasize = (off_t)strtoumax(optarg, &p, 0);
+ if (p == NULL || *p == '\0')
+ mdio.md_mediasize *= DEV_BSIZE;
+ else if (*p == 'b' || *p == 'B')
+ ; /* do nothing */
+ else if (*p == 'k' || *p == 'K')
+ mdio.md_mediasize <<= 10;
+ else if (*p == 'm' || *p == 'M')
+ mdio.md_mediasize <<= 20;
+ else if (*p == 'g' || *p == 'G')
+ mdio.md_mediasize <<= 30;
+ else if (*p == 't' || *p == 'T') {
+ mdio.md_mediasize <<= 30;
+ mdio.md_mediasize <<= 10;
+ } else
+ errx(1, "Unknown suffix on -s argument");
+ break;
+ case 'u':
+ if (cmdline != 2 && cmdline != 3)
+ usage();
+ if (!strncmp(optarg, "/dev/", 5))
+ optarg += 5;
+ if (!strncmp(optarg, MD_NAME, sizeof(MD_NAME) - 1))
+ optarg += sizeof(MD_NAME) - 1;
+ mdio.md_unit = strtoul(optarg, &p, 0);
+ if (mdio.md_unit == (unsigned)ULONG_MAX || *p != '\0')
+ errx(1, "bad unit: %s", optarg);
+ mdio.md_options &= ~MD_AUTOUNIT;
+ break;
+ case 'x':
+ if (cmdline != 2)
+ usage();
+ mdio.md_fwsectors = strtoul(optarg, &p, 0);
+ break;
+ case 'y':
+ if (cmdline != 2)
+ usage();
+ mdio.md_fwheads = strtoul(optarg, &p, 0);
+ break;
+ default:
+ usage();
+ }
+ }
+ mdio.md_version = MDIOVERSION;
+
+ mdmaybeload();
+ fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
+ if (fd < 0)
+ err(1, "open(/dev/%s)", MDCTL_NAME);
+ if (cmdline == 2
+ && (mdio.md_type == MD_MALLOC || mdio.md_type == MD_SWAP))
+ if (mdio.md_mediasize == 0)
+ errx(1, "must specify -s for -t malloc or -t swap");
+ if (cmdline == 2 && mdio.md_type == MD_VNODE)
+ if (mdio.md_file[0] == '\0')
+ errx(1, "must specify -f for -t vnode");
+ if (mdio.md_type == MD_VNODE &&
+ (mdio.md_options & MD_READONLY) == 0) {
+ if (access(mdio.md_file, W_OK) < 0 &&
+ (errno == EACCES || errno == EPERM || errno == EROFS)) {
+ fprintf(stderr,
+ "WARNING: opening backing store: %s readonly\n",
+ mdio.md_file);
+ mdio.md_options |= MD_READONLY;
+ }
+ }
+ if (action == LIST) {
+ if (mdio.md_options & MD_AUTOUNIT)
+ list(fd);
+ else
+ query(fd, mdio.md_unit);
+ } else if (action == ATTACH) {
+ if (cmdline < 2)
+ usage();
+ i = ioctl(fd, MDIOCATTACH, &mdio);
+ if (i < 0)
+ err(1, "ioctl(/dev/%s)", MDCTL_NAME);
+ if (mdio.md_options & MD_AUTOUNIT)
+ printf("%s%d\n", nflag ? "" : MD_NAME, mdio.md_unit);
+ } else if (action == DETACH) {
+ if (mdio.md_options & MD_AUTOUNIT)
+ usage();
+ i = ioctl(fd, MDIOCDETACH, &mdio);
+ if (i < 0)
+ err(1, "ioctl(/dev/%s)", MDCTL_NAME);
+ } else
+ usage();
+ close (fd);
+ return (0);
+}
+
+static int
+mdunitcmp(const void *a, const void *b)
+{
+ return (*(int *)a - *(int *)b);
+}
+
+int
+list(const int fd)
+{
+ int unit;
+ int mdcount;
+
+ if (ioctl(fd, MDIOCLIST, &mdio) < 0)
+ err(1, "ioctl(/dev/%s)", MDCTL_NAME);
+ mdcount = mdio.md_pad[0];
+ assert(mdcount < MDNPAD - 1);
+ if (mdcount > 0)
+ qsort(&mdio.md_pad[1], mdcount, sizeof(mdio.md_pad[0]), mdunitcmp);
+ for (unit = 0; unit < mdcount; unit++) {
+ printf("%s%s%d", unit > 0 ? " " : "",
+ nflag ? "" : MD_NAME, mdio.md_pad[unit + 1]);
+ }
+ if (unit > 0)
+ printf("\n");
+ return (0);
+}
+
+static void
+prthumanval(int64_t bytes)
+{
+ char buf[6];
+
+ humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1),
+ bytes, "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+ (void)printf("%6s", buf);
+}
+
+int
+query(const int fd, const int unit)
+{
+
+ mdio.md_version = MDIOVERSION;
+ mdio.md_unit = unit;
+
+ if (ioctl(fd, MDIOCQUERY, &mdio) < 0)
+ err(1, "ioctl(/dev/%s)", MDCTL_NAME);
+
+ (void)printf("%s%d\t", MD_NAME, mdio.md_unit);
+ switch (mdio.md_type) {
+ case MD_MALLOC:
+ (void)printf("malloc");
+ break;
+ case MD_PRELOAD:
+ (void)printf("preload");
+ break;
+ case MD_SWAP:
+ (void)printf("swap");
+ break;
+ case MD_VNODE:
+ (void)printf("vnode");
+ break;
+ }
+ printf("\t");
+ prthumanval(mdio.md_mediasize);
+ if (mdio.md_type == MD_VNODE)
+ printf("\t%s", mdio.md_file);
+ printf("\n");
+
+ return (0);
+}
+
+void
+mdmaybeload(void)
+{
+ char name1[64], name2[64];
+
+ snprintf(name1, sizeof(name1), "g_%s", MD_NAME);
+ snprintf(name2, sizeof(name2), "geom_%s", MD_NAME);
+ if (modfind(name1) == -1) {
+ /* Not present in kernel, try loading it. */
+ if (kldload(name2) == -1 || modfind(name1) == -1) {
+ if (errno != EEXIST) {
+ errx(EXIT_FAILURE,
+ "%s module not available!", name2);
+ }
+ }
+ }
+}
diff --git a/sbin/mdmfs/Makefile b/sbin/mdmfs/Makefile
new file mode 100644
index 0000000..1ef7084
--- /dev/null
+++ b/sbin/mdmfs/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= mdmfs
+LINKS= ${BINDIR}/${PROG} ${BINDIR}/mount_mfs
+MAN= mdmfs.8
+MLINKS+= mdmfs.8 mount_mfs.8
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/sbin/mdmfs/mdmfs.8 b/sbin/mdmfs/mdmfs.8
new file mode 100644
index 0000000..3d42988
--- /dev/null
+++ b/sbin/mdmfs/mdmfs.8
@@ -0,0 +1,373 @@
+.\"
+.\" Copyright (c) 2001 Dima Dorfman.
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 2, 2006
+.Dt MDMFS 8
+.Os
+.Sh NAME
+.Nm mdmfs ,
+.Nm mount_mfs
+.Nd configure and mount an in-memory file system using the
+.Xr md 4
+driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl DLlMNPSUX
+.Op Fl a Ar maxcontig
+.Op Fl b Ar block-size
+.Op Fl c Ar cylinders
+.Op Fl d Ar rotdelay
+.Op Fl e Ar maxbpg
+.Op Fl F Ar file
+.Op Fl f Ar frag-size
+.Op Fl i Ar bytes
+.Op Fl m Ar percent-free
+.Op Fl n Ar rotational-positions
+.Op Fl O Ar optimization
+.Op Fl o Ar mount-options
+.Op Fl p Ar permissions
+.Op Fl s Ar size
+.Op Fl v Ar version
+.Op Fl w Ar user : Ns Ar group
+.Ar md-device
+.Ar mount-point
+.Nm
+.Fl C
+.Op Fl lNU
+.Op Fl a Ar maxcontig
+.Op Fl b Ar block-size
+.Op Fl c Ar cylinders
+.Op Fl d Ar rotdelay
+.Op Fl e Ar maxbpg
+.Op Fl F Ar file
+.Op Fl f Ar frag-size
+.Op Fl i Ar bytes
+.Op Fl m Ar percent-free
+.Op Fl n Ar rotational-positions
+.Op Fl O Ar optimization
+.Op Fl o Ar mount-options
+.Op Fl s Ar size
+.Op Fl v Ar version
+.Ar md-device
+.Ar mount-point
+.Sh DESCRIPTION
+The
+.Nm
+utility is designed to be a work-alike and look-alike of the deprecated
+.Xr mount_mfs 8 .
+The end result is essentially the same,
+but is accomplished in a completely different way.
+The
+.Nm
+utility configures an
+.Xr md 4
+disk using
+.Xr mdconfig 8 ,
+puts a UFS file system on it using
+.Xr newfs 8 ,
+and mounts it using
+.Xr mount 8 .
+All the command line options are passed to the appropriate program
+at the appropriate stage in order to achieve the desired effect.
+.Pp
+By default,
+.Nm
+creates a swap-based
+.Pq Dv MD_SWAP
+disk with soft-updates enabled
+and mounts it on
+.Ar mount-point .
+It uses the
+.Xr md 4
+device specified by
+.Ar md-device .
+If
+.Ar md-device
+is
+.Ql md
+(no unit number),
+it will use
+.Xr md 4 Ns 's
+auto-unit feature to automatically select an unused device.
+Unless otherwise specified with one of the options below,
+it uses the default arguments to all the helper programs.
+.Pp
+The following options are available.
+Where possible,
+the option letter matches the one used by
+.Xr mount_mfs 8
+for the same thing.
+.Bl -tag -width indent
+.It Fl a Ar maxcontig
+Specify the maximum number of contiguous blocks that will be laid
+out before forcing a rotational delay
+(see the
+.Fl d
+option).
+.It Fl b Ar block-size
+The block size of the file system, in bytes.
+.It Fl C
+Enable full compatibility mode with
+.Xr mount_mfs 8 .
+See the
+.Sx COMPATIBILITY
+section for more information.
+.It Fl c Ar cylinders
+The number of cylinders per cylinder group in the file system.
+.It Fl D
+If not using auto-unit,
+do not run
+.Xr mdconfig 8
+to try to detach the unit before attaching it.
+.It Fl d Ar rotdelay
+Specify the minimum time in milliseconds required to initiate another
+disk transfer on the same cylinder.
+Modern disks with read/write-behind achieve higher performance without
+this feature,
+so it is best to leave it at 0 milliseconds.
+.It Fl e Ar maxbpg
+Indicate the maximum number of blocks any single file can allocate
+out of a cylinder group before it is forced to begin allocating
+blocks from another cylinder group.
+.It Fl F Ar file
+Create a vnode-backed
+.Pq Dv MD_VNODE
+memory disk backed by
+.Ar file .
+.It Fl f Ar frag-size
+The fragment size of the file system in bytes.
+.It Fl i Ar bytes
+Number of bytes per inode.
+.It Fl l
+Enable multilabel MAC on the new file system.
+.It Fl L
+Show the output of the helper programs.
+By default,
+it is sent to
+.Pa /dev/null .
+.It Fl M
+Create a
+.Xr malloc 9
+backed disk
+.Pq Dv MD_MALLOC
+instead of a swap-backed disk.
+.It Fl m Ar percent-free
+The percentage of space reserved for the superuser.
+.It Fl N
+Do not actually run the helper programs.
+This is most useful in conjunction with
+.Fl X .
+.It Fl n Ar rotational-positions
+The default number of rotational positions to distinguish.
+.It Fl O Ar optimization
+Select the optimization preference;
+valid choices are
+.Cm space
+and
+.Cm time ,
+which will optimize for minimum space fragmentation and
+minimum time spent allocating blocks,
+respectively.
+.It Fl o Ar mount-options
+Specify the mount options with which to mount the file system.
+See
+.Xr mount 8
+for more information.
+.It Fl P
+Preserve the existing filesystem;
+do not run
+.Xr newfs 8 .
+This only makes sense if
+.Fl F
+is specified to create a vnode-backed disk.
+.It Fl p Ar permissions
+Set the file (directory) permissions of the mount point
+.Ar mount-point
+to
+.Ar permissions .
+The
+.Ar permissions
+argument can be in any of the mode formats recognized by
+.Xr chmod 1 .
+If symbolic permissions are specified,
+the operation characters
+.Dq +
+and
+.Dq -
+are interpreted relative to the initial permissions of
+.Dq a=rwx .
+.It Fl S
+Do not enable soft-updates on the file system.
+.It Fl s Ar size
+Specify the size of the disk to create.
+This only makes sense if
+.Fl F
+is
+.Em not
+specified.
+That is,
+this will work for the default swap-backed
+.Pq Dv MD_SWAP
+disks,
+and the optional
+.Pq Fl M
+.Xr malloc 9
+backed disks
+.Pq Dv MD_MALLOC .
+.It Fl U
+Enable soft-updates on the file system.
+This is the default, even in compatibility mode, and is accepted only
+for compatibility.
+It is only really useful to negate the
+.Fl S
+flag, should such a need occur.
+.It Fl v Ar version
+Specify the UFS version number for use on the file system; it may be
+either
+.Dv 1
+or
+.Dv 2 .
+The default is derived from the default of the
+.Xr newfs 8
+command.
+.It Fl w Ar user : Ns Ar group
+Set the owner and group to
+.Ar user
+and
+.Ar group ,
+respectively.
+The arguments have the same semantics as with
+.Xr chown 8 ,
+but specifying just a
+.Ar user
+or just a
+.Ar group
+is not supported.
+.It Fl X
+Print what command will be run before running it, and
+other assorted debugging information.
+.El
+.Pp
+The
+.Fl F
+and
+.Fl s
+options are passed to
+.Xr mdconfig 8
+as
+.Fl f
+and
+.Fl s ,
+respectively.
+The
+.Fl a , b , c , d , e , f , i , m
+and
+.Fl n
+options are passed to
+.Xr newfs 8
+with the same letter;
+the
+.Fl O
+option is passed to
+.Xr newfs 8
+as
+.Fl o .
+The
+.Fl o
+option is passed to
+.Xr mount 8
+with the same letter.
+See the programs that the options are passed to for more information
+on their semantics.
+.Sh EXAMPLES
+Create and mount a 32 megabyte swap-backed file system on
+.Pa /tmp :
+.Pp
+.Dl "mdmfs -s 32m md /tmp"
+.Pp
+The same file system created as an entry in
+.Pa /etc/fstab :
+.Pp
+.Dl "md /tmp mfs rw,-s32m 2 0"
+.Pp
+Create and mount a 16 megabyte malloc-backed file system on
+.Pa /tmp
+using the
+.Pa /dev/md1
+device;
+furthermore,
+do not use soft-updates on it and mount it
+.Cm async :
+.Pp
+.Dl "mdmfs -M -S -o async -s 16m md1 /tmp"
+.Sh COMPATIBILITY
+The
+.Nm
+utility, while designed to be fully compatible with
+.Xr mount_mfs 8 ,
+can be useful by itself.
+Since
+.Xr mount_mfs 8
+had some silly defaults, a
+.Dq full compatibility
+mode is provided for the case where bug-to-bug compatibility is desired.
+.Pp
+Full compatibility is enabled with the
+.Fl C
+flag,
+or by starting
+.Nm
+with the name
+.Li mount_mfs
+or
+.Li mfs
+(as returned by
+.Xr getprogname 3 ) .
+In this mode, only the options which would be accepted by
+.Xr mount_mfs 8
+are valid.
+Furthermore, the following behavior, as done by
+.Xr mount_mfs 8 ,
+is duplicated:
+.Bl -bullet -offset indent
+.It
+The file mode of
+.Ar mount-point
+is set to
+.Li 01777
+as if
+.Fl p Ar 1777
+was given on the command line.
+.El
+.Sh SEE ALSO
+.Xr md 4 ,
+.Xr fstab 5 ,
+.Xr mdconfig 8 ,
+.Xr mount 8 ,
+.Xr newfs 8
+.Sh AUTHORS
+.An Dima Dorfman
diff --git a/sbin/mdmfs/mdmfs.c b/sbin/mdmfs/mdmfs.c
new file mode 100644
index 0000000..33c35f9
--- /dev/null
+++ b/sbin/mdmfs/mdmfs.c
@@ -0,0 +1,689 @@
+/*
+ * Copyright (c) 2001 Dima Dorfman.
+ * 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.
+ */
+
+/*
+ * mdmfs (md/MFS) is a wrapper around mdconfig(8),
+ * newfs(8), and mount(8) that mimics the command line option set of
+ * the deprecated mount_mfs(8).
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/mdioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+typedef enum { false, true } bool;
+
+struct mtpt_info {
+ uid_t mi_uid;
+ bool mi_have_uid;
+ gid_t mi_gid;
+ bool mi_have_gid;
+ mode_t mi_mode;
+ bool mi_have_mode;
+};
+
+static bool compat; /* Full compatibility with mount_mfs? */
+static bool debug; /* Emit debugging information? */
+static bool loudsubs; /* Suppress output from helper programs? */
+static bool norun; /* Actually run the helper programs? */
+static int unit; /* The unit we're working with. */
+static const char *mdname; /* Name of memory disk device (e.g., "md"). */
+static size_t mdnamelen; /* Length of mdname. */
+
+static void argappend(char **, const char *, ...) __printflike(2, 3);
+static void debugprintf(const char *, ...) __printflike(1, 2);
+static void do_mdconfig_attach(const char *, const enum md_types);
+static void do_mdconfig_attach_au(const char *, const enum md_types);
+static void do_mdconfig_detach(void);
+static void do_mount(const char *, const char *);
+static void do_mtptsetup(const char *, struct mtpt_info *);
+static void do_newfs(const char *);
+static void extract_ugid(const char *, struct mtpt_info *);
+static int run(int *, const char *, ...) __printflike(2, 3);
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ struct mtpt_info mi; /* Mountpoint info. */
+ char *mdconfig_arg, *newfs_arg, /* Args to helper programs. */
+ *mount_arg;
+ enum md_types mdtype; /* The type of our memory disk. */
+ bool have_mdtype;
+ bool detach, softdep, autounit, newfs;
+ char *mtpoint, *unitstr;
+ char *p;
+ int ch;
+ void *set;
+ unsigned long ul;
+
+ /* Misc. initialization. */
+ (void)memset(&mi, '\0', sizeof(mi));
+ detach = true;
+ softdep = true;
+ autounit = false;
+ newfs = true;
+ have_mdtype = false;
+ mdtype = MD_SWAP;
+ mdname = MD_NAME;
+ mdnamelen = strlen(mdname);
+ /*
+ * Can't set these to NULL. They may be passed to the
+ * respective programs without modification. I.e., we may not
+ * receive any command-line options which will caused them to
+ * be modified.
+ */
+ mdconfig_arg = strdup("");
+ newfs_arg = strdup("");
+ mount_arg = strdup("");
+
+ /* If we were started as mount_mfs or mfs, imply -C. */
+ if (strcmp(getprogname(), "mount_mfs") == 0 ||
+ strcmp(getprogname(), "mfs") == 0)
+ compat = true;
+
+ while ((ch = getopt(argc, argv,
+ "a:b:Cc:Dd:e:F:f:hi:LlMm:Nn:O:o:p:PSs:t:Uv:w:X")) != -1)
+ switch (ch) {
+ case 'a':
+ argappend(&newfs_arg, "-a %s", optarg);
+ break;
+ case 'b':
+ argappend(&newfs_arg, "-b %s", optarg);
+ break;
+ case 'C':
+ if (compat)
+ usage();
+ compat = true;
+ break;
+ case 'c':
+ argappend(&newfs_arg, "-c %s", optarg);
+ break;
+ case 'D':
+ if (compat)
+ usage();
+ detach = false;
+ break;
+ case 'd':
+ argappend(&newfs_arg, "-d %s", optarg);
+ break;
+ case 'e':
+ argappend(&newfs_arg, "-e %s", optarg);
+ break;
+ case 'F':
+ if (have_mdtype)
+ usage();
+ mdtype = MD_VNODE;
+ have_mdtype = true;
+ argappend(&mdconfig_arg, "-f %s", optarg);
+ break;
+ case 'f':
+ argappend(&newfs_arg, "-f %s", optarg);
+ break;
+ case 'h':
+ usage();
+ break;
+ case 'i':
+ argappend(&newfs_arg, "-i %s", optarg);
+ break;
+ case 'L':
+ if (compat)
+ usage();
+ loudsubs = true;
+ break;
+ case 'l':
+ argappend(&newfs_arg, "-l");
+ break;
+ case 'M':
+ if (have_mdtype)
+ usage();
+ mdtype = MD_MALLOC;
+ have_mdtype = true;
+ break;
+ case 'm':
+ argappend(&newfs_arg, "-m %s", optarg);
+ break;
+ case 'N':
+ if (compat)
+ usage();
+ norun = true;
+ break;
+ case 'n':
+ argappend(&newfs_arg, "-n %s", optarg);
+ break;
+ case 'O':
+ argappend(&newfs_arg, "-o %s", optarg);
+ break;
+ case 'o':
+ argappend(&mount_arg, "-o %s", optarg);
+ break;
+ case 'P':
+ if (compat)
+ usage();
+ newfs = false;
+ break;
+ case 'p':
+ if (compat)
+ usage();
+ if ((set = setmode(optarg)) == NULL)
+ usage();
+ mi.mi_mode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO);
+ mi.mi_have_mode = true;
+ free(set);
+ break;
+ case 'S':
+ if (compat)
+ usage();
+ softdep = false;
+ break;
+ case 's':
+ argappend(&mdconfig_arg, "-s %s", optarg);
+ break;
+ case 'U':
+ softdep = true;
+ break;
+ case 'v':
+ argappend(&newfs_arg, "-O %s", optarg);
+ break;
+ case 'w':
+ if (compat)
+ usage();
+ extract_ugid(optarg, &mi);
+ break;
+ case 'X':
+ if (compat)
+ usage();
+ debug = true;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 2)
+ usage();
+
+ /* Make compatibility assumptions. */
+ if (compat) {
+ mi.mi_mode = 01777;
+ mi.mi_have_mode = true;
+ }
+
+ /* Derive 'unit' (global). */
+ unitstr = argv[0];
+ if (strncmp(unitstr, "/dev/", 5) == 0)
+ unitstr += 5;
+ if (strncmp(unitstr, mdname, mdnamelen) == 0)
+ unitstr += mdnamelen;
+ if (*unitstr == '\0') {
+ autounit = true;
+ unit = -1;
+ } else {
+ ul = strtoul(unitstr, &p, 10);
+ if (ul == ULONG_MAX || *p != '\0')
+ errx(1, "bad device unit: %s", unitstr);
+ unit = ul;
+ }
+
+ mtpoint = argv[1];
+ if (!have_mdtype)
+ mdtype = MD_SWAP;
+ if (softdep)
+ argappend(&newfs_arg, "-U");
+ if (mdtype != MD_VNODE && !newfs)
+ errx(1, "-P requires a vnode-backed disk");
+
+ /* Do the work. */
+ if (detach && !autounit)
+ do_mdconfig_detach();
+ if (autounit)
+ do_mdconfig_attach_au(mdconfig_arg, mdtype);
+ else
+ do_mdconfig_attach(mdconfig_arg, mdtype);
+ if (newfs)
+ do_newfs(newfs_arg);
+ do_mount(mount_arg, mtpoint);
+ do_mtptsetup(mtpoint, &mi);
+
+ return (0);
+}
+
+/*
+ * Append the expansion of 'fmt' to the buffer pointed to by '*dstp';
+ * reallocate as required.
+ */
+static void
+argappend(char **dstp, const char *fmt, ...)
+{
+ char *old, *new;
+ va_list ap;
+
+ old = *dstp;
+ assert(old != NULL);
+
+ va_start(ap, fmt);
+ if (vasprintf(&new, fmt,ap) == -1)
+ errx(1, "vasprintf");
+ va_end(ap);
+
+ *dstp = new;
+ if (asprintf(&new, "%s %s", old, new) == -1)
+ errx(1, "asprintf");
+ free(*dstp);
+ free(old);
+
+ *dstp = new;
+}
+
+/*
+ * If run-time debugging is enabled, print the expansion of 'fmt'.
+ * Otherwise, do nothing.
+ */
+static void
+debugprintf(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!debug)
+ return;
+ fprintf(stderr, "DEBUG: ");
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+}
+
+/*
+ * Attach a memory disk with a known unit.
+ */
+static void
+do_mdconfig_attach(const char *args, const enum md_types mdtype)
+{
+ int rv;
+ const char *ta; /* Type arg. */
+
+ switch (mdtype) {
+ case MD_SWAP:
+ ta = "-t swap";
+ break;
+ case MD_VNODE:
+ ta = "-t vnode";
+ break;
+ case MD_MALLOC:
+ ta = "-t malloc";
+ break;
+ default:
+ abort();
+ }
+ rv = run(NULL, "%s -a %s%s -u %s%d", _PATH_MDCONFIG, ta, args,
+ mdname, unit);
+ if (rv)
+ errx(1, "mdconfig (attach) exited with error code %d", rv);
+}
+
+/*
+ * Attach a memory disk with an unknown unit; use autounit.
+ */
+static void
+do_mdconfig_attach_au(const char *args, const enum md_types mdtype)
+{
+ const char *ta; /* Type arg. */
+ char *linep, *linebuf; /* Line pointer, line buffer. */
+ int fd; /* Standard output of mdconfig invocation. */
+ FILE *sfd;
+ int rv;
+ char *p;
+ size_t linelen;
+ unsigned long ul;
+
+ switch (mdtype) {
+ case MD_SWAP:
+ ta = "-t swap";
+ break;
+ case MD_VNODE:
+ ta = "-t vnode";
+ break;
+ case MD_MALLOC:
+ ta = "-t malloc";
+ break;
+ default:
+ abort();
+ }
+ rv = run(&fd, "%s -a %s%s", _PATH_MDCONFIG, ta, args);
+ if (rv)
+ errx(1, "mdconfig (attach) exited with error code %d", rv);
+
+ /* Receive the unit number. */
+ if (norun) { /* Since we didn't run, we can't read. Fake it. */
+ unit = 0;
+ return;
+ }
+ sfd = fdopen(fd, "r");
+ if (sfd == NULL)
+ err(1, "fdopen");
+ linep = fgetln(sfd, &linelen);
+ if (linep == NULL && linelen < mdnamelen + 1)
+ errx(1, "unexpected output from mdconfig (attach)");
+ /* If the output format changes, we want to know about it. */
+ assert(strncmp(linep, mdname, mdnamelen) == 0);
+ linebuf = malloc(linelen - mdnamelen + 1);
+ assert(linebuf != NULL);
+ /* Can't use strlcpy because linep is not NULL-terminated. */
+ strncpy(linebuf, linep + mdnamelen, linelen);
+ linebuf[linelen] = '\0';
+ ul = strtoul(linebuf, &p, 10);
+ if (ul == ULONG_MAX || *p != '\n')
+ errx(1, "unexpected output from mdconfig (attach)");
+ unit = ul;
+
+ fclose(sfd);
+ close(fd);
+}
+
+/*
+ * Detach a memory disk.
+ */
+static void
+do_mdconfig_detach(void)
+{
+ int rv;
+
+ rv = run(NULL, "%s -d -u %s%d", _PATH_MDCONFIG, mdname, unit);
+ if (rv && debug) /* This is allowed to fail. */
+ warnx("mdconfig (detach) exited with error code %d (ignored)",
+ rv);
+}
+
+/*
+ * Mount the configured memory disk.
+ */
+static void
+do_mount(const char *args, const char *mtpoint)
+{
+ int rv;
+
+ rv = run(NULL, "%s%s /dev/%s%d %s", _PATH_MOUNT, args,
+ mdname, unit, mtpoint);
+ if (rv)
+ errx(1, "mount exited with error code %d", rv);
+}
+
+/*
+ * Various configuration of the mountpoint. Mostly, enact 'mip'.
+ */
+static void
+do_mtptsetup(const char *mtpoint, struct mtpt_info *mip)
+{
+
+ if (mip->mi_have_mode) {
+ debugprintf("changing mode of %s to %o.", mtpoint,
+ mip->mi_mode);
+ if (!norun)
+ if (chmod(mtpoint, mip->mi_mode) == -1)
+ err(1, "chmod: %s", mtpoint);
+ }
+ /*
+ * We have to do these separately because the user may have
+ * only specified one of them.
+ */
+ if (mip->mi_have_uid) {
+ debugprintf("changing owner (user) or %s to %u.", mtpoint,
+ mip->mi_uid);
+ if (!norun)
+ if (chown(mtpoint, mip->mi_uid, -1) == -1)
+ err(1, "chown %s to %u (user)", mtpoint,
+ mip->mi_uid);
+ }
+ if (mip->mi_have_gid) {
+ debugprintf("changing owner (group) or %s to %u.", mtpoint,
+ mip->mi_gid);
+ if (!norun)
+ if (chown(mtpoint, -1, mip->mi_gid) == -1)
+ err(1, "chown %s to %u (group)", mtpoint,
+ mip->mi_gid);
+ }
+}
+
+/*
+ * Put a file system on the memory disk.
+ */
+static void
+do_newfs(const char *args)
+{
+ int rv;
+
+ rv = run(NULL, "%s%s /dev/%s%d", _PATH_NEWFS, args, mdname, unit);
+ if (rv)
+ errx(1, "newfs exited with error code %d", rv);
+}
+
+/*
+ * 'str' should be a user and group name similar to the last argument
+ * to chown(1); i.e., a user, followed by a colon, followed by a
+ * group. The user and group in 'str' may be either a [ug]id or a
+ * name. Upon return, the uid and gid fields in 'mip' will contain
+ * the uid and gid of the user and group name in 'str', respectively.
+ *
+ * In other words, this derives a user and group id from a string
+ * formatted like the last argument to chown(1).
+ *
+ * Notice: At this point we don't support only a username or only a
+ * group name. do_mtptsetup already does, so when this feature is
+ * desired, this is the only routine that needs to be changed.
+ */
+static void
+extract_ugid(const char *str, struct mtpt_info *mip)
+{
+ char *ug; /* Writable 'str'. */
+ char *user, *group; /* Result of extracton. */
+ struct passwd *pw;
+ struct group *gr;
+ char *p;
+ uid_t *uid;
+ gid_t *gid;
+
+ uid = &mip->mi_uid;
+ gid = &mip->mi_gid;
+ mip->mi_have_uid = mip->mi_have_gid = false;
+
+ /* Extract the user and group from 'str'. Format above. */
+ ug = strdup(str);
+ assert(ug != NULL);
+ group = ug;
+ user = strsep(&group, ":");
+ if (user == NULL || group == NULL || *user == '\0' || *group == '\0')
+ usage();
+
+ /* Derive uid. */
+ *uid = strtoul(user, &p, 10);
+ if (*uid == (uid_t)ULONG_MAX)
+ usage();
+ if (*p != '\0') {
+ pw = getpwnam(user);
+ if (pw == NULL)
+ errx(1, "invalid user: %s", user);
+ *uid = pw->pw_uid;
+ }
+ mip->mi_have_uid = true;
+
+ /* Derive gid. */
+ *gid = strtoul(group, &p, 10);
+ if (*gid == (gid_t)ULONG_MAX)
+ usage();
+ if (*p != '\0') {
+ gr = getgrnam(group);
+ if (gr == NULL)
+ errx(1, "invalid group: %s", group);
+ *gid = gr->gr_gid;
+ }
+ mip->mi_have_gid = true;
+
+ free(ug);
+}
+
+/*
+ * Run a process with command name and arguments pointed to by the
+ * formatted string 'cmdline'. Since system(3) is not used, the first
+ * space-delimited token of 'cmdline' must be the full pathname of the
+ * program to run. The return value is the return code of the process
+ * spawned. If 'ofd' is non-NULL, it is set to the standard output of
+ * the program spawned (i.e., you can read from ofd and get the output
+ * of the program).
+ */
+static int
+run(int *ofd, const char *cmdline, ...)
+{
+ char **argv, **argvp; /* Result of splitting 'cmd'. */
+ int argc;
+ char *cmd; /* Expansion of 'cmdline'. */
+ int pid, status; /* Child info. */
+ int pfd[2]; /* Pipe to the child. */
+ int nfd; /* Null (/dev/null) file descriptor. */
+ bool dup2dn; /* Dup /dev/null to stdout? */
+ va_list ap;
+ char *p;
+ int rv, i;
+
+ dup2dn = true;
+ va_start(ap, cmdline);
+ rv = vasprintf(&cmd, cmdline, ap);
+ if (rv == -1)
+ err(1, "vasprintf");
+ va_end(ap);
+
+ /* Split up 'cmd' into 'argv' for use with execve. */
+ for (argc = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++)
+ argc++; /* 'argc' generation loop. */
+ argv = (char **)malloc(sizeof(*argv) * (argc + 1));
+ assert(argv != NULL);
+ for (p = cmd, argvp = argv; (*argvp = strsep(&p, " ")) != NULL;)
+ if (**argv != '\0')
+ if (++argvp >= &argv[argc]) {
+ *argvp = NULL;
+ break;
+ }
+ assert(*argv);
+
+ /* Make sure the above loop works as expected. */
+ if (debug) {
+ /*
+ * We can't, but should, use debugprintf here. First,
+ * it appends a trailing newline to the output, and
+ * second it prepends "DEBUG: " to the output. The
+ * former is a problem for this would-be first call,
+ * and the latter for the would-be call inside the
+ * loop.
+ */
+ (void)fprintf(stderr, "DEBUG: running:");
+ /* Should be equivilent to 'cmd' (before strsep, of course). */
+ for (i = 0; argv[i] != NULL; i++)
+ (void)fprintf(stderr, " %s", argv[i]);
+ (void)fprintf(stderr, "\n");
+ }
+
+ /* Create a pipe if necessary and fork the helper program. */
+ if (ofd != NULL) {
+ if (pipe(&pfd[0]) == -1)
+ err(1, "pipe");
+ *ofd = pfd[0];
+ dup2dn = false;
+ }
+ pid = fork();
+ switch (pid) {
+ case 0:
+ /* XXX can we call err() in here? */
+ if (norun)
+ _exit(0);
+ if (ofd != NULL)
+ if (dup2(pfd[1], STDOUT_FILENO) < 0)
+ err(1, "dup2");
+ if (!loudsubs) {
+ nfd = open(_PATH_DEVNULL, O_RDWR);
+ if (nfd == -1)
+ err(1, "open: %s", _PATH_DEVNULL);
+ if (dup2(nfd, STDIN_FILENO) < 0)
+ err(1, "dup2");
+ if (dup2dn)
+ if (dup2(nfd, STDOUT_FILENO) < 0)
+ err(1, "dup2");
+ if (dup2(nfd, STDERR_FILENO) < 0)
+ err(1, "dup2");
+ }
+
+ (void)execv(argv[0], argv);
+ warn("exec: %s", argv[0]);
+ _exit(-1);
+ case -1:
+ err(1, "fork");
+ }
+
+ free(cmd);
+ free(argv);
+ while (waitpid(pid, &status, 0) != pid)
+ ;
+ return (WEXITSTATUS(status));
+}
+
+static void
+usage(void)
+{
+ const char *name;
+
+ if (compat)
+ name = getprogname();
+ else
+ name = "mdmfs";
+ if (!compat)
+ fprintf(stderr,
+"usage: %s [-DLlMNPSUX] [-a maxcontig] [-b block-size] [-c cylinders]\n"
+"\t[-d rotdelay] [-e maxbpg] [-F file] [-f frag-size] [-i bytes]\n"
+"\t[-m percent-free] [-n rotational-positions] [-O optimization]\n"
+"\t[-o mount-options] [-p permissions] [-s size] [-v version]\n"
+"\t[-w user:group] md-device mount-point\n", name);
+ fprintf(stderr,
+"usage: %s -C [-lNU] [-a maxcontig] [-b block-size] [-c cylinders]\n"
+"\t[-d rotdelay] [-e maxbpg] [-F file] [-f frag-size] [-i bytes]\n"
+"\t[-m percent-free] [-n rotational-positions] [-O optimization]\n"
+"\t[-o mount-options] [-s size] [-v version] md-device mount-point\n", name);
+ exit(1);
+}
diff --git a/sbin/mknod/Makefile b/sbin/mknod/Makefile
new file mode 100644
index 0000000..d28d3a5
--- /dev/null
+++ b/sbin/mknod/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= mknod
+WARNS?= 0
+MAN= mknod.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/mknod/mknod.8 b/sbin/mknod/mknod.8
new file mode 100644
index 0000000..54b5441
--- /dev/null
+++ b/sbin/mknod/mknod.8
@@ -0,0 +1,154 @@
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)mknod.8 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.Dd December 15, 2004
+.Dt MKNOD 8
+.Os
+.Sh NAME
+.Nm mknod
+.Nd build special file
+.Sh SYNOPSIS
+.Nm
+.Ar name
+.Op Cm b | c
+.Ar major minor
+.Op Ar owner : Ns Ar group
+.Sh DESCRIPTION
+.Bf -symbolic
+The
+.Nm
+utility is deprecated on modern
+.Fx
+systems.
+.Ef
+.Pp
+The
+.Nm
+utility creates device special files.
+To make nodes manually, the four required arguments are:
+.Pp
+.Bl -tag -width indent
+.It Ar name
+Device name, for example
+.Dq sd
+for a SCSI disk on an HP300 or a
+.Dq pty
+for pseudo-terminals.
+.It Cm b | c
+Type of device.
+If the
+device is a block type device such as a tape or disk drive which needs
+both cooked and raw special files,
+the type is
+.Cm b .
+All other devices are character type devices, such as terminal
+and pseudo devices, and are type
+.Cm c .
+.It Ar major
+The major device number is an integer number which tells the kernel
+which device driver entry point to use.
+To learn what
+major device number to use for a particular device, check
+.Pa /usr/src/sys/conf/majors .
+.It Ar minor
+The minor device number tells the kernel which subunit
+the node corresponds to on the device; for example,
+a subunit may be a file system partition
+or a tty line.
+.It Ar owner : Ns Ar group
+The
+.Ar owner
+.Ar group
+operand pair is optional, however, if one is specified, they both must be
+specified.
+The
+.Ar owner
+may be either a numeric user ID or a user name.
+If a user name is also a numeric user ID, the operand is used as a
+user name.
+The
+.Ar group
+may be either a numeric group ID or a group name.
+Similar to the user name,
+if a group name is also a numeric group ID, the operand is used as a
+group name.
+.El
+.Pp
+Major and minor device numbers can be given in any format acceptable to
+.Xr strtoul 3 ,
+so that a leading
+.Ql 0x
+indicates a hexadecimal number, and a leading
+.Ql 0
+will cause the number to be interpreted as octal.
+.Pp
+The
+.Nm
+utility can be used to recreate deleted device nodes under a
+.Xr devfs 5
+mount point by invoking it using dummy arguments.
+Example:
+.Pp
+.Dl "mknod cd0 c 0 0"
+.Pp
+where
+.Dq Li cd0
+is the name of the deleted device node.
+.Sh COMPATIBILITY
+The
+.Xr chown 8 Ns - Ns
+like functionality is specific to
+.Fx .
+.Pp
+As of
+.Fx 4.0 ,
+block devices were deprecated in favour of character
+devices.
+As of
+.Fx 5.0 ,
+device nodes are managed by the device file system
+.Xr devfs 5 ,
+making the
+.Nm
+utility superfluous.
+As of
+.Fx 6.0
+device nodes may be created in regular file systems but such
+nodes cannot be used to access devices.
+.Sh SEE ALSO
+.Xr mkfifo 1 ,
+.Xr mknod 2 ,
+.Xr devfs 5 ,
+.Xr chown 8
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.At v6 .
diff --git a/sbin/mknod/mknod.c b/sbin/mknod/mknod.c
new file mode 100644
index 0000000..4c0493c
--- /dev/null
+++ b/sbin/mknod/mknod.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kevin Fall.
+ *
+ * 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mknod.c 8.1 (Berkeley) 6/5/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <grp.h>
+#include <pwd.h>
+#include <string.h>
+
+static void
+usage()
+{
+
+ (void)fprintf(stderr,
+ "usage: mknod name [b | c] major minor [owner:group]\n");
+ exit(1);
+}
+
+static u_long
+id(name, type)
+ char *name, *type;
+{
+ u_long val;
+ char *ep;
+
+ /*
+ * XXX
+ * We know that uid_t's and gid_t's are unsigned longs.
+ */
+ errno = 0;
+ val = strtoul(name, &ep, 10);
+ if (errno)
+ err(1, "%s", name);
+ if (*ep != '\0')
+ errx(1, "%s: illegal %s name", name, type);
+ return (val);
+}
+
+static gid_t
+a_gid(s)
+ char *s;
+{
+ struct group *gr;
+
+ if (*s == '\0') /* Argument was "uid[:.]". */
+ errx(1, "group must be specified when the owner is");
+ return ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid;
+}
+
+static uid_t
+a_uid(s)
+ char *s;
+{
+ struct passwd *pw;
+
+ if (*s == '\0') /* Argument was "[:.]gid". */
+ errx(1, "owner must be specified when the group is");
+ return ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid;
+}
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int range_error;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ dev_t dev;
+ char *cp, *endp;
+ long mymajor, myminor;
+
+ if (argc != 5 && argc != 6)
+ usage();
+
+ mode = 0666;
+ if (argv[2][0] == 'c')
+ mode |= S_IFCHR;
+ else if (argv[2][0] == 'b')
+ mode |= S_IFBLK;
+ else
+ errx(1, "node must be type 'b' or 'c'");
+
+ errno = 0;
+ mymajor = (long)strtoul(argv[3], &endp, 0);
+ if (endp == argv[3] || *endp != '\0')
+ errx(1, "%s: non-numeric major number", argv[3]);
+ range_error = errno;
+ errno = 0;
+ myminor = (long)strtoul(argv[4], &endp, 0);
+ if (endp == argv[4] || *endp != '\0')
+ errx(1, "%s: non-numeric minor number", argv[4]);
+ range_error |= errno;
+ dev = makedev(mymajor, myminor);
+ if (range_error || major(dev) != (u_int) mymajor ||
+ minor(dev) != (u_int) myminor)
+ errx(1, "major or minor number too large");
+
+ uid = gid = -1;
+ if (6 == argc) {
+ /* have owner:group */
+ if ((cp = strchr(argv[5], ':')) != NULL) {
+ *cp++ = '\0';
+ gid = a_gid(cp);
+ } else
+ usage();
+ uid = a_uid(argv[5]);
+ }
+
+ if (mknod(argv[1], mode, dev) != 0)
+ err(1, "%s", argv[1]);
+ if (6 == argc)
+ if (chown(argv[1], uid, gid))
+ err(1, "setting ownership on %s", argv[1]);
+ exit(0);
+}
diff --git a/sbin/mksnap_ffs/Makefile b/sbin/mksnap_ffs/Makefile
new file mode 100644
index 0000000..3c08f56
--- /dev/null
+++ b/sbin/mksnap_ffs/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG= mksnap_ffs
+MAN= mksnap_ffs.8
+
+.if defined(NOSUID)
+BINMODE=550
+.else
+BINMODE=4550
+BINOWN= root
+.endif
+BINGRP= operator
+
+.include <bsd.prog.mk>
diff --git a/sbin/mksnap_ffs/mksnap_ffs.8 b/sbin/mksnap_ffs/mksnap_ffs.8
new file mode 100644
index 0000000..2465f55
--- /dev/null
+++ b/sbin/mksnap_ffs/mksnap_ffs.8
@@ -0,0 +1,75 @@
+.\"
+.\" Copyright (c) 2003 Networks Associates Technology, Inc.
+.\" All rights reserved.
+.\"
+.\" This software was developed for the FreeBSD Project by Marshall
+.\" Kirk McKusick and Network Associates Laboratories, the Security
+.\" Research Division of Network Associates, Inc. under DARPA/SPAWAR
+.\" contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+.\" research program.
+.\"
+.\" 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 names of the authors 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 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 19, 2003
+.Dt MKSNAP_FFS 8
+.Os
+.Sh NAME
+.Nm mksnap_ffs
+.Nd take a file system snapshot
+.Sh SYNOPSIS
+.Nm
+.Ar mountpoint
+.Ar snapshot_name
+.Sh DESCRIPTION
+The
+.Nm
+utility creates a snapshot named
+.Ar snapshot_name
+on the file system mounted at
+.Ar mountpoint .
+The
+.Ar snapshot_name
+argument must be contained within the file system mounted at
+.Ar mountpoint .
+.Pp
+The group ownership of the file is set to
+.Dq Li operator ;
+the owner of the file remains
+.Dq Li root .
+The mode of the snapshot is set to be readable by the owner
+or members of the
+.Dq Li operator
+group.
+.Sh SEE ALSO
+.Xr chmod 2 ,
+.Xr chown 8 ,
+.Xr mount 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.0 .
diff --git a/sbin/mksnap_ffs/mksnap_ffs.c b/sbin/mksnap_ffs/mksnap_ffs.c
new file mode 100644
index 0000000..a7ff957
--- /dev/null
+++ b/sbin/mksnap_ffs/mksnap_ffs.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2003 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and Network Associates Laboratories, the Security
+ * Research Division of Network Associates, Inc. under DARPA/SPAWAR
+ * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+ * research program.
+ *
+ * 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 names of the authors 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <ufs/ufs/ufsmount.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ char *dir, *cp, path[PATH_MAX];
+ struct statfs stfsbuf;
+ struct ufs_args args;
+ struct group *grp;
+ struct stat stbuf;
+ int fd;
+
+ if (argc != 3)
+ usage();
+
+ dir = argv[1];
+ memset(&args, 0, sizeof args);
+ args.fspec = argv[2];
+
+ /*
+ * Check that the user running this program has permission
+ * to create and remove a snapshot file from the directory
+ * in which they have requested to have it made. If the
+ * directory is sticky and not owned by the user, then they
+ * will not be able to remove the snapshot when they are
+ * done with it.
+ */
+ if (strlen(args.fspec) >= PATH_MAX)
+ errx(1, "pathname too long %s", args.fspec);
+ cp = strrchr(args.fspec, '/');
+ if (cp == NULL) {
+ strlcpy(path, ".", PATH_MAX);
+ } else if (cp == args.fspec) {
+ strlcpy(path, "/", PATH_MAX);
+ } else {
+ strlcpy(path, args.fspec, cp - args.fspec + 1);
+ }
+ if (statfs(path, &stfsbuf) < 0)
+ err(1, "%s", path);
+ if (stat(path, &stbuf) < 0)
+ err(1, "%s", path);
+ if (!S_ISDIR(stbuf.st_mode))
+ errx(1, "%s: Not a directory", path);
+ if (access(path, W_OK) < 0)
+ err(1, "Lack write permission in %s", path);
+ if ((stbuf.st_mode & S_ISTXT) && stbuf.st_uid != getuid())
+ errx(1, "Lack write permission in %s: Sticky bit set", path);
+
+ /*
+ * Having verified access to the directory in which the
+ * snapshot is to be built, proceed with creating it.
+ */
+ if ((grp = getgrnam("operator")) == NULL)
+ errx(1, "Cannot retrieve operator gid");
+ if (mount("ufs", dir, MNT_UPDATE | MNT_SNAPSHOT | stfsbuf.f_flags,
+ &args) < 0)
+ err(1, "Cannot create %s", args.fspec);
+ if ((fd = open(args.fspec, O_RDONLY)) < 0)
+ err(1, "Cannot open %s", args.fspec);
+ if (fstat(fd, &stbuf) != 0)
+ err(1, "Cannot stat %s", args.fspec);
+ if ((stbuf.st_flags & SF_SNAPSHOT) == 0)
+ errx(1, "File %s is not a snapshot", args.fspec);
+ if (fchown(fd, -1, grp->gr_gid) != 0)
+ err(1, "Cannot chown %s", args.fspec);
+ if (fchmod(fd, S_IRUSR | S_IRGRP) != 0)
+ err(1, "Cannot chmod %s", args.fspec);
+
+ exit(EXIT_SUCCESS);
+}
+
+void
+usage()
+{
+
+ fprintf(stderr, "usage: mksnap_ffs mountpoint snapshot_name\n");
+ exit(EX_USAGE);
+}
diff --git a/sbin/mount/Makefile b/sbin/mount/Makefile
new file mode 100644
index 0000000..49005fb
--- /dev/null
+++ b/sbin/mount/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.6 (Berkeley) 5/8/95
+# $FreeBSD$
+
+PROG= mount
+SRCS= mount.c mount_fs.c getmntopts.c vfslist.c
+WARNS?= 6
+MAN= mount.8
+# We do NOT install the getmntopts.3 man page.
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount/extern.h b/sbin/mount/extern.h
new file mode 100644
index 0000000..91e2ec4
--- /dev/null
+++ b/sbin/mount/extern.h
@@ -0,0 +1,33 @@
+/*-
+ * Copyright (c) 1997 FreeBSD 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.
+ *
+ * $FreeBSD$
+ */
+
+/* vfslist.c */
+int checkvfsname(const char *, const char **);
+const char **makevfslist(char *);
+
+int mount_fs(const char *, int, char *[]);
diff --git a/sbin/mount/getmntopts.3 b/sbin/mount/getmntopts.3
new file mode 100644
index 0000000..b9022d9
--- /dev/null
+++ b/sbin/mount/getmntopts.3
@@ -0,0 +1,180 @@
+.\" Copyright (c) 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.
+.\" 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 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.
+.\"
+.\" @(#)getmntopts.3 8.3 (Berkeley) 3/30/95
+.\" $FreeBSD$
+.\"
+.Dd March 30, 1995
+.Dt GETMNTOPTS 3
+.Os
+.Sh NAME
+.Nm getmntopts
+.Nd scan mount options
+.Sh SYNOPSIS
+.Fd #include \&"mntopts.h"
+.Ft void
+.Fo getmntopts
+.Fa "const char *options" "const struct mntopt *mopts"
+.Fa "int *flagp" "int *altflagp"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn getmntopts
+function takes a comma separated option list and a list
+of valid option names, and computes the bitmask
+corresponding to the requested set of options.
+.Pp
+The string
+.Fa options
+is broken down into a sequence of comma separated tokens.
+Each token is looked up in the table described by
+.Fa mopts
+and the bits in
+the word referenced by either
+.Fa flagp
+or
+.Fa altflagp
+(depending on the
+.Va m_altloc
+field of the option's table entry)
+are updated.
+The flag words are not initialized by
+.Fn getmntopts .
+The table,
+.Fa mopts ,
+has the following format:
+.Bd -literal
+struct mntopt {
+ char *m_option; /* option name */
+ int m_inverse; /* is this a negative option, e.g. "dev" */
+ int m_flag; /* bit to set, e.g. MNT_RDONLY */
+ int m_altloc; /* non-zero to use altflagp rather than flagp */
+};
+.Ed
+.Pp
+The members of this structure are:
+.Bl -tag -width m_inverse
+.It Va m_option
+the option name,
+for example
+.Dq Li suid .
+.It Va m_inverse
+tells
+.Fn getmntopts
+that the name has the inverse meaning of the
+bit.
+For example,
+.Dq Li suid
+is the string, whereas the
+mount flag is
+.Dv MNT_NOSUID .
+In this case, the sense of the string and the flag
+are inverted, so the
+.Va m_inverse
+flag should be set.
+.It Va m_flag
+the value of the bit to be set or cleared in
+the flag word when the option is recognized.
+The bit is set when the option is discovered,
+but cleared if the option name was preceded
+by the letters
+.Dq Li no .
+The
+.Va m_inverse
+flag causes these two operations to be reversed.
+.It Va m_altloc
+the bit should be set or cleared in
+.Fa altflagp
+rather than
+.Fa flagp .
+.El
+.Pp
+Each of the user visible
+.Dv MNT_
+flags has a corresponding
+.Dv MOPT_
+macro which defines an appropriate
+.Vt "struct mntopt"
+entry.
+To simplify the program interface and ensure consistency across all
+programs, a general purpose macro,
+.Dv MOPT_STDOPTS ,
+is defined which
+contains an entry for all the generic VFS options.
+In addition, the macros
+.Dv MOPT_FORCE
+and
+.Dv MOPT_UPDATE
+exist to enable the
+.Dv MNT_FORCE
+and
+.Dv MNT_UPDATE
+flags to be set.
+Finally, the table must be terminated by an entry with a
+.Dv NULL
+first element.
+.Sh EXAMPLES
+Most commands will use the standard option set.
+Local file systems which support the
+.Dv MNT_UPDATE
+flag, would also have an
+.Dv MOPT_UPDATE
+entry.
+This can be declared and used as follows:
+.Bd -literal
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_UPDATE,
+ { NULL }
+};
+
+ ...
+ mntflags = mntaltflags = 0;
+ ...
+ getmntopts(options, mopts, &mntflags, &mntaltflags);
+ ...
+.Ed
+.Sh DIAGNOSTICS
+If the external integer variable
+.Va getmnt_silent
+is non-zero then the
+.Fn getmntopts
+function displays an error message and exits if an
+unrecognized option is encountered.
+By default
+.Va getmnt_silent
+is zero.
+.Sh SEE ALSO
+.Xr err 3 ,
+.Xr mount 8
+.Sh HISTORY
+The
+.Fn getmntopts
+function appeared in
+.Bx 4.4 .
diff --git a/sbin/mount/getmntopts.c b/sbin/mount/getmntopts.c
new file mode 100644
index 0000000..0c0e0fe
--- /dev/null
+++ b/sbin/mount/getmntopts.c
@@ -0,0 +1,182 @@
+/*-
+ * Copyright (c) 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)getmntopts.c 8.3 (Berkeley) 3/29/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include "mntopts.h"
+
+int getmnt_silent = 0;
+
+void
+getmntopts(const char *options, const struct mntopt *m0, int *flagp,
+ int *altflagp)
+{
+ const struct mntopt *m;
+ int negative, len;
+ char *opt, *optbuf, *p;
+ int *thisflagp;
+
+ /* Copy option string, since it is about to be torn asunder... */
+ if ((optbuf = strdup(options)) == NULL)
+ err(1, NULL);
+
+ for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
+ /* Check for "no" prefix. */
+ if (opt[0] == 'n' && opt[1] == 'o') {
+ negative = 1;
+ opt += 2;
+ } else
+ negative = 0;
+
+ /*
+ * for options with assignments in them (ie. quotas)
+ * ignore the assignment as it's handled elsewhere
+ */
+ p = strchr(opt, '=');
+ if (p != NULL)
+ *++p = '\0';
+
+ /* Scan option table. */
+ for (m = m0; m->m_option != NULL; ++m) {
+ len = strlen(m->m_option);
+ if (strncasecmp(opt, m->m_option, len) == 0)
+ if (opt[len] == '\0' || opt[len] == '=')
+ break;
+ }
+
+ /* Save flag, or fail if option is not recognized. */
+ if (m->m_option) {
+ thisflagp = m->m_altloc ? altflagp : flagp;
+ if (negative == m->m_inverse)
+ *thisflagp |= m->m_flag;
+ else
+ *thisflagp &= ~m->m_flag;
+ } else if (!getmnt_silent) {
+ errx(1, "-o %s: option not supported", opt);
+ }
+ }
+
+ free(optbuf);
+}
+
+void
+rmslashes(char *rrpin, char *rrpout)
+{
+ char *rrpoutstart;
+
+ *rrpout = *rrpin;
+ for (rrpoutstart = rrpout; *rrpin != '\0'; *rrpout++ = *rrpin++) {
+
+ /* skip all double slashes */
+ while (*rrpin == '/' && *(rrpin + 1) == '/')
+ rrpin++;
+ }
+
+ /* remove trailing slash if necessary */
+ if (rrpout - rrpoutstart > 1 && *(rrpout - 1) == '/')
+ *(rrpout - 1) = '\0';
+ else
+ *rrpout = '\0';
+}
+
+void
+checkpath(const char *path, char *resolved)
+{
+ struct stat sb;
+
+ if (realpath(path, resolved) != NULL && stat(resolved, &sb) == 0) {
+ if (!S_ISDIR(sb.st_mode))
+ errx(EX_USAGE, "%s: not a directory", resolved);
+ } else
+ errx(EX_USAGE, "%s: %s", resolved, strerror(errno));
+}
+
+void
+build_iovec(struct iovec **iov, int *iovlen, const char *name, void *val,
+ size_t len)
+{
+ int i;
+
+ if (*iovlen < 0)
+ return;
+ i = *iovlen;
+ *iov = realloc(*iov, sizeof **iov * (i + 2));
+ if (*iov == NULL) {
+ *iovlen = -1;
+ return;
+ }
+ (*iov)[i].iov_base = strdup(name);
+ (*iov)[i].iov_len = strlen(name) + 1;
+ i++;
+ (*iov)[i].iov_base = val;
+ if (len == (size_t)-1) {
+ if (val != NULL)
+ len = strlen(val) + 1;
+ else
+ len = 0;
+ }
+ (*iov)[i].iov_len = (int)len;
+ *iovlen = ++i;
+}
+
+/*
+ * This function is needed for compatibility with parameters
+ * which used to use the mount_argf() command for the old mount() syscall.
+ */
+void
+build_iovec_argf(struct iovec **iov, int *iovlen, const char *name,
+ const char *fmt, ...)
+{
+ va_list ap;
+ char val[255] = { 0 };
+
+ va_start(ap, fmt);
+ vsnprintf(val, sizeof(val), fmt, ap);
+ va_end(ap);
+ build_iovec(iov, iovlen, name, strdup(val), (size_t)-1);
+}
diff --git a/sbin/mount/mntopts.h b/sbin/mount/mntopts.h
new file mode 100644
index 0000000..2fcbbdd
--- /dev/null
+++ b/sbin/mount/mntopts.h
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (c) 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.
+ * 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 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.
+ *
+ * @(#)mntopts.h 8.7 (Berkeley) 3/29/95
+ * $FreeBSD$
+ */
+
+struct mntopt {
+ const char *m_option; /* option name */
+ int m_inverse; /* if a negative option, e.g. "atime" */
+ int m_flag; /* bit to set, e.g. MNT_RDONLY */
+ int m_altloc; /* 1 => set bit in altflags */
+};
+
+/* User-visible MNT_ flags. */
+#define MOPT_ASYNC { "async", 0, MNT_ASYNC, 0 }
+#define MOPT_NOATIME { "atime", 1, MNT_NOATIME, 0 }
+#define MOPT_NOEXEC { "exec", 1, MNT_NOEXEC, 0 }
+#define MOPT_NOSUID { "suid", 1, MNT_NOSUID, 0 }
+#define MOPT_NOSYMFOLLOW { "symfollow", 1, MNT_NOSYMFOLLOW, 0 }
+#define MOPT_RDONLY { "rdonly", 0, MNT_RDONLY, 0 }
+#define MOPT_SYNC { "sync", 0, MNT_SYNCHRONOUS, 0 }
+#define MOPT_UNION { "union", 0, MNT_UNION, 0 }
+#define MOPT_USERQUOTA { "userquota", 0, 0, 0 }
+#define MOPT_GROUPQUOTA { "groupquota", 0, 0, 0 }
+#define MOPT_NOCLUSTERR { "clusterr", 1, MNT_NOCLUSTERR, 0 }
+#define MOPT_NOCLUSTERW { "clusterw", 1, MNT_NOCLUSTERW, 0 }
+#define MOPT_SUIDDIR { "suiddir", 0, MNT_SUIDDIR, 0 }
+#define MOPT_SNAPSHOT { "snapshot", 0, MNT_SNAPSHOT, 0 }
+#define MOPT_MULTILABEL { "multilabel", 0, MNT_MULTILABEL, 0 }
+#define MOPT_ACLS { "acls", 0, MNT_ACLS, 0 }
+
+/* Control flags. */
+#define MOPT_FORCE { "force", 0, MNT_FORCE, 0 }
+#define MOPT_UPDATE { "update", 0, MNT_UPDATE, 0 }
+#define MOPT_RO { "ro", 0, MNT_RDONLY, 0 }
+#define MOPT_RW { "rw", 1, MNT_RDONLY, 0 }
+
+/* This is parsed by mount(8), but is ignored by specific mount_*(8)s. */
+#define MOPT_AUTO { "auto", 0, 0, 0 }
+
+/* A handy macro as terminator of MNT_ array. */
+#define MOPT_END { NULL, 0, 0, 0 }
+
+#define MOPT_FSTAB_COMPAT \
+ MOPT_RO, \
+ MOPT_RW, \
+ MOPT_AUTO
+
+/* Standard options which all mounts can understand. */
+#define MOPT_STDOPTS \
+ MOPT_USERQUOTA, \
+ MOPT_GROUPQUOTA, \
+ MOPT_FSTAB_COMPAT, \
+ MOPT_NOATIME, \
+ MOPT_NOEXEC, \
+ MOPT_SUIDDIR, /* must be before MOPT_NOSUID */ \
+ MOPT_NOSUID, \
+ MOPT_NOSYMFOLLOW, \
+ MOPT_RDONLY, \
+ MOPT_UNION, \
+ MOPT_NOCLUSTERR, \
+ MOPT_NOCLUSTERW, \
+ MOPT_MULTILABEL, \
+ MOPT_ACLS
+
+void getmntopts(const char *, const struct mntopt *, int *, int *);
+void rmslashes(char *, char *);
+void checkpath(const char *, char resolved_path[]);
+extern int getmnt_silent;
+void build_iovec(struct iovec **iov, int *iovlen, const char *name, void *val, size_t len);
+void build_iovec_argf(struct iovec **iov, int *iovlen, const char *name, const char *fmt, ...);
diff --git a/sbin/mount/mount.8 b/sbin/mount/mount.8
new file mode 100644
index 0000000..95e8c4d
--- /dev/null
+++ b/sbin/mount/mount.8
@@ -0,0 +1,491 @@
+.\" Copyright (c) 1980, 1989, 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.
+.\" 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 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.
+.\"
+.\" @(#)mount.8 8.8 (Berkeley) 6/16/94
+.\" $FreeBSD$
+.\"
+.Dd November 26, 2004
+.Dt MOUNT 8
+.Os
+.Sh NAME
+.Nm mount
+.Nd mount file systems
+.Sh SYNOPSIS
+.Nm
+.Op Fl adfpruvw
+.Op Fl F Ar fstab
+.Op Fl o Ar options
+.Op Fl t Ar ufs | external_type
+.Nm
+.Op Fl dfpruvw
+.Ar special | node
+.Nm
+.Op Fl dfpruvw
+.Op Fl o Ar options
+.Op Fl t Ar ufs | external_type
+.Ar special node
+.Sh DESCRIPTION
+The
+.Nm
+utility calls the
+.Xr mount 2
+system call to prepare and graft a
+.Ar "special device"
+or the remote node (rhost:path) on to the file system tree at the point
+.Ar node .
+If either
+.Ar special
+or
+.Ar node
+are not provided, the appropriate information is taken from the
+.Xr fstab 5
+file.
+.Pp
+The system maintains a list of currently mounted file systems.
+If no arguments are given to
+.Nm ,
+this list is printed.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a
+All the file systems described in
+.Xr fstab 5
+are mounted.
+Exceptions are those marked as
+.Dq noauto ,
+excluded by the
+.Fl t
+flag (see below), or if they are already mounted (except the
+root file system which is always remounted to preserve
+traditional single user mode behavior).
+.It Fl d
+Causes everything to be done except for the actual system call.
+This option is useful in conjunction with the
+.Fl v
+flag to
+determine what the
+.Nm
+command is trying to do.
+.It Fl F Ar fstab
+Specify the
+.Pa fstab
+file to use.
+.It Fl f
+Forces the revocation of write access when trying to downgrade
+a file system mount status from read-write to read-only.
+Also
+forces the R/W mount of an unclean file system (dangerous; use with
+caution).
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+In case of conflicting options being specified, the rightmost option
+takes effect.
+The following options are available:
+.Bl -tag -width indent
+.It Cm acls
+Enable Access Control Lists, or ACLS, which can be customized via the
+.Xr setfacl 1
+and
+.Xr getfacl 1
+commands.
+.It Cm async
+All
+.Tn I/O
+to the file system should be done asynchronously.
+This is a
+.Em dangerous
+flag to set,
+and should not be used unless you are prepared to recreate the file
+system should your system crash.
+.It Cm current
+When used with the
+.Fl u
+flag, this is the same as specifying the options currently in effect for
+the mounted file system.
+.It Cm force
+The same as
+.Fl f ;
+forces the revocation of write access when trying to downgrade
+a file system mount status from read-write to read-only.
+Also
+forces the R/W mount of an unclean file system (dangerous; use with caution).
+.It Cm fstab
+When used with the
+.Fl u
+flag, this is the same as specifying all the options listed in the
+.Xr fstab 5
+file for the file system.
+.It Cm multilabel
+Enable multi-label Mandatory Access Control, or MAC, on the specified file
+system.
+If the file system supports multilabel operation, individual labels will
+be maintained for each object in the file system, rather than using a
+single label for all objects.
+An alternative to the
+.Fl l
+flag in
+.Xr tunefs 8 .
+See
+.Xr mac 4
+for more information, which cause the multilabel mount flag to be set
+automatically at mount-time.
+.It Cm noasync
+Metadata I/O should be done synchronously, while data I/O should be done
+asynchronously.
+This is the default.
+.It Cm noatime
+Do not update the file access time when reading from a file.
+This option
+is useful on file systems where there are large numbers of files and
+performance is more critical than updating the file access time (which is
+rarely ever important).
+This option is currently only supported on local file systems.
+.It Cm noauto
+This file system should be skipped when
+.Nm
+is run with the
+.Fl a
+flag.
+.It Cm noclusterr
+Disable read clustering.
+.It Cm noclusterw
+Disable write clustering.
+.It Cm noexec
+Do not allow execution of any binaries on the mounted file system.
+This option is useful for a server that has file systems containing
+binaries for architectures other than its own.
+Note: This option was not designed as a security feature and no
+guarantee is made that it will prevent malicious code execution; for
+example, it is still possible to execute scripts which reside on a
+.Cm noexec
+mounted partition.
+.It Cm nosuid
+Do not allow set-user-identifier or set-group-identifier bits to take effect.
+Note: this option is worthless if a public available suid or sgid
+wrapper like
+.Xr suidperl 1
+is installed on your system.
+It is set automatically when the user does not have super-user privileges.
+.It Cm nosymfollow
+Do not follow symlinks
+on the mounted file system.
+.It Cm ro
+The same as
+.Fl r ;
+mount the file system read-only (even the super-user may not write it).
+.It Cm sync
+All
+.Tn I/O
+to the file system should be done synchronously.
+.It Cm snapshot
+This option allows a snapshot of the specified file system to be taken.
+The
+.Fl u
+flag is required with this option.
+Note that snapshot files must be created in the file system that is being
+snapshotted.
+You may create up to 20 snapshots per file system.
+Active snapshots are recorded in the superblock, so they persist across unmount
+and remount operations and across system reboots.
+When you are done with a snapshot, it can be removed with the
+.Xr rm 1
+command.
+Snapshots may be removed in any order, however you may not get back all the
+space contained in the snapshot as another snapshot may claim some of the blocks
+that it is releasing.
+Note that the schg flag is set on snapshots to ensure that not even the root
+user can write to them.
+The unlink command makes an exception for snapshot files in that it allows them
+to be removed even though they have the schg flag set, so it is not necessary to
+clear the schg flag before removing a snapshot file.
+.Pp
+Once you have taken a snapshot, there are three interesting things that you can
+do with it:
+.Pp
+.Bl -enum -compact
+.It
+Run
+.Xr fsck 8
+on the snapshot file.
+Assuming that the file system was clean when it was mounted, you should always
+get a clean (and unchanging) result from running fsck on the snapshot.
+This is essentially what the background fsck process does.
+.Pp
+.It
+Run
+.Xr dump 8
+on the snapshot.
+You will get a dump that is consistent with the file system as of the timestamp
+of the snapshot.
+.Pp
+.It
+Mount the snapshot as a frozen image of the file system.
+To mount the snapshot
+.Pa /var/snapshot/snap1 :
+.Bd -literal
+mdconfig -a -t vnode -f /var/snapshot/snap1 -u 4
+mount -r /dev/md4 /mnt
+.Ed
+.Pp
+You can now cruise around your frozen
+.Pa /var
+file system at
+.Pa /mnt .
+Everything will be in the same state that it was at the time the snapshot was
+taken.
+The one exception is that any earlier snapshots will appear as zero length
+files.
+When you are done with the mounted snapshot:
+.Bd -literal
+umount /mnt
+mdconfig -d -u 4
+.Ed
+.Pp
+Further details can be found in the file at
+.Pa /usr/src/sys/ufs/ffs/README.snapshot .
+.El
+.It Cm suiddir
+A directory on the mounted file system will respond to the SUID bit
+being set, by setting the owner of any new files to be the same
+as the owner of the directory.
+New directories will inherit the bit from their parents.
+Execute bits are removed from
+the file, and it will not be given to root.
+.Pp
+This feature is designed for use on fileservers serving PC users via
+ftp, SAMBA, or netatalk.
+It provides security holes for shell users and as
+such should not be used on shell machines, especially on home directories.
+This option requires the SUIDDIR
+option in the kernel to work.
+Only UFS file systems support this option.
+See
+.Xr chmod 2
+for more information.
+.It Cm update
+The same as
+.Fl u ;
+indicate that the status of an already mounted file system should be changed.
+.It Cm union
+Causes the namespace at the mount point to appear as the union
+of the mounted file system root and the existing directory.
+Lookups will be done in the mounted file system first.
+If those operations fail due to a non-existent file the underlying
+directory is then accessed.
+All creates are done in the mounted file system.
+.El
+.Pp
+Any additional options specific to a file system type that is not
+one of the internally known types (see the
+.Fl t
+option) may be passed as a comma separated list; these options are
+distinguished by a leading
+.Dq \&-
+(dash).
+Options that take a value are specified using the syntax -option=value.
+For example, the
+.Nm
+command:
+.Bd -literal -offset indent
+mount -t unionfs -o -b /sys $HOME/sys
+.Ed
+.Pp
+causes
+.Nm
+to execute the equivalent of:
+.Bd -literal -offset indent
+/sbin/mount_unionfs -b /sys $HOME/sys
+.Ed
+.Pp
+Additional options specific to file system types
+which are not internally known
+(see the description of the
+.Fl t
+option below)
+may be described in the manual pages for the associated
+.Pa /sbin/mount_ Ns Sy XXX
+utilities.
+.It Fl p
+Print mount information in
+.Xr fstab 5
+format.
+Implies also the
+.Fl v
+option.
+.It Fl r
+The file system is to be mounted read-only.
+Mount the file system read-only (even the super-user may not write it).
+The same as the
+.Cm ro
+argument to the
+.Fl o
+option.
+.It Fl t Ar ufs | external_type
+The argument following the
+.Fl t
+is used to indicate the file system type.
+The type
+.Ar ufs
+is the default.
+The
+.Fl t
+option can be used
+to indicate that the actions should only be taken on
+file systems of the specified type.
+More than one type may be specified in a comma separated list.
+The list of file system types can be prefixed with
+.Dq no
+to specify the file system types for which action should
+.Em not
+be taken.
+For example, the
+.Nm
+command:
+.Bd -literal -offset indent
+mount -a -t nonfs,nullfs
+.Ed
+.Pp
+mounts all file systems except those of type
+.Tn NFS
+and
+.Tn NULLFS .
+.Pp
+If the type is not one of the internally known types,
+.Nm
+will attempt to execute a program in
+.Pa /sbin/mount_ Ns Sy XXX
+where
+.Sy XXX
+is replaced by the type name.
+For example, nfs file systems are mounted by the program
+.Pa /sbin/mount_nfs .
+.Pp
+Most file systems will be dynamically loaded by the kernel
+if not already present, and if the kernel module is available.
+.It Fl u
+The
+.Fl u
+flag indicates that the status of an already mounted file
+system should be changed.
+Any of the options discussed above (the
+.Fl o
+option)
+may be changed;
+also a file system can be changed from read-only to read-write
+or vice versa.
+An attempt to change from read-write to read-only will fail if any
+files on the file system are currently open for writing unless the
+.Fl f
+flag is also specified.
+The set of options is determined by applying the options specified
+in the argument to
+.Fl o
+and finally applying the
+.Fl r
+or
+.Fl w
+option.
+.It Fl v
+Verbose mode.
+.It Fl w
+The file system object is to be read and write.
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width ".Ev PATH_FSTAB"
+.It Ev PATH_FSTAB
+If the environment variable
+.Ev PATH_FSTAB
+is set, all operations are performed against the specified file.
+.Ev PATH_FSTAB
+will not be honored if the process environment or memory address space is
+considered
+.Dq tainted .
+(See
+.Xr issetugid 2
+for more information.)
+.El
+.Sh FILES
+.Bl -tag -width /etc/fstab -compact
+.It Pa /etc/fstab
+file system table
+.El
+.Sh DIAGNOSTICS
+Various, most of them are self-explanatory.
+.Pp
+.Dl XXXXX file system is not available
+.Pp
+The kernel does not support the respective file system type.
+Note that
+support for a particular file system might be provided either on a static
+(kernel compile-time), or dynamic basis (loaded as a kernel module by
+.Xr kldload 8 ) .
+.Sh SEE ALSO
+.Xr getfacl 1 ,
+.Xr setfacl 1 ,
+.Xr mount 2 ,
+.Xr acl 3 ,
+.Xr mac 4 ,
+.Xr fstab 5 ,
+.Xr kldload 8 ,
+.Xr mount_cd9660 8 ,
+.Xr mount_devfs 8 ,
+.Xr mount_ext2fs 8 ,
+.Xr mount_fdescfs 8 ,
+.Xr mount_linprocfs 8 ,
+.Xr mount_msdosfs 8 ,
+.Xr mount_nfs 8 ,
+.Xr mount_ntfs 8 ,
+.Xr mount_nullfs 8 ,
+.Xr mount_nwfs 8 ,
+.Xr mount_portalfs 8 ,
+.Xr mount_procfs 8 ,
+.Xr mount_reiserfs 8 ,
+.Xr mount_smbfs 8 ,
+.Xr mount_std 8 ,
+.Xr mount_udf 8 ,
+.Xr mount_umapfs 8 ,
+.Xr mount_unionfs 8 ,
+.Xr umount 8
+.Sh CAVEATS
+After a successful
+.Nm ,
+the permissions on the original mount point determine if
+.Pa ..\&
+is accessible from the mounted file system.
+The minimum permissions for
+the mount point for traversal across the mount point in both
+directions to be possible for all users is 0111 (execute for all).
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.At v1 .
+.Sh BUGS
+It is possible for a corrupted file system to cause a crash.
diff --git a/sbin/mount/mount.c b/sbin/mount/mount.c
new file mode 100644
index 0000000..e1a36c0
--- /dev/null
+++ b/sbin/mount/mount.c
@@ -0,0 +1,802 @@
+/*-
+ * Copyright (c) 1980, 1989, 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mount.c 8.25 (Berkeley) 5/8/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fstab.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+#include "mntopts.h"
+#include "pathnames.h"
+
+/* `meta' options */
+#define MOUNT_META_OPTION_FSTAB "fstab"
+#define MOUNT_META_OPTION_CURRENT "current"
+
+int debug, fstab_style, verbose;
+
+char *catopt(char *, const char *);
+struct statfs *getmntpt(const char *);
+int hasopt(const char *, const char *);
+int ismounted(struct fstab *, struct statfs *, int);
+int isremountable(const char *);
+void mangle(char *, int *, char **);
+char *update_options(char *, char *, int);
+int mountfs(const char *, const char *, const char *,
+ int, const char *, const char *);
+void remopt(char *, const char *);
+void prmount(struct statfs *);
+void putfsent(const struct statfs *);
+void usage(void);
+char *flags2opts(int);
+
+/* Map from mount options to printable formats. */
+static struct opt {
+ int o_opt;
+ const char *o_name;
+} optnames[] = {
+ { MNT_ASYNC, "asynchronous" },
+ { MNT_EXPORTED, "NFS exported" },
+ { MNT_LOCAL, "local" },
+ { MNT_NOATIME, "noatime" },
+ { MNT_NOEXEC, "noexec" },
+ { MNT_NOSUID, "nosuid" },
+ { MNT_NOSYMFOLLOW, "nosymfollow" },
+ { MNT_QUOTA, "with quotas" },
+ { MNT_RDONLY, "read-only" },
+ { MNT_SYNCHRONOUS, "synchronous" },
+ { MNT_UNION, "union" },
+ { MNT_NOCLUSTERR, "noclusterr" },
+ { MNT_NOCLUSTERW, "noclusterw" },
+ { MNT_SUIDDIR, "suiddir" },
+ { MNT_SOFTDEP, "soft-updates" },
+ { MNT_MULTILABEL, "multilabel" },
+ { MNT_ACLS, "acls" },
+ { 0, NULL }
+};
+
+/*
+ * List of VFS types that can be remounted without becoming mounted on top
+ * of each other.
+ * XXX Is this list correct?
+ */
+static const char *
+remountable_fs_names[] = {
+ "ufs", "ffs", "ext2fs",
+ 0
+};
+
+static int
+use_mountprog(const char *vfstype)
+{
+ /* XXX: We need to get away from implementing external mount
+ * programs for every filesystem, and move towards having
+ * each filesystem properly implement the nmount() system call.
+ */
+ unsigned int i;
+ const char *fs[] = {
+ "cd9660", "mfs", "msdosfs", "nfs", "nfs4", "ntfs",
+ "nwfs", "nullfs", "portalfs", "smbfs", "udf", "umapfs",
+ "unionfs",
+ NULL
+ };
+
+ for (i=0; fs[i] != NULL; ++i) {
+ if (strcmp(vfstype, fs[i]) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+exec_mountprog(const char *name, const char *execname,
+ char *const argv[])
+{
+ pid_t pid;
+ int status;
+
+ switch (pid = fork()) {
+ case -1: /* Error. */
+ warn("fork");
+ exit (1);
+ case 0: /* Child. */
+ /* Go find an executable. */
+ execvP(execname, _PATH_SYSPATH, argv);
+ if (errno == ENOENT) {
+ warn("exec %s not found in %s", execname,
+ _PATH_SYSPATH);
+ }
+ exit(1);
+ default: /* Parent. */
+ if (waitpid(pid, &status, 0) < 0) {
+ warn("waitpid");
+ return (1);
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0)
+ return (WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ warnx("%s: %s", name, sys_siglist[WTERMSIG(status)]);
+ return (1);
+ }
+ break;
+ }
+
+ return (0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *mntfromname, **vfslist, *vfstype;
+ struct fstab *fs;
+ struct statfs *mntbuf;
+ FILE *mountdfp;
+ pid_t pid;
+ int all, ch, i, init_flags, mntsize, rval, have_fstab;
+ char *cp, *ep, *options;
+
+ all = init_flags = 0;
+ options = NULL;
+ vfslist = NULL;
+ vfstype = "ufs";
+ while ((ch = getopt(argc, argv, "adF:fo:prwt:uv")) != -1)
+ switch (ch) {
+ case 'a':
+ all = 1;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'F':
+ setfstab(optarg);
+ break;
+ case 'f':
+ init_flags |= MNT_FORCE;
+ break;
+ case 'o':
+ if (*optarg)
+ options = catopt(options, optarg);
+ break;
+ case 'p':
+ fstab_style = 1;
+ verbose = 1;
+ break;
+ case 'r':
+ options = catopt(options, "ro");
+ break;
+ case 't':
+ if (vfslist != NULL)
+ errx(1, "only one -t option may be specified");
+ vfslist = makevfslist(optarg);
+ vfstype = optarg;
+ break;
+ case 'u':
+ init_flags |= MNT_UPDATE;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'w':
+ options = catopt(options, "noro");
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ argc -= optind;
+ argv += optind;
+
+#define BADTYPE(type) \
+ (strcmp(type, FSTAB_RO) && \
+ strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
+
+ rval = 0;
+ switch (argc) {
+ case 0:
+ if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
+ err(1, "getmntinfo");
+ if (all) {
+ while ((fs = getfsent()) != NULL) {
+ if (BADTYPE(fs->fs_type))
+ continue;
+ if (checkvfsname(fs->fs_vfstype, vfslist))
+ continue;
+ if (hasopt(fs->fs_mntops, "noauto"))
+ continue;
+ if (!(init_flags & MNT_UPDATE) &&
+ ismounted(fs, mntbuf, mntsize))
+ continue;
+ options = update_options(options, fs->fs_mntops,
+ mntbuf->f_flags);
+ if (mountfs(fs->fs_vfstype, fs->fs_spec,
+ fs->fs_file, init_flags, options,
+ fs->fs_mntops))
+ rval = 1;
+ }
+ } else if (fstab_style) {
+ for (i = 0; i < mntsize; i++) {
+ if (checkvfsname(mntbuf[i].f_fstypename, vfslist))
+ continue;
+ putfsent(&mntbuf[i]);
+ }
+ } else {
+ for (i = 0; i < mntsize; i++) {
+ if (checkvfsname(mntbuf[i].f_fstypename,
+ vfslist))
+ continue;
+ prmount(&mntbuf[i]);
+ }
+ }
+ exit(rval);
+ case 1:
+ if (vfslist != NULL)
+ usage();
+
+ rmslashes(*argv, *argv);
+ if (init_flags & MNT_UPDATE) {
+ mntfromname = NULL;
+ have_fstab = 0;
+ if ((mntbuf = getmntpt(*argv)) == NULL)
+ errx(1, "not currently mounted %s", *argv);
+ /*
+ * Only get the mntflags from fstab if both mntpoint
+ * and mntspec are identical. Also handle the special
+ * case where just '/' is mounted and 'spec' is not
+ * identical with the one from fstab ('/dev' is missing
+ * in the spec-string at boot-time).
+ */
+ if ((fs = getfsfile(mntbuf->f_mntonname)) != NULL) {
+ if (strcmp(fs->fs_spec,
+ mntbuf->f_mntfromname) == 0 &&
+ strcmp(fs->fs_file,
+ mntbuf->f_mntonname) == 0) {
+ have_fstab = 1;
+ mntfromname = mntbuf->f_mntfromname;
+ } else if (argv[0][0] == '/' &&
+ argv[0][1] == '\0') {
+ fs = getfsfile("/");
+ have_fstab = 1;
+ mntfromname = fs->fs_spec;
+ }
+ }
+ if (have_fstab) {
+ options = update_options(options, fs->fs_mntops,
+ mntbuf->f_flags);
+ } else {
+ mntfromname = mntbuf->f_mntfromname;
+ options = update_options(options, NULL,
+ mntbuf->f_flags);
+ }
+ rval = mountfs(mntbuf->f_fstypename, mntfromname,
+ mntbuf->f_mntonname, init_flags, options, 0);
+ break;
+ }
+ if ((fs = getfsfile(*argv)) == NULL &&
+ (fs = getfsspec(*argv)) == NULL)
+ errx(1, "%s: unknown special file or file system",
+ *argv);
+ if (BADTYPE(fs->fs_type))
+ errx(1, "%s has unknown file system type",
+ *argv);
+ rval = mountfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file,
+ init_flags, options, fs->fs_mntops);
+ break;
+ case 2:
+ /*
+ * If -t flag has not been specified, the path cannot be
+ * found, spec contains either a ':' or a '@', then assume
+ * that an NFS file system is being specified ala Sun.
+ * Check if the hostname contains only allowed characters
+ * to reduce false positives. IPv6 addresses containing
+ * ':' will be correctly parsed only if the separator is '@'.
+ * The definition of a valid hostname is taken from RFC 1034.
+ */
+ if (vfslist == NULL && ((ep = strchr(argv[0], '@')) != NULL ||
+ (ep = strchr(argv[0], ':')) != NULL)) {
+ if (*ep == '@') {
+ cp = ep + 1;
+ ep = cp + strlen(cp);
+ } else
+ cp = argv[0];
+ while (cp != ep) {
+ if (!isdigit(*cp) && !isalpha(*cp) &&
+ *cp != '.' && *cp != '-' && *cp != ':')
+ break;
+ cp++;
+ }
+ if (cp == ep)
+ vfstype = "nfs";
+ }
+ rval = mountfs(vfstype,
+ argv[0], argv[1], init_flags, options, NULL);
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+
+ /*
+ * If the mount was successfully, and done by root, tell mountd the
+ * good news. Pid checks are probably unnecessary, but don't hurt.
+ */
+ if (rval == 0 && getuid() == 0 &&
+ (mountdfp = fopen(_PATH_MOUNTDPID, "r")) != NULL) {
+ if (fscanf(mountdfp, "%d", &pid) == 1 &&
+ pid > 0 && kill(pid, SIGHUP) == -1 && errno != ESRCH)
+ err(1, "signal mountd");
+ (void)fclose(mountdfp);
+ }
+
+ exit(rval);
+}
+
+int
+ismounted(struct fstab *fs, struct statfs *mntbuf, int mntsize)
+{
+ char realfsfile[PATH_MAX];
+ int i;
+
+ if (fs->fs_file[0] == '/' && fs->fs_file[1] == '\0')
+ /* the root file system can always be remounted */
+ return (0);
+
+ /* The user may have specified a symlink in fstab, resolve the path */
+ if (realpath(fs->fs_file, realfsfile) == NULL) {
+ /* Cannot resolve the path, use original one */
+ strlcpy(realfsfile, fs->fs_file, sizeof(realfsfile));
+ }
+
+ for (i = mntsize - 1; i >= 0; --i)
+ if (strcmp(realfsfile, mntbuf[i].f_mntonname) == 0 &&
+ (!isremountable(fs->fs_vfstype) ||
+ strcmp(fs->fs_spec, mntbuf[i].f_mntfromname) == 0))
+ return (1);
+ return (0);
+}
+
+int
+isremountable(const char *vfsname)
+{
+ const char **cp;
+
+ for (cp = remountable_fs_names; *cp; cp++)
+ if (strcmp(*cp, vfsname) == 0)
+ return (1);
+ return (0);
+}
+
+int
+hasopt(const char *mntopts, const char *option)
+{
+ int negative, found;
+ char *opt, *optbuf;
+
+ if (option[0] == 'n' && option[1] == 'o') {
+ negative = 1;
+ option += 2;
+ } else
+ negative = 0;
+ optbuf = strdup(mntopts);
+ found = 0;
+ for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
+ if (opt[0] == 'n' && opt[1] == 'o') {
+ if (!strcasecmp(opt + 2, option))
+ found = negative;
+ } else if (!strcasecmp(opt, option))
+ found = !negative;
+ }
+ free(optbuf);
+ return (found);
+}
+
+int
+mountfs(const char *vfstype, const char *spec, const char *name, int flags,
+ const char *options, const char *mntopts)
+{
+ char *argv[100];
+ struct statfs sf;
+ int argc, i, ret;
+ char *optbuf, execname[PATH_MAX], mntpath[PATH_MAX];
+
+ /* resolve the mountpoint with realpath(3) */
+ (void)checkpath(name, mntpath);
+ name = mntpath;
+
+ if (mntopts == NULL)
+ mntopts = "";
+ if (options == NULL) {
+ if (*mntopts == '\0') {
+ options = "rw";
+ } else {
+ options = mntopts;
+ mntopts = "";
+ }
+ }
+ optbuf = catopt(strdup(mntopts), options);
+
+ if (strcmp(name, "/") == 0)
+ flags |= MNT_UPDATE;
+ if (flags & MNT_FORCE)
+ optbuf = catopt(optbuf, "force");
+ if (flags & MNT_RDONLY)
+ optbuf = catopt(optbuf, "ro");
+ /*
+ * XXX
+ * The mount_mfs (newfs) command uses -o to select the
+ * optimization mode. We don't pass the default "-o rw"
+ * for that reason.
+ */
+ if (flags & MNT_UPDATE)
+ optbuf = catopt(optbuf, "update");
+
+ /* Compatibility glue. */
+ if (strcmp(vfstype, "msdos") == 0)
+ vfstype = "msdosfs";
+
+ /* Construct the name of the appropriate mount command */
+ (void)snprintf(execname, sizeof(execname), "mount_%s", vfstype);
+
+ argc = 0;
+ argv[argc++] = execname;
+ mangle(optbuf, &argc, argv);
+ argv[argc++] = strdup(spec);
+ argv[argc++] = strdup(name);
+ argv[argc] = NULL;
+
+ if (debug) {
+ (void)printf("exec: mount_%s", vfstype);
+ for (i = 1; i < argc; i++)
+ (void)printf(" %s", argv[i]);
+ (void)printf("\n");
+ return (0);
+ }
+
+ if (use_mountprog(vfstype)) {
+ ret = exec_mountprog(name, execname, argv);
+ } else {
+ ret = mount_fs(vfstype, argc, argv);
+ }
+
+ free(optbuf);
+
+ if (verbose) {
+ if (statfs(name, &sf) < 0) {
+ warn("statfs %s", name);
+ return (1);
+ }
+ if (fstab_style)
+ putfsent(&sf);
+ else
+ prmount(&sf);
+ }
+
+ return (0);
+}
+
+void
+prmount(struct statfs *sfp)
+{
+ int flags;
+ unsigned int i;
+ struct opt *o;
+ struct passwd *pw;
+
+ (void)printf("%s on %s (%s", sfp->f_mntfromname, sfp->f_mntonname,
+ sfp->f_fstypename);
+
+ flags = sfp->f_flags & MNT_VISFLAGMASK;
+ for (o = optnames; flags && o->o_opt; o++)
+ if (flags & o->o_opt) {
+ (void)printf(", %s", o->o_name);
+ flags &= ~o->o_opt;
+ }
+ /*
+ * Inform when file system is mounted by an unprivileged user
+ * or privileged non-root user.
+ */
+ if ((flags & MNT_USER) != 0 || sfp->f_owner != 0) {
+ (void)printf(", mounted by ");
+ if ((pw = getpwuid(sfp->f_owner)) != NULL)
+ (void)printf("%s", pw->pw_name);
+ else
+ (void)printf("%d", sfp->f_owner);
+ }
+ if (verbose) {
+ if (sfp->f_syncwrites != 0 || sfp->f_asyncwrites != 0)
+ (void)printf(", writes: sync %ju async %ju",
+ (uintmax_t)sfp->f_syncwrites,
+ (uintmax_t)sfp->f_asyncwrites);
+ if (sfp->f_syncreads != 0 || sfp->f_asyncreads != 0)
+ (void)printf(", reads: sync %ju async %ju",
+ (uintmax_t)sfp->f_syncreads,
+ (uintmax_t)sfp->f_asyncreads);
+ if (sfp->f_fsid.val[0] != 0 || sfp->f_fsid.val[1] != 0) {
+ printf(", fsid ");
+ for (i = 0; i < sizeof(sfp->f_fsid); i++)
+ printf("%02x", ((u_char *)&sfp->f_fsid)[i]);
+ }
+ }
+ (void)printf(")\n");
+}
+
+struct statfs *
+getmntpt(const char *name)
+{
+ struct statfs *mntbuf;
+ int i, mntsize;
+
+ mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
+ for (i = mntsize - 1; i >= 0; i--) {
+ if (strcmp(mntbuf[i].f_mntfromname, name) == 0 ||
+ strcmp(mntbuf[i].f_mntonname, name) == 0)
+ return (&mntbuf[i]);
+ }
+ return (NULL);
+}
+
+char *
+catopt(char *s0, const char *s1)
+{
+ size_t i;
+ char *cp;
+
+ if (s1 == NULL || *s1 == '\0')
+ return s0;
+
+ if (s0 && *s0) {
+ i = strlen(s0) + strlen(s1) + 1 + 1;
+ if ((cp = malloc(i)) == NULL)
+ errx(1, "malloc failed");
+ (void)snprintf(cp, i, "%s,%s", s0, s1);
+ } else
+ cp = strdup(s1);
+
+ if (s0)
+ free(s0);
+ return (cp);
+}
+
+void
+mangle(options, argcp, argv)
+ char *options;
+ int *argcp;
+ char **argv;
+{
+ char *p, *s;
+ int argc;
+
+ argc = *argcp;
+ for (s = options; (p = strsep(&s, ",")) != NULL;)
+ if (*p != '\0') {
+ if (strcmp(p, "noauto") == 0) {
+ /*
+ * Do not pass noauto option to nmount().
+ * or external mount program. noauto is
+ * only used to prevent mounting a filesystem
+ * when 'mount -a' is specified, and is
+ * not a real mount option.
+ */
+ continue;
+ } else if (strcmp(p, "userquota") == 0) {
+ continue;
+ } else if (strcmp(p, "groupquota") == 0) {
+ continue;
+ } else if (*p == '-') {
+ argv[argc++] = p;
+ p = strchr(p, '=');
+ if (p != NULL) {
+ *p = '\0';
+ argv[argc++] = p+1;
+ }
+ } else {
+ argv[argc++] = strdup("-o");
+ argv[argc++] = p;
+ }
+ }
+
+ *argcp = argc;
+}
+
+
+char *
+update_options(opts, fstab, curflags)
+ char *opts;
+ char *fstab;
+ int curflags;
+{
+ char *o, *p;
+ char *cur;
+ char *expopt, *newopt, *tmpopt;
+
+ if (opts == NULL)
+ return strdup("");
+
+ /* remove meta options from list */
+ remopt(fstab, MOUNT_META_OPTION_FSTAB);
+ remopt(fstab, MOUNT_META_OPTION_CURRENT);
+ cur = flags2opts(curflags);
+
+ /*
+ * Expand all meta-options passed to us first.
+ */
+ expopt = NULL;
+ for (p = opts; (o = strsep(&p, ",")) != NULL;) {
+ if (strcmp(MOUNT_META_OPTION_FSTAB, o) == 0)
+ expopt = catopt(expopt, fstab);
+ else if (strcmp(MOUNT_META_OPTION_CURRENT, o) == 0)
+ expopt = catopt(expopt, cur);
+ else
+ expopt = catopt(expopt, o);
+ }
+ free(cur);
+ free(opts);
+
+ /*
+ * Remove previous contradictory arguments. Given option "foo" we
+ * remove all the "nofoo" options. Given "nofoo" we remove "nonofoo"
+ * and "foo" - so we can deal with possible options like "notice".
+ */
+ newopt = NULL;
+ for (p = expopt; (o = strsep(&p, ",")) != NULL;) {
+ if ((tmpopt = malloc( strlen(o) + 2 + 1 )) == NULL)
+ errx(1, "malloc failed");
+
+ strcpy(tmpopt, "no");
+ strcat(tmpopt, o);
+ remopt(newopt, tmpopt);
+ free(tmpopt);
+
+ if (strncmp("no", o, 2) == 0)
+ remopt(newopt, o+2);
+
+ newopt = catopt(newopt, o);
+ }
+ free(expopt);
+
+ return newopt;
+}
+
+void
+remopt(string, opt)
+ char *string;
+ const char *opt;
+{
+ char *o, *p, *r;
+
+ if (string == NULL || *string == '\0' || opt == NULL || *opt == '\0')
+ return;
+
+ r = string;
+
+ for (p = string; (o = strsep(&p, ",")) != NULL;) {
+ if (strcmp(opt, o) != 0) {
+ if (*r == ',' && *o != '\0')
+ r++;
+ while ((*r++ = *o++) != '\0')
+ ;
+ *--r = ',';
+ }
+ }
+ *r = '\0';
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr, "%s\n%s\n%s\n",
+"usage: mount [-adfpruvw] [-F fstab] [-o options] [-t ufs | external_type]",
+" mount [-dfpruvw] special | node",
+" mount [-dfpruvw] [-o options] [-t ufs | external_type] special node");
+ exit(1);
+}
+
+void
+putfsent(ent)
+ const struct statfs *ent;
+{
+ struct fstab *fst;
+ char *opts;
+
+ opts = flags2opts(ent->f_flags);
+ printf("%s\t%s\t%s %s", ent->f_mntfromname, ent->f_mntonname,
+ ent->f_fstypename, opts);
+ free(opts);
+
+ if ((fst = getfsspec(ent->f_mntfromname)))
+ printf("\t%u %u\n", fst->fs_freq, fst->fs_passno);
+ else if ((fst = getfsfile(ent->f_mntonname)))
+ printf("\t%u %u\n", fst->fs_freq, fst->fs_passno);
+ else if (strcmp(ent->f_fstypename, "ufs") == 0) {
+ if (strcmp(ent->f_mntonname, "/") == 0)
+ printf("\t1 1\n");
+ else
+ printf("\t2 2\n");
+ } else
+ printf("\t0 0\n");
+}
+
+
+char *
+flags2opts(flags)
+ int flags;
+{
+ char *res;
+
+ res = NULL;
+
+ res = catopt(res, (flags & MNT_RDONLY) ? "ro" : "rw");
+
+ if (flags & MNT_SYNCHRONOUS) res = catopt(res, "sync");
+ if (flags & MNT_NOEXEC) res = catopt(res, "noexec");
+ if (flags & MNT_NOSUID) res = catopt(res, "nosuid");
+ if (flags & MNT_UNION) res = catopt(res, "union");
+ if (flags & MNT_ASYNC) res = catopt(res, "async");
+ if (flags & MNT_NOATIME) res = catopt(res, "noatime");
+ if (flags & MNT_NOCLUSTERR) res = catopt(res, "noclusterr");
+ if (flags & MNT_NOCLUSTERW) res = catopt(res, "noclusterw");
+ if (flags & MNT_NOSYMFOLLOW) res = catopt(res, "nosymfollow");
+ if (flags & MNT_SUIDDIR) res = catopt(res, "suiddir");
+ if (flags & MNT_MULTILABEL) res = catopt(res, "multilabel");
+ if (flags & MNT_ACLS) res = catopt(res, "acls");
+
+ return res;
+}
diff --git a/sbin/mount/mount_fs.c b/sbin/mount/mount_fs.c
new file mode 100644
index 0000000..d9b0b3c
--- /dev/null
+++ b/sbin/mount/mount_fs.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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 the University of
+ * California, Berkeley and its contributors.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mount_fs.c 8.6 (Berkeley) 4/26/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <getopt.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_END
+};
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr,
+ "usage: mount [-t fstype] [-o options] target_fs mount_point\n");
+ exit(1);
+}
+
+int
+mount_fs(const char *vfstype, int argc, char *argv[])
+{
+ struct iovec *iov;
+ int iovlen;
+ int mntflags = 0;
+ int ch;
+ char *dev, *dir, mntpath[MAXPATHLEN];
+ char fstype[32];
+ char *p, *val;
+ int ret;
+
+ strncpy(fstype, vfstype, sizeof(fstype));
+
+ getmnt_silent = 1;
+ iov = NULL;
+ iovlen = 0;
+
+ optind = optreset = 1; /* Reset for parse of new argv. */
+ while ((ch = getopt(argc, argv, "o:")) != -1) {
+ switch(ch) {
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ p = strchr(optarg, '=');
+ val = NULL;
+ if (p != NULL) {
+ *p = '\0';
+ val = p + 1;
+ }
+ build_iovec(&iov, &iovlen, optarg, val, (size_t)-1);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ if (argc != 2)
+ usage();
+
+ dev = argv[0];
+ dir = argv[1];
+
+ (void)checkpath(dir, mntpath);
+ (void)rmslashes(dev, dev);
+
+ build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1);
+ build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1);
+ build_iovec(&iov, &iovlen, "from", dev, (size_t)-1);
+
+ ret = nmount(iov, iovlen, mntflags);
+ if (ret < 0)
+ err(1, "%s", dev);
+
+ return (ret);
+}
diff --git a/sbin/mount/pathnames.h b/sbin/mount/pathnames.h
new file mode 100644
index 0000000..aaa0a2c
--- /dev/null
+++ b/sbin/mount/pathnames.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 1989, 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.
+ * 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 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.
+ *
+ * @(#)pathnames.h 8.2 (Berkeley) 3/27/94
+ * $FreeBSD$
+ */
+
+#define _PATH_MOUNTDPID "/var/run/mountd.pid"
diff --git a/sbin/mount/vfslist.c b/sbin/mount/vfslist.c
new file mode 100644
index 0000000..f95a76e
--- /dev/null
+++ b/sbin/mount/vfslist.c
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 1995
+ * 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.
+ * 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 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 lint
+#if 0
+static char sccsid[] = "@(#)vfslist.c 8.1 (Berkeley) 5/8/95";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "extern.h"
+
+static int skipvfs;
+
+int
+checkvfsname(vfsname, vfslist)
+ const char *vfsname;
+ const char **vfslist;
+{
+
+ if (vfslist == NULL)
+ return (0);
+ while (*vfslist != NULL) {
+ if (strcmp(vfsname, *vfslist) == 0)
+ return (skipvfs);
+ ++vfslist;
+ }
+ return (!skipvfs);
+}
+
+const char **
+makevfslist(fslist)
+ char *fslist;
+{
+ const char **av;
+ int i;
+ char *nextcp;
+
+ if (fslist == NULL)
+ return (NULL);
+ if (fslist[0] == 'n' && fslist[1] == 'o') {
+ fslist += 2;
+ skipvfs = 1;
+ }
+ for (i = 0, nextcp = fslist; *nextcp; nextcp++)
+ if (*nextcp == ',')
+ i++;
+ if ((av = malloc((size_t)(i + 2) * sizeof(char *))) == NULL) {
+ warnx("malloc failed");
+ return (NULL);
+ }
+ nextcp = fslist;
+ i = 0;
+ av[i++] = nextcp;
+ while ((nextcp = strchr(nextcp, ',')) != NULL) {
+ *nextcp++ = '\0';
+ av[i++] = nextcp;
+ }
+ av[i++] = NULL;
+ return (av);
+}
diff --git a/sbin/mount_autofs/Makefile b/sbin/mount_autofs/Makefile
new file mode 100644
index 0000000..e7d040d
--- /dev/null
+++ b/sbin/mount_autofs/Makefile
@@ -0,0 +1,8 @@
+# $Id: Makefile,v 1.6 2004/09/08 08:27:12 bright Exp $
+# $FreeBSD$
+
+PROG=mount_autofs
+MAN=mount_autofs.8
+BINDIR?=/sbin
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_autofs/mount_autofs.8 b/sbin/mount_autofs/mount_autofs.8
new file mode 100644
index 0000000..5f10ea7
--- /dev/null
+++ b/sbin/mount_autofs/mount_autofs.8
@@ -0,0 +1,71 @@
+.\" Copyright (c) 2004 Alfred Perlstein <alfred@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.
+.\"
+.\" $Id: mount_autofs.8,v 1.3 2004/09/08 08:12:21 bright Exp $
+.\" $FreeBSD$
+.Dd August 30, 2004
+.Dt MOUNT_AUTOFS 8
+.Os
+.Sh NAME
+.Nm mount_autofs
+.Nd mount an autofs file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl o Ar options
+.Ar dummy
+.Ar node
+.Sh DESCRIPTION
+The
+.Nm
+utility attaches an autofs file system
+device on to the file system tree at the point
+.Ar node .
+.Pp
+This command is normally executed by
+.Xr mount 8
+at boot time.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 6.0 .
+.Sh AUTHORS
+This manual page and the autofs file system suite were written by
+.An Alfred Perlstein .
diff --git a/sbin/mount_autofs/mount_autofs.c b/sbin/mount_autofs/mount_autofs.c
new file mode 100644
index 0000000..17fbb5b
--- /dev/null
+++ b/sbin/mount_autofs/mount_autofs.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2004 Alfred Perlstein <alfred@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.
+ *
+ * $Id: mount_autofs.c,v 1.5 2004/09/08 08:12:21 bright Exp $
+ * $FreeBSD$
+ */
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/uio.h>
+
+void usage(void);
+
+const char *progname;
+
+void
+usage(void) {
+
+ errx(1, "usage: %s node", progname);
+}
+int mymount(const char *type, const char *dir, int flags, void *data);
+
+#if __FreeBSD_version < 600000
+int
+mymount(const char *type, const char *dir, int flags, void *data)
+{
+
+ return (mount(type, dir, flags, data));
+}
+#else
+void ioset(struct iovec *iovp, const char *str);
+
+void
+ioset(struct iovec *iovp, const char *str)
+{
+
+ iovp->iov_base = __DECONST(char *, str);
+ iovp->iov_len = strlen(str) + 1;
+}
+
+int
+mymount(
+ const char *type,
+ const char *dir,
+ int flags __unused,
+ void *data __unused
+)
+{
+ struct iovec iov[4], *iovp;
+
+ iovp = &iov[0];
+ ioset(iovp++, "fstype");
+ ioset(iovp++, type);
+ ioset(iovp++, "fspath");
+ ioset(iovp++, dir);
+ return (nmount(iov, 4, 0));
+}
+#endif
+
+int
+main(int argc, char **argv)
+{
+ int error;
+ int ch;
+
+ progname = argv[0];
+
+ while ((ch = getopt(argc, argv, "o:")) != -1) {
+ /* just eat opts for now */
+ switch (ch) {
+ case '?':
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2) {
+ usage();
+ }
+
+ error = mymount("autofs", argv[1], 0, NULL);
+ if (error)
+ perror("mount");
+ return (error == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/sbin/mount_cd9660/Makefile b/sbin/mount_cd9660/Makefile
new file mode 100644
index 0000000..0602d9b
--- /dev/null
+++ b/sbin/mount_cd9660/Makefile
@@ -0,0 +1,20 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+# $FreeBSD$
+
+PROG= mount_cd9660
+SRCS= mount_cd9660.c getmntopts.c
+MAN= mount_cd9660.8
+DPADD= ${LIBKICONV}
+LDADD= -lkiconv
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${MOUNT}
+WARNS?= 6
+
+# Needs to be dynamically linked for optional dlopen() access to
+# userland libiconv
+NO_SHARED?= NO
+
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_cd9660/mount_cd9660.8 b/sbin/mount_cd9660/mount_cd9660.8
new file mode 100644
index 0000000..909af40
--- /dev/null
+++ b/sbin/mount_cd9660/mount_cd9660.8
@@ -0,0 +1,160 @@
+.\" Copyright (c) 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software donated to Berkeley by
+.\" Christopher G. Demetriou.
+.\"
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)mount_cd9660.8 8.3 (Berkeley) 3/27/94
+.\" $FreeBSD$
+.\"
+.Dd October 3, 2005
+.Dt MOUNT_CD9660 8
+.Os
+.Sh NAME
+.Nm mount_cd9660
+.Nd mount an ISO-9660 file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl begjrv
+.Op Fl C Ar charset
+.Op Fl o Ar options
+.Op Fl s Ar startsector
+.Ar special node
+.Sh DESCRIPTION
+The
+.Nm
+utility attaches the ISO-9660 file system residing on the device
+.Pa special
+to the global file system namespace at the location indicated by
+.Pa node .
+This command is normally executed by
+.Xr mount 8
+at boot time.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl b
+Relax checking for Supplementary Volume Descriptor Flags field
+which is set to a wrong value on some Joliet formatted disks.
+.It Fl e
+Enable the use of extended attributes.
+.It Fl g
+Do not strip version numbers on files.
+(By default, if there are files with different version numbers on the disk,
+only the last one will be listed.)
+In either case, files may be opened without explicitly stating a
+version number.
+.It Fl j
+Do not use any Joliet extensions included in the file system.
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+The following cd9660 specific options are available:
+.Pp
+.Bl -tag -width "nostrictjoliet" -compact
+.It Cm extatt
+Same as
+.Fl e .
+.It Cm gens
+Same as
+.Fl g .
+.It Cm nojoliet
+Same as
+.Fl j .
+.It Cm norrip
+Same as
+.Fl r .
+.It Cm nostrictjoliet
+Same as
+.Fl b .
+.El
+.It Fl r
+Do not use any Rockridge extensions included in the file system.
+.It Fl s Ar startsector
+Start the file system at
+.Ar startsector .
+Normally, if the underlying device is a CD-ROM drive,
+.Nm
+will try to figure out the last track from the CD-ROM containing
+data, and start the file system there.
+If the device is not a CD-ROM,
+or the table of contents cannot be examined, the file system will be
+started at sector 0.
+This option can be used to override the behaviour.
+Note that
+.Ar startsector
+is measured in CD-ROM blocks, with 2048 bytes each.
+This is the same
+as for example the
+.Cm info
+command of
+.Xr cdcontrol 1
+is printing.
+It is possible to mount an arbitrary session of a multi-session CD by specifying
+the correct
+.Ar startsector
+here.
+.It Fl C Ar charset
+Specify local
+.Ar charset
+to convert Unicode file names when using Joliet extensions.
+.It Fl v
+Be verbose about the starting sector decisions made.
+.El
+.Sh EXAMPLES
+The following command can be used to mount a Kodak Photo-CD:
+.Pp
+.Dl "mount_cd9660 -o rw -v -s 0 /dev/cd0 /cdrom"
+.Sh SEE ALSO
+.Xr cdcontrol 1 ,
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
+.Pp
+The Unicode conversion routine was added by
+.An Ryuichiro Imura Aq imura@ryu16.org
+in 2003.
+.Sh BUGS
+POSIX device node mapping is currently not supported.
+.Pp
+Version numbers are not stripped if Rockridge extensions are in use.
+In this case, accessing files that do not have Rockridge names without
+version numbers gets the one with the lowest version number and not
+the one with the highest.
+.Pp
+There is no ECMA support.
diff --git a/sbin/mount_cd9660/mount_cd9660.c b/sbin/mount_cd9660/mount_cd9660.c
new file mode 100644
index 0000000..9a9bd9d
--- /dev/null
+++ b/sbin/mount_cd9660/mount_cd9660.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * 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.
+ * 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 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.
+ *
+ * @(#)mount_cd9660.c 8.7 (Berkeley) 5/1/95
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+/*
+static char sccsid[] = "@(#)mount_cd9660.c 8.7 (Berkeley) 5/1/95";
+*/
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/cdio.h>
+#include <sys/file.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/module.h>
+#include <sys/iconv.h>
+#include <sys/linker.h>
+
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_UPDATE,
+ MOPT_END
+};
+
+static int get_ssector(const char *dev);
+static int set_charset(struct iovec **, int *iovlen, const char *);
+void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ struct iovec *iov;
+ int iovlen;
+ int ch, mntflags, opts;
+ char *dev, *dir, *p, *val, mntpath[MAXPATHLEN];
+ int verbose;
+ int ssector; /* starting sector, 0 for 1st session */
+ char fstype[] = "cd9660";
+
+ iov = NULL;
+ iovlen = 0;
+ mntflags = opts = verbose = 0;
+ ssector = -1;
+
+ while ((ch = getopt(argc, argv, "begjo:rs:vC:")) != -1)
+ switch (ch) {
+ case 'b':
+ build_iovec(&iov, &iovlen, "brokenjoliet", NULL, (size_t)-1);
+ break;
+ case 'e':
+ build_iovec(&iov, &iovlen, "extatt", NULL, (size_t)-1);
+ break;
+ case 'g':
+ build_iovec(&iov, &iovlen, "gens", NULL, (size_t)-1);
+ break;
+ case 'j':
+ build_iovec(&iov, &iovlen, "nojoliet", NULL, (size_t)-1);
+ break;
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, &opts);
+ p = strchr(optarg, '=');
+ val = NULL;
+ if (p != NULL) {
+ *p = '\0';
+ val = p + 1;
+ }
+ build_iovec(&iov, &iovlen, optarg, val, (size_t)-1);
+ break;
+ case 'r':
+ build_iovec(&iov, &iovlen, "norrip", NULL, (size_t)-1);
+ break;
+ case 's':
+ ssector = atoi(optarg);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'C':
+ if (set_charset(&iov, &iovlen, optarg) == -1)
+ err(EX_OSERR, "cd9660_iconv");
+ build_iovec(&iov, &iovlen, "kiconv", NULL, (size_t)-1);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ dev = argv[0];
+ dir = argv[1];
+
+ /*
+ * Resolve the mountpoint with realpath(3) and remove unnecessary
+ * slashes from the devicename if there are any.
+ */
+ (void)checkpath(dir, mntpath);
+ (void)rmslashes(dev, dev);
+
+ if (ssector == -1) {
+ /*
+ * The start of the session has not been specified on
+ * the command line. If we can successfully read the
+ * TOC of a CD-ROM, use the last data track we find.
+ * Otherwise, just use 0, in order to mount the very
+ * first session. This is compatible with the
+ * historic behaviour of mount_cd9660(8). If the user
+ * has specified -s <ssector> above, we don't get here
+ * and leave the user's will.
+ */
+ if ((ssector = get_ssector(dev)) == -1) {
+ if (verbose)
+ printf("could not determine starting sector, "
+ "using very first session\n");
+ ssector = 0;
+ } else if (verbose)
+ printf("using starting sector %d\n", ssector);
+ }
+ mntflags |= MNT_RDONLY;
+ build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1);
+ build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1);
+ build_iovec(&iov, &iovlen, "from", dev, (size_t)-1);
+ build_iovec_argf(&iov, &iovlen, "ssector", "%d", ssector);
+
+ if (nmount(iov, iovlen, mntflags) < 0)
+ err(1, "%s", dev);
+ exit(0);
+}
+
+void
+usage(void)
+{
+ (void)fprintf(stderr,
+"usage: mount_cd9660 [-begjrv] [-C charset] [-o options] [-s startsector]\n"
+" special node\n");
+ exit(EX_USAGE);
+}
+
+static int
+get_ssector(const char *dev)
+{
+ struct ioc_toc_header h;
+ struct ioc_read_toc_entry t;
+ struct cd_toc_entry toc_buffer[100];
+ int fd, ntocentries, i;
+
+ if ((fd = open(dev, O_RDONLY)) == -1)
+ return -1;
+ if (ioctl(fd, CDIOREADTOCHEADER, &h) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ ntocentries = h.ending_track - h.starting_track + 1;
+ if (ntocentries > 100) {
+ /* unreasonable, only 100 allowed */
+ close(fd);
+ return -1;
+ }
+ t.address_format = CD_LBA_FORMAT;
+ t.starting_track = 0;
+ t.data_len = ntocentries * sizeof(struct cd_toc_entry);
+ t.data = toc_buffer;
+
+ if (ioctl(fd, CDIOREADTOCENTRYS, (char *) &t) == -1) {
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ for (i = ntocentries - 1; i >= 0; i--)
+ if ((toc_buffer[i].control & 4) != 0)
+ /* found a data track */
+ break;
+ if (i < 0)
+ return -1;
+
+ return ntohl(toc_buffer[i].addr.lba);
+}
+
+static int
+set_charset(struct iovec **iov, int *iovlen, const char *localcs)
+{
+ int error;
+ char *cs_disk; /* disk charset for Joliet cs conversion */
+ char *cs_local; /* local charset for Joliet cs conversion */
+
+ cs_disk = NULL;
+ cs_local = NULL;
+
+ if (modfind("cd9660_iconv") < 0)
+ if (kldload("cd9660_iconv") < 0 || modfind("cd9660_iconv") < 0) {
+ warnx( "cannot find or load \"cd9660_iconv\" kernel module");
+ return (-1);
+ }
+
+ if ((cs_disk = malloc(ICONV_CSNMAXLEN)) == NULL)
+ return (-1);
+ if ((cs_local = malloc(ICONV_CSNMAXLEN)) == NULL)
+ return (-1);
+ strncpy(cs_disk, ENCODING_UNICODE, ICONV_CSNMAXLEN);
+ strncpy(cs_local, kiconv_quirkcs(localcs, KICONV_VENDOR_MICSFT),
+ ICONV_CSNMAXLEN);
+ error = kiconv_add_xlat16_cspairs(cs_disk, cs_local);
+ if (error)
+ return (-1);
+
+ build_iovec(iov, iovlen, "cs_disk", cs_disk, (size_t)-1);
+ build_iovec(iov, iovlen, "cs_local", cs_local, (size_t)-1);
+
+ return (0);
+}
diff --git a/sbin/mount_ext2fs/Makefile b/sbin/mount_ext2fs/Makefile
new file mode 100644
index 0000000..3eefb92
--- /dev/null
+++ b/sbin/mount_ext2fs/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+# $FreeBSD$
+
+PROG= mount_ext2fs
+SRCS= mount_ext2fs.c getmntopts.c
+MAN= mount_ext2fs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${MOUNT}
+
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_ext2fs/mount_ext2fs.8 b/sbin/mount_ext2fs/mount_ext2fs.8
new file mode 100644
index 0000000..91a315e
--- /dev/null
+++ b/sbin/mount_ext2fs/mount_ext2fs.8
@@ -0,0 +1,72 @@
+.\" Copyright (c) 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.
+.\" 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 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 31, 1996
+.Dt MOUNT_EXT2FS 8
+.Os
+.Sh NAME
+.Nm mount_ext2fs
+.Nd mount an ext2fs file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl o Ar options
+.Ar special
+.Ar node
+.Sh DESCRIPTION
+The
+.Nm
+utility attaches an ext2fs file system
+.Ar special
+device on to the file system tree at the point
+.Ar node .
+.Pp
+This command is normally executed by
+.Xr mount 8
+at boot time.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 2.2 .
diff --git a/sbin/mount_ext2fs/mount_ext2fs.c b/sbin/mount_ext2fs/mount_ext2fs.c
new file mode 100644
index 0000000..e396823
--- /dev/null
+++ b/sbin/mount_ext2fs/mount_ext2fs.c
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+/*
+static char sccsid[] = "@(#)mount_lfs.c 8.3 (Berkeley) 3/27/94";
+*/
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/uio.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_ASYNC,
+ MOPT_FORCE,
+ MOPT_SYNC,
+ MOPT_UPDATE,
+ MOPT_END
+};
+
+static void usage(void) __dead2;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct iovec iov[6];
+ int ch, mntflags;
+ char *fs_name, *fspec, mntpath[MAXPATHLEN];
+
+ mntflags = 0;
+ while ((ch = getopt(argc, argv, "o:")) != -1)
+ switch (ch) {
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ fspec = argv[0]; /* the name of the device file */
+ fs_name = argv[1]; /* the mount point */
+
+ /*
+ * Resolve the mountpoint with realpath(3) and remove unnecessary
+ * slashes from the devicename if there are any.
+ */
+ (void)checkpath(fs_name, mntpath);
+ (void)rmslashes(fspec, fspec);
+
+ iov[0].iov_base = "fstype";
+ iov[0].iov_len = sizeof("fstype");
+ iov[1].iov_base = "ext2fs";
+ iov[1].iov_len = strlen(iov[1].iov_base) + 1;
+ iov[2].iov_base = "fspath";
+ iov[2].iov_len = sizeof("fspath");
+ iov[3].iov_base = mntpath;
+ iov[3].iov_len = strlen(mntpath) + 1;
+ iov[4].iov_base = "from";
+ iov[4].iov_len = sizeof("from");
+ iov[5].iov_base = fspec;
+ iov[5].iov_len = strlen(fspec) + 1;
+ if (nmount(iov, 6, mntflags) < 0)
+ err(EX_OSERR, "%s", fspec);
+ exit(0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_ext2fs [-o options] special node\n");
+ exit(EX_USAGE);
+}
diff --git a/sbin/mount_hpfs/Makefile b/sbin/mount_hpfs/Makefile
new file mode 100644
index 0000000..0843774
--- /dev/null
+++ b/sbin/mount_hpfs/Makefile
@@ -0,0 +1,15 @@
+#
+# $FreeBSD$
+#
+
+PROG= mount_hpfs
+SRCS= mount_hpfs.c getmntopts.c
+MAN= mount_hpfs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+=-I${MOUNT} -DHPFS
+WARNS?= 0
+
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_hpfs/mount_hpfs.8 b/sbin/mount_hpfs/mount_hpfs.8
new file mode 100644
index 0000000..0cdcb20
--- /dev/null
+++ b/sbin/mount_hpfs/mount_hpfs.8
@@ -0,0 +1,100 @@
+.\"
+.\" Copyright (c) 1993,1994 Christopher G. Demetriou
+.\" Copyright (c) 1999 Semen Ustimenko (semenu@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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed by Christopher G. Demetriou.
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 20, 1999
+.Dt MOUNT_HPFS 8
+.Os
+.Sh NAME
+.Nm mount_hpfs
+.Nd mount an HPFS file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl o Ar options
+.Op Fl u Ar uid
+.Op Fl g Ar gid
+.Op Fl m Ar mask
+.Pa special
+.Pa node
+.Sh DESCRIPTION
+The
+.Nm
+utility attaches the HPFS file system residing on the device
+.Pa special
+to the global file system namespace at the location
+indicated by
+.Pa node .
+This command is normally executed by
+.Xr mount 8
+at boot time, but can be used by any user to mount an
+HPFS file system on any directory that they own (provided,
+of course, that they have appropriate access to the device that
+contains the file system).
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl u Ar uid
+Set the owner of the files in the file system to
+.Ar uid .
+The default owner is the owner of the directory
+on which the file system is being mounted.
+.It Fl g Ar gid
+Set the group of the files in the file system to
+.Ar gid .
+The default group is the group of the directory
+on which the file system is being mounted.
+.It Fl m Ar mask
+Specify the maximum file permissions for files
+in the file system.
+.El
+.Sh EXAMPLES
+To mount an hpfs volume located in /dev/wd1s1:
+.Bd -literal -offset indent
+# mount_hpfs /dev/wd1s1 /mnt
+.Ed
+.Sh WRITING
+There is limited writing ability and it is not well-tested.
+It is strongly recommended to mount readonly!
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+HPFS kernel implementation,
+.Nm
+and manual were written by
+.An Semen Ustimenko Aq semenu@FreeBSD.org .
diff --git a/sbin/mount_hpfs/mount_hpfs.c b/sbin/mount_hpfs/mount_hpfs.c
new file mode 100644
index 0000000..a9a87e2
--- /dev/null
+++ b/sbin/mount_hpfs/mount_hpfs.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou
+ * Copyright (c) 1999 Semen Ustimenko (semenu@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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fs/hpfs/hpfsmount.h>
+#include <ctype.h>
+#include <err.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_END
+};
+
+static gid_t a_gid(char *);
+static uid_t a_uid(char *);
+static mode_t a_mask(char *);
+static void usage(void) __dead2;
+static void load_u2wtable(struct hpfs_args *, char *);
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct hpfs_args args;
+ struct stat sb;
+ int c, mntflags, set_gid, set_uid, set_mask;
+ int forcerw = 0;
+ char *dev, *dir, ndir[MAXPATHLEN];
+
+ mntflags = set_gid = set_uid = set_mask = 0;
+ (void)memset(&args, '\0', sizeof(args));
+
+ while ((c = getopt(argc, argv, "u:g:m:o:c:W:F")) != -1) {
+ switch (c) {
+ case 'F':
+ forcerw=1;
+ break;
+ case 'u':
+ args.uid = a_uid(optarg);
+ set_uid = 1;
+ break;
+ case 'g':
+ args.gid = a_gid(optarg);
+ set_gid = 1;
+ break;
+ case 'm':
+ args.mode = a_mask(optarg);
+ set_mask = 1;
+ break;
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case 'W':
+ load_u2wtable(&args, optarg);
+ args.flags |= HPFSMNT_TABLES;
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (optind + 2 != argc)
+ usage();
+
+ if (!(mntflags & MNT_RDONLY) && !forcerw) {
+ warnx("Write support is BETA, you need -F flag to enable RW mount!");
+ exit (111);
+ }
+
+ dev = argv[optind];
+ dir = argv[optind + 1];
+ if (dir[0] != '/') {
+ warnx("\"%s\" is a relative path", dir);
+ if (getcwd(ndir, sizeof(ndir)) == NULL)
+ err(EX_OSERR, "getcwd");
+ strncat(ndir, "/", sizeof(ndir) - strlen(ndir) - 1);
+ strncat(ndir, dir, sizeof(ndir) - strlen(ndir) - 1);
+ dir = ndir;
+ warnx("using \"%s\" instead", dir);
+ }
+
+ args.fspec = dev;
+ args.export.ex_root = 65534; /* unchecked anyway on DOS fs */
+ if (mntflags & MNT_RDONLY)
+ args.export.ex_flags = MNT_EXRDONLY;
+ else
+ args.export.ex_flags = 0;
+
+ if (!set_gid || !set_uid || !set_mask) {
+ if (stat(dir, &sb) == -1)
+ err(EX_OSERR, "stat %s", dir);
+
+ if (!set_uid)
+ args.uid = sb.st_uid;
+ if (!set_gid)
+ args.gid = sb.st_gid;
+ if (!set_mask)
+ args.mode = sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ }
+
+ if (mount("hpfs", dir, mntflags, &args) < 0)
+ err(EX_OSERR, "%s", dev);
+
+ exit (0);
+}
+
+gid_t
+a_gid(s)
+ char *s;
+{
+ struct group *gr;
+ char *gname;
+ gid_t gid;
+
+ if ((gr = getgrnam(s)) != NULL)
+ gid = gr->gr_gid;
+ else {
+ for (gname = s; *s && isdigit(*s); ++s);
+ if (!*s)
+ gid = atoi(gname);
+ else
+ errx(EX_NOUSER, "unknown group id: %s", gname);
+ }
+ return (gid);
+}
+
+uid_t
+a_uid(s)
+ char *s;
+{
+ struct passwd *pw;
+ char *uname;
+ uid_t uid;
+
+ if ((pw = getpwnam(s)) != NULL)
+ uid = pw->pw_uid;
+ else {
+ for (uname = s; *s && isdigit(*s); ++s);
+ if (!*s)
+ uid = atoi(uname);
+ else
+ errx(EX_NOUSER, "unknown user id: %s", uname);
+ }
+ return (uid);
+}
+
+mode_t
+a_mask(s)
+ char *s;
+{
+ int done, rv=0;
+ char *ep;
+
+ done = 0;
+ if (*s >= '0' && *s <= '7') {
+ done = 1;
+ rv = strtol(optarg, &ep, 8);
+ }
+ if (!done || rv < 0 || *ep)
+ errx(EX_USAGE, "invalid file mode: %s", s);
+ return (rv);
+}
+
+void
+usage()
+{
+ fprintf(stderr, "usage: mount_hpfs [-u user] [-g group] [-m mask] bdev dir\n");
+ exit(EX_USAGE);
+}
+
+void
+load_u2wtable (pargs, name)
+ struct hpfs_args *pargs;
+ char *name;
+{
+ FILE *f;
+ int i, code;
+ char buf[128];
+ char *fn;
+
+ if (*name == '/')
+ fn = name;
+ else {
+ snprintf(buf, sizeof(buf), "/usr/libdata/msdosfs/%s", name);
+ buf[127] = '\0';
+ fn = buf;
+ }
+ if ((f = fopen(fn, "r")) == NULL)
+ err(EX_NOINPUT, "%s", fn);
+ for (i = 0; i < 128; i++) {
+ if (fscanf(f, "%i", &code) != 1)
+ errx(EX_DATAERR, "u2w: missing item number %d", i);
+ /* pargs->u2w[i] = code; */
+ }
+ for (i = 0; i < 128; i++) {
+ if (fscanf(f, "%i", &code) != 1)
+ errx(EX_DATAERR, "d2u: missing item number %d", i);
+ pargs->d2u[i] = code;
+ }
+ for (i = 0; i < 128; i++) {
+ if (fscanf(f, "%i", &code) != 1)
+ errx(EX_DATAERR, "u2d: missing item number %d", i);
+ pargs->u2d[i] = code;
+ }
+ fclose(f);
+}
diff --git a/sbin/mount_msdosfs/Makefile b/sbin/mount_msdosfs/Makefile
new file mode 100644
index 0000000..8752f0d
--- /dev/null
+++ b/sbin/mount_msdosfs/Makefile
@@ -0,0 +1,21 @@
+#
+# $FreeBSD$
+#
+
+PROG= mount_msdosfs
+SRCS= mount_msdosfs.c getmntopts.c
+MAN= mount_msdosfs.8
+DPADD= ${LIBKICONV}
+LDADD= -lkiconv
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${MOUNT}
+WARNS?= 6
+
+# Needs to be dynamically linked for optional dlopen() access to
+# userland libiconv
+NO_SHARED?= NO
+
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_msdosfs/mount_msdosfs.8 b/sbin/mount_msdosfs/mount_msdosfs.8
new file mode 100644
index 0000000..6147aa4
--- /dev/null
+++ b/sbin/mount_msdosfs/mount_msdosfs.8
@@ -0,0 +1,226 @@
+.\" $NetBSD: mount_msdos.8,v 1.13 1998/02/06 05:57:00 perry Exp $
+.\"
+.\" Copyright (c) 1993,1994 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. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed by Christopher G. Demetriou.
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 7, 1994
+.Dt MOUNT_MSDOSFS 8
+.Os
+.Sh NAME
+.Nm mount_msdosfs
+.Nd mount an MS-DOS file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl 9ls
+.Op Fl D Ar DOS_codepage
+.Op Fl g Ar gid
+.Op Fl L Ar locale
+.Op Fl M Ar mask
+.Op Fl m Ar mask
+.Op Fl o Ar options
+.Op Fl u Ar uid
+.Op Fl W Ar table
+.Ar special node
+.Sh DESCRIPTION
+The
+.Nm
+utility attaches the MS-DOS file system residing on
+the device
+.Pa special
+to the global file system namespace at the location
+indicated by
+.Pa node .
+This command is normally executed by
+.Xr mount 8
+at boot time, but can be used by any user to mount an
+MS-DOS file system on any directory that they own (provided,
+of course, that they have appropriate access to the device that
+contains the file system).
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl o Ar options
+Use the specified mount
+.Ar options ,
+as described in
+.Xr mount 8 .
+The following MSDOS file system-specific options are available:
+.Bl -tag -width indent
+.It Cm longnames
+Force Windows 95 long filenames to be visible.
+.It Cm shortnames
+Force only the old MS-DOS 8.3 style filenames to be visible.
+.It Cm nowin95
+Completely ignore Windows 95 extended file information.
+.El
+.It Fl u Ar uid
+Set the owner of the files in the file system to
+.Ar uid .
+The default owner is the owner of the directory
+on which the file system is being mounted.
+.It Fl g Ar gid
+Set the group of the files in the file system to
+.Ar gid .
+The default group is the group of the directory
+on which the file system is being mounted.
+.It Fl m Ar mask
+Specify the maximum file permissions for files
+in the file system.
+(For example, a
+.Ar mask
+of
+.Li 755
+specifies that, by default, the owner should have
+read, write, and execute permissions for files, but
+others should only have read and execute permissions.
+See
+.Xr chmod 1
+for more information about octal file modes.
+Only the nine low-order bits of
+.Ar mask
+are used.
+The value of
+.Ar -M
+is used if it is supplied and
+.Ar -m
+is omitted.
+The default
+.Ar mask
+is taken from the
+directory on which the file system is being mounted.
+.It Fl M Ar mask
+Specify the maximum file permissions for directories
+in the file system.
+The value of
+.Ar -m
+is used if it is supplied and
+.Ar -M
+is omitted.
+See the previous option's description for details.
+.It Fl s
+Force behaviour to
+ignore and not generate Win'95 long filenames.
+.It Fl l
+Force listing and generation of
+Win'95 long filenames
+and separate creation/modification/access dates.
+.Pp
+If neither
+.Fl s
+nor
+.Fl l
+are given,
+.Nm
+searches the root directory of the file system to
+be mounted for any existing Win'95 long filenames.
+If no such entries are found, but short DOS filenames are found,
+.Fl s
+is the default.
+Otherwise
+.Fl l
+is assumed.
+.It Fl 9
+Ignore the special Win'95 directory entries even
+if deleting or renaming a file.
+This forces
+.Fl s .
+.\".It Fl G
+.\"This option causes the file system to be interpreted as an Atari-Gemdos
+.\"file system.
+.\"The differences to the MS-DOS file system are minimal and
+.\"limited to the boot block.
+.\"This option enforces
+.\".Fl s .
+.It Fl L Ar locale
+Specify locale name used for file name conversions
+for DOS and Win'95 names.
+By default ISO 8859-1 assumed as local character set.
+.It Fl D Ar DOS_codepage
+Specify the MS-DOS code page (aka IBM/OEM code page) name used for
+file name conversions for DOS names.
+.It Fl W Ar table
+.Bf Em
+This option is preserved for backward compatibility purpose only,
+and will be removed in the future.
+Please avoid using this option.
+.Ef
+.Pp
+Specify text file name with conversion table:
+.Pa iso22dos , iso72dos , koi2dos , koi8u2dos .
+.El
+.Sh EXAMPLES
+To mount a Russian MS-DOS file system located in
+.Pa /dev/ad1s1 :
+.Pp
+.Dl "mount_msdosfs -L ru_RU.KOI8-R -D CP866 /dev/ad1s1 /mnt"
+.Pp
+To mount a Japanese MS-DOS file system located in
+.Pa /dev/ad1s1 :
+.Pp
+.Dl "mount_msdosfs -L ja_JP.eucJP -D CP932 /dev/ad1s1 /mnt"
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr msdosfs 5 ,
+.Xr mount 8
+.Pp
+List of Localized MS Operating Systems:
+.Pa http://www.microsoft.com/globaldev/reference/oslocversion.mspx .
+.Sh CAVEATS
+The use of the
+.Fl 9
+flag could result in damaged file systems,
+albeit the damage is in part taken care of by
+procedures similar to the ones used in Win'95.
+.Pp
+.Fx 2.1
+and earlier versions could not handle cluster sizes larger than 16K.
+Just mounting an MS-DOS file system could cause corruption to any
+mounted file system.
+Cluster sizes larger than 16K are unavoidable for file system sizes
+larger than 1G, and also occur when file systems larger than 1G are
+shrunk to smaller than 1G using FIPS.
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 2.0 .
+Its predecessor, the
+.Nm mount_pcfs
+utility appeared in
+.Fx 1.0 ,
+and was abandoned in favor
+of the more aptly-named
+.Nm .
+.Pp
+The character code conversion routine was added by
+.An Ryuichiro Imura Aq imura@ryu16.org
+at 2003.
diff --git a/sbin/mount_msdosfs/mount_msdosfs.c b/sbin/mount_msdosfs/mount_msdosfs.c
new file mode 100644
index 0000000..a4e29d8
--- /dev/null
+++ b/sbin/mount_msdosfs/mount_msdosfs.c
@@ -0,0 +1,331 @@
+/* $NetBSD: mount_msdos.c,v 1.18 1997/09/16 12:24:18 lukem Exp $ */
+
+/*
+ * Copyright (c) 1994 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou.
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/iconv.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <grp.h>
+#include <locale.h>
+#include <pwd.h>
+#include <stdio.h>
+/* must be after stdio to declare fparseln */
+#include <libutil.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_FORCE,
+ MOPT_SYNC,
+ MOPT_UPDATE,
+ MOPT_END
+};
+
+static gid_t a_gid(char *);
+static uid_t a_uid(char *);
+static mode_t a_mask(char *);
+static void usage(void) __dead2;
+static int set_charset(struct iovec **iov, int *iovlen, const char *, const char *);
+
+int
+main(int argc, char **argv)
+{
+ struct iovec *iov = NULL;
+ int iovlen = 0;
+ struct stat sb;
+ int c, mntflags, set_gid, set_uid, set_mask, set_dirmask;
+ int optflags = 0;
+ char *dev, *dir, mntpath[MAXPATHLEN], *csp;
+ char fstype[] = "msdosfs";
+ char *cs_dos = NULL;
+ char *cs_local = NULL;
+ mode_t mask = 0, dirmask = 0;
+ uid_t uid = 0;
+ gid_t gid = 0;
+ getmnt_silent = 1;
+
+ mntflags = set_gid = set_uid = set_mask = set_dirmask = 0;
+
+ while ((c = getopt(argc, argv, "sl9u:g:m:M:o:L:D:W:")) != -1) {
+ switch (c) {
+ case 's':
+ build_iovec(&iov, &iovlen, "shortnames", NULL, (size_t)-1);
+ break;
+ case 'l':
+ build_iovec(&iov, &iovlen, "longnames", NULL, (size_t)-1);
+ break;
+ case '9':
+ build_iovec_argf(&iov, &iovlen, "nowin95", "", (size_t)-1);
+ break;
+ case 'u':
+ uid = a_uid(optarg);
+ set_uid = 1;
+ break;
+ case 'g':
+ gid = a_gid(optarg);
+ set_gid = 1;
+ break;
+ case 'm':
+ mask = a_mask(optarg);
+ set_mask = 1;
+ break;
+ case 'M':
+ dirmask = a_mask(optarg);
+ set_dirmask = 1;
+ break;
+ case 'L': {
+ const char *quirk = NULL;
+ if (setlocale(LC_CTYPE, optarg) == NULL)
+ err(EX_CONFIG, "%s", optarg);
+ csp = strchr(optarg,'.');
+ if (!csp)
+ err(EX_CONFIG, "%s", optarg);
+ quirk = kiconv_quirkcs(csp + 1, KICONV_VENDOR_MICSFT);
+ build_iovec_argf(&iov, &iovlen, "cs_local", quirk);
+ cs_local = strdup(quirk);
+ }
+ break;
+ case 'D':
+ cs_dos = strdup(optarg);
+ build_iovec_argf(&iov, &iovlen, "cs_dos", cs_dos, (size_t)-1);
+ break;
+ case 'o': {
+ char *p = NULL;
+ char *val = strdup("");
+ getmntopts(optarg, mopts, &mntflags, &optflags);
+ p = strchr(optarg, '=');
+ if (p != NULL) {
+ free(val);
+ *p = '\0';
+ val = p + 1;
+ }
+ build_iovec(&iov, &iovlen, optarg, val, (size_t)-1);
+ }
+ break;
+ case 'W':
+ if (strcmp(optarg, "iso22dos") == 0) {
+ cs_local = strdup("ISO8859-2");
+ cs_dos = strdup("CP852");
+ } else if (strcmp(optarg, "iso72dos") == 0) {
+ cs_local = strdup("ISO8859-7");
+ cs_dos = strdup("CP737");
+ } else if (strcmp(optarg, "koi2dos") == 0) {
+ cs_local = strdup("KOI8-R");
+ cs_dos = strdup("CP866");
+ } else if (strcmp(optarg, "koi8u2dos") == 0) {
+ cs_local = strdup("KOI8-U");
+ cs_dos = strdup("CP866");
+ } else {
+ err(EX_NOINPUT, "%s", optarg);
+ }
+ build_iovec(&iov, &iovlen, "cs_local", cs_local, (size_t)-1);
+ build_iovec(&iov, &iovlen, "cs_dos", cs_dos, (size_t)-1);
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (optind + 2 != argc)
+ usage();
+
+ if (set_mask && !set_dirmask) {
+ dirmask = mask;
+ set_dirmask = 1;
+ }
+ else if (set_dirmask && !set_mask) {
+ mask = dirmask;
+ set_mask = 1;
+ }
+
+ dev = argv[optind];
+ dir = argv[optind + 1];
+
+ if (cs_local != NULL) {
+ if (set_charset(&iov, &iovlen, cs_local, cs_dos) == -1)
+ err(EX_OSERR, "msdosfs_iconv");
+ build_iovec_argf(&iov, &iovlen, "kiconv", "");
+ } else if (cs_dos != NULL) {
+ build_iovec_argf(&iov, &iovlen, "cs_local", "ISO8859-1");
+ if (set_charset(&iov, &iovlen, "ISO8859-1", cs_dos) == -1)
+ err(EX_OSERR, "msdosfs_iconv");
+ build_iovec_argf(&iov, &iovlen, "kiconv", "");
+ }
+
+ /*
+ * Resolve the mountpoint with realpath(3) and remove unnecessary
+ * slashes from the devicename if there are any.
+ */
+ (void)checkpath(dir, mntpath);
+ (void)rmslashes(dev, dev);
+
+ if (!set_gid || !set_uid || !set_mask) {
+ if (stat(mntpath, &sb) == -1)
+ err(EX_OSERR, "stat %s", mntpath);
+
+ if (!set_uid)
+ uid = sb.st_uid;
+ if (!set_gid)
+ gid = sb.st_gid;
+ if (!set_mask)
+ mask = dirmask =
+ sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ }
+
+ build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1);
+ build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1);
+ build_iovec(&iov, &iovlen, "from", dev, (size_t)-1);
+ build_iovec_argf(&iov, &iovlen, "uid", "%d", uid);
+ build_iovec_argf(&iov, &iovlen, "gid", "%u", gid);
+ build_iovec_argf(&iov, &iovlen, "mask", "%u", mask);
+ build_iovec_argf(&iov, &iovlen, "dirmask", "%u", dirmask);
+
+ if (nmount(iov, iovlen, mntflags) < 0)
+ err(1, "%s", dev);
+
+ exit (0);
+}
+
+gid_t
+a_gid(s)
+ char *s;
+{
+ struct group *gr;
+ char *gname;
+ gid_t gid;
+
+ if ((gr = getgrnam(s)) != NULL)
+ gid = gr->gr_gid;
+ else {
+ for (gname = s; *s && isdigit(*s); ++s);
+ if (!*s)
+ gid = atoi(gname);
+ else
+ errx(EX_NOUSER, "unknown group id: %s", gname);
+ }
+ return (gid);
+}
+
+uid_t
+a_uid(s)
+ char *s;
+{
+ struct passwd *pw;
+ char *uname;
+ uid_t uid;
+
+ if ((pw = getpwnam(s)) != NULL)
+ uid = pw->pw_uid;
+ else {
+ for (uname = s; *s && isdigit(*s); ++s);
+ if (!*s)
+ uid = atoi(uname);
+ else
+ errx(EX_NOUSER, "unknown user id: %s", uname);
+ }
+ return (uid);
+}
+
+mode_t
+a_mask(s)
+ char *s;
+{
+ int done, rv;
+ char *ep;
+
+ done = 0;
+ rv = -1;
+ if (*s >= '0' && *s <= '7') {
+ done = 1;
+ rv = strtol(optarg, &ep, 8);
+ }
+ if (!done || rv < 0 || *ep)
+ errx(EX_USAGE, "invalid file mode: %s", s);
+ return (rv);
+}
+
+void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: mount_msdosfs [-9ls] [-D DOS_codepage] [-g gid] [-L locale]",
+ " [-M mask] [-m mask] [-o options] [-u uid]",
+ " [-W table] special node");
+ exit(EX_USAGE);
+}
+
+int
+set_charset(struct iovec **iov, int *iovlen, const char *cs_local, const char *cs_dos)
+{
+ int error;
+
+ if (modfind("msdosfs_iconv") < 0)
+ if (kldload("msdosfs_iconv") < 0 || modfind("msdosfs_iconv") < 0) {
+ warnx("cannot find or load \"msdosfs_iconv\" kernel module");
+ return (-1);
+ }
+
+ build_iovec_argf(iov, iovlen, "cs_win", ENCODING_UNICODE);
+ error = kiconv_add_xlat16_cspairs(ENCODING_UNICODE, cs_local);
+ if (error)
+ return (-1);
+ if (cs_dos != NULL) {
+ error = kiconv_add_xlat16_cspairs(cs_dos, cs_local);
+ if (error)
+ return (-1);
+ } else {
+ build_iovec_argf(iov, iovlen, "cs_dos", cs_local);
+ error = kiconv_add_xlat16_cspair(cs_local, cs_local,
+ KICONV_FROM_UPPER | KICONV_LOWER);
+ if (error)
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/sbin/mount_nfs/Makefile b/sbin/mount_nfs/Makefile
new file mode 100644
index 0000000..68fe70a
--- /dev/null
+++ b/sbin/mount_nfs/Makefile
@@ -0,0 +1,16 @@
+# @(#)Makefile 8.2 (Berkeley) 3/27/94
+#
+# $FreeBSD$
+
+PROG= mount_nfs
+SRCS= mount_nfs.c getmntopts.c mounttab.c
+MAN= mount_nfs.8
+
+MOUNT= ${.CURDIR}/../mount
+UMNTALL= ${.CURDIR}/../../usr.sbin/rpc.umntall
+CFLAGS+= -DNFS -I${MOUNT} -I${UMNTALL}
+WARNS?= 2
+
+.PATH: ${MOUNT} ${UMNTALL}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_nfs/mount_nfs.8 b/sbin/mount_nfs/mount_nfs.8
new file mode 100644
index 0000000..ce07e13a
--- /dev/null
+++ b/sbin/mount_nfs/mount_nfs.8
@@ -0,0 +1,338 @@
+.\" Copyright (c) 1992, 1993, 1994, 1995
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)mount_nfs.8 8.3 (Berkeley) 3/29/95
+.\" $FreeBSD$
+.\"
+.Dd December 25, 2005
+.Dt MOUNT_NFS 8
+.Os
+.Sh NAME
+.Nm mount_nfs
+.Nd mount NFS file systems
+.Sh SYNOPSIS
+.Nm
+.Op Fl 23bcdiLlNPsTU
+.Op Fl a Ar maxreadahead
+.Op Fl D Ar deadthresh
+.Op Fl g Ar maxgroups
+.Op Fl I Ar readdirsize
+.Op Fl o Ar options
+.Op Fl R Ar retrycnt
+.Op Fl r Ar readsize
+.Op Fl t Ar timeout
+.Op Fl w Ar writesize
+.Op Fl x Ar retrans
+.Ar rhost : Ns Ar path node
+.Sh DESCRIPTION
+The
+.Nm
+utility calls the
+.Xr mount 2
+system call to prepare and graft a remote NFS file system
+.Pq Ar rhost : Ns Ar path
+on to the file system tree at the point
+.Ar node .
+This command is normally executed by
+.Xr mount 8 .
+It implements the mount protocol as described in RFC 1094, Appendix A and
+.%T "NFS: Network File System Version 3 Protocol Specification" ,
+Appendix I.
+.Pp
+By default,
+.Nm
+keeps retrying until the mount succeeds.
+This behaviour is intended for file systems listed in
+.Xr fstab 5
+that are critical to the boot process.
+For non-critical file systems, the
+.Fl b
+and
+.Fl R
+flags provide mechanisms to prevent the boot process from hanging
+if the server is unavailable.
+.Pp
+If the server becomes unresponsive while an NFS file system is
+mounted, any new or outstanding file operations on that file system
+will hang uninterruptibly until the server comes back.
+To modify this default behaviour, see the
+.Fl i
+and
+.Fl s
+flags.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl 2
+Use the NFS Version 2 protocol (the default is to try version 3 first
+then version 2).
+Note that NFS version 2 has a file size limit of 2 gigabytes.
+.It Fl 3
+Use the NFS Version 3 protocol.
+.It Fl D
+Set the
+.Dq "dead server threshold"
+to the specified number of round trip timeout intervals before a
+.Dq "server not responding"
+message is displayed.
+.It Fl I
+Set the readdir read size to the specified value.
+The value should normally
+be a multiple of
+.Dv DIRBLKSIZ
+that is <= the read size for the mount.
+.It Fl L
+Do
+.Em not
+forward
+.Xr fcntl 2
+locks over the wire.
+All locks will be local and not seen by the server
+and likewise not seen by other NFS clients.
+This removes the need to run the
+.Xr rpcbind 8
+service and the
+.Xr rpc.statd 8
+and
+.Xr rpc.lockd 8
+servers on the client.
+Note that this option will only be honored when performing the
+initial mount, it will be silently ignored if used while updating
+the mount options.
+.It Fl N
+Do
+.Em not
+use a reserved socket port number (see below).
+.It Fl P
+Use a reserved socket port number.
+This flag is obsolete, and only retained for compatibility reasons.
+Reserved port numbers are used by default now.
+(For the rare case where the client has a trusted root account
+but untrustworthy users and the network cables are in secure areas this does
+help, but for normal desktop clients this does not apply.)
+.It Fl R
+Set the mount retry count to the specified value.
+The default is a retry count of zero, which means to keep retrying
+forever.
+There is a 60 second delay between each attempt.
+.It Fl T
+Use TCP transport instead of UDP.
+This is recommended for servers that are not on the same LAN cable as
+the client.
+Not all NFS servers support this method, especially older ones,
+caution should be observed in these cases.
+.It Fl U
+Force the mount protocol to use UDP transport, even for TCP NFS mounts.
+(Necessary for some old
+.Bx
+servers.)
+.It Fl a
+Set the read-ahead count to the specified value.
+This may be in the range of 0 - 4, and determines how many blocks
+will be read ahead when a large file is being read sequentially.
+Trying a value greater than 1 for this is suggested for
+mounts with a large bandwidth * delay product.
+.It Fl b
+If an initial attempt to contact the server fails, fork off a child to keep
+trying the mount in the background.
+Useful for
+.Xr fstab 5 ,
+where the file system mount is not critical to multiuser operation.
+.It Fl c
+For UDP mount points, do not do a
+.Xr connect 2 .
+This must be used if the server does not reply to requests from the standard
+NFS port number 2049 or replies to requests using a different IP address
+(which can occur if the server is multi-homed).
+Setting the
+.Va vfs.nfs.nfs_ip_paranoia
+sysctl to 0 will make this option the default.
+.It Fl d
+Turn off the dynamic retransmit timeout estimator.
+This may be useful for UDP mounts that exhibit high retry rates,
+since it is possible that the dynamically estimated timeout interval is too
+short.
+.It Fl g
+Set the maximum size of the group list for the credentials to the
+specified value.
+This should be used for mounts on old servers that cannot handle a
+group list size of 16, as specified in RFC 1057.
+Try 8, if users in a lot of groups cannot get response from the mount
+point.
+.It Fl i
+Make the mount interruptible, which implies that file system calls that
+are delayed due to an unresponsive server will fail with EINTR when a
+termination signal is posted for the process.
+.It Fl l
+Used with NQNFS and NFSV3 to specify that the \fBReaddirPlus\fR RPC should
+be used.
+This option reduces RPC traffic for cases such as
+.Dq "ls -l" ,
+but tends to flood the attribute and name caches with prefetched entries.
+Try this option and see whether performance improves or degrades.
+Probably
+most useful for client to server network interconnects with a large bandwidth
+times delay product.
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+The following NFS specific options are also available:
+.Bl -tag -width indent
+.It Cm port Ns = Ns Aq Ar port_number
+Use specified port number for NFS requests.
+The default is to query the portmapper for the NFS port.
+.It Cm acregmin Ns = Ns Aq Ar seconds
+.It Cm acregmax Ns = Ns Aq Ar seconds
+.It Cm acdirmin Ns = Ns Aq Ar seconds
+.It Cm acdirmax Ns = Ns Aq Ar seconds
+When attributes of files are cached, a timeout calculated to determine
+whether a given cache entry has expired.
+These four values determine the upper and lower bounds of the timeouts for
+.Dq directory
+attributes and
+.Dq regular
+(ie: everything else).
+The default values are 3 -> 60 seconds
+for regular files, and 30 -> 60 seconds for directories.
+The algorithm to calculate the timeout is based on the age of the file.
+The older the file,
+the longer the cache is considered valid, subject to the limits above.
+.It Cm noinet4 , noinet6
+Disables
+.Dv AF_INET
+or
+.Dv AF_INET6
+connections.
+Useful for hosts that have
+both an A record and an AAAA record for the same name.
+.El
+.Pp
+.Sy Historic Fl o Sy Options
+.Pp
+Use of these options is deprecated, they are only mentioned here for
+compatibility with historic versions of
+.Nm .
+.Bl -tag -width ".Cm dumbtimer"
+.It Cm bg
+Same as
+.Fl b .
+.It Cm conn
+Same as not specifying
+.Fl c .
+.It Cm dumbtimer
+Same as
+.Fl d .
+.It Cm intr
+Same as
+.Fl i .
+.It Cm lockd
+Same as not specifying
+.Fl L .
+.It Cm nfsv2
+Same as
+.Fl 2 .
+.It Cm nfsv3
+Same as
+.Fl 3 .
+.It Cm rdirplus
+Same as
+.Fl l .
+.It Cm mntudp
+Same as
+.Fl U .
+.It Cm resvport
+Same as
+.Fl P .
+.It Cm soft
+Same as
+.Fl s .
+.It Cm tcp
+Same as
+.Fl T .
+.El
+.It Fl r
+Set the read data size to the specified value.
+It should normally be a power of 2 greater than or equal to 1024.
+This should be used for UDP mounts when the
+.Dq "fragments dropped due to timeout"
+value is getting large while actively using a mount point.
+(Use
+.Xr netstat 1
+with the
+.Fl s
+option to see what the
+.Dq "fragments dropped due to timeout"
+value is.)
+See the
+.Fl w
+option as well.
+.It Fl s
+A soft mount, which implies that file system calls will fail
+after
+.Ar retrycnt
+round trip timeout intervals.
+.It Fl t
+Set the initial retransmit timeout to the specified value.
+May be useful for fine tuning UDP mounts over internetworks
+with high packet loss rates or an overloaded server.
+Try increasing the interval if
+.Xr nfsstat 1
+shows high retransmit rates while the file system is active or reducing the
+value if there is a low retransmit rate but long response delay observed.
+(Normally, the
+.Fl d
+option should be specified when using this option to manually
+tune the timeout
+interval.)
+.It Fl w
+Set the write data size to the specified value.
+Ditto the comments w.r.t.\& the
+.Fl r
+option, but using the
+.Dq "fragments dropped due to timeout"
+value on the server instead of the client.
+Note that both the
+.Fl r
+and
+.Fl w
+options should only be used as a last ditch effort at improving performance
+when mounting servers that do not support TCP mounts.
+.It Fl x
+Set the retransmit timeout count for soft mounts to the specified value.
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8 ,
+.Xr nfsd 8 ,
+.Xr nfsiod 8 ,
+.Xr showmount 8
diff --git a/sbin/mount_nfs/mount_nfs.c b/sbin/mount_nfs/mount_nfs.c
new file mode 100644
index 0000000..a2e0586
--- /dev/null
+++ b/sbin/mount_nfs/mount_nfs.c
@@ -0,0 +1,926 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfsclient/nfs.h>
+#include <nfsclient/nfsargs.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+#include "mounttab.h"
+
+#define ALTF_BG 0x1
+#define ALTF_NOCONN 0x2
+#define ALTF_DUMBTIMR 0x4
+#define ALTF_INTR 0x8
+#define ALTF_NFSV3 0x20
+#define ALTF_RDIRPLUS 0x40
+#define ALTF_MNTUDP 0x80
+#define ALTF_RESVPORT 0x100
+#define ALTF_SEQPACKET 0x200
+#define ALTF_SOFT 0x800
+#define ALTF_TCP 0x1000
+#define ALTF_PORT 0x2000
+#define ALTF_NFSV2 0x4000
+#define ALTF_ACREGMIN 0x8000
+#define ALTF_ACREGMAX 0x10000
+#define ALTF_ACDIRMIN 0x20000
+#define ALTF_ACDIRMAX 0x40000
+#define ALTF_NOLOCKD 0x80000
+#define ALTF_NOINET4 0x100000
+#define ALTF_NOINET6 0x200000
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_FORCE,
+ MOPT_UPDATE,
+ MOPT_ASYNC,
+ { "bg", 0, ALTF_BG, 1 },
+ { "conn", 1, ALTF_NOCONN, 1 },
+ { "dumbtimer", 0, ALTF_DUMBTIMR, 1 },
+ { "intr", 0, ALTF_INTR, 1 },
+ { "nfsv3", 0, ALTF_NFSV3, 1 },
+ { "rdirplus", 0, ALTF_RDIRPLUS, 1 },
+ { "mntudp", 0, ALTF_MNTUDP, 1 },
+ { "resvport", 0, ALTF_RESVPORT, 1 },
+ { "soft", 0, ALTF_SOFT, 1 },
+ { "tcp", 0, ALTF_TCP, 1 },
+ { "port=", 0, ALTF_PORT, 1 },
+ { "nfsv2", 0, ALTF_NFSV2, 1 },
+ { "acregmin=", 0, ALTF_ACREGMIN, 1 },
+ { "acregmax=", 0, ALTF_ACREGMAX, 1 },
+ { "acdirmin=", 0, ALTF_ACDIRMIN, 1 },
+ { "acdirmax=", 0, ALTF_ACDIRMAX, 1 },
+ { "lockd", 1, ALTF_NOLOCKD, 1 },
+ { "inet4", 1, ALTF_NOINET4, 1 },
+ { "inet6", 1, ALTF_NOINET6, 1 },
+ MOPT_END
+};
+
+struct nfs_args nfsdefargs = {
+ NFS_ARGSVERSION,
+ NULL,
+ sizeof (struct sockaddr_in),
+ SOCK_DGRAM,
+ 0,
+ NULL,
+ 0,
+ NFSMNT_RESVPORT,
+ NFS_WSIZE,
+ NFS_RSIZE,
+ NFS_READDIRSIZE,
+ 10,
+ NFS_RETRANS,
+ NFS_MAXGRPS,
+ NFS_DEFRAHEAD,
+ 0, /* was: NQ_DEFLEASE */
+ NFS_MAXDEADTHRESH, /* was: NQ_DEADTHRESH */
+ NULL,
+ /* args version 4 */
+ NFS_MINATTRTIMO,
+ NFS_MAXATTRTIMO,
+ NFS_MINDIRATTRTIMO,
+ NFS_MAXDIRATTRTIMO,
+};
+
+/* Table for af,sotype -> netid conversions. */
+struct nc_protos {
+ char *netid;
+ int af;
+ int sotype;
+} nc_protos[] = {
+ {"udp", AF_INET, SOCK_DGRAM},
+ {"tcp", AF_INET, SOCK_STREAM},
+ {"udp6", AF_INET6, SOCK_DGRAM},
+ {"tcp6", AF_INET6, SOCK_STREAM},
+ {NULL}
+};
+
+struct nfhret {
+ u_long stat;
+ long vers;
+ long auth;
+ long fhsize;
+ u_char nfh[NFSX_V3FHMAX];
+};
+#define BGRND 1
+#define ISBGRND 2
+#define OF_NOINET4 4
+#define OF_NOINET6 8
+int retrycnt = -1;
+int opflags = 0;
+int nfsproto = IPPROTO_UDP;
+int mnttcp_ok = 1;
+char *portspec = NULL; /* Server nfs port; NULL means look up via rpcbind. */
+enum mountmode {
+ ANY,
+ V2,
+ V3
+} mountmode = ANY;
+
+/* Return codes for nfs_tryproto. */
+enum tryret {
+ TRYRET_SUCCESS,
+ TRYRET_TIMEOUT, /* No response received. */
+ TRYRET_REMOTEERR, /* Error received from remote server. */
+ TRYRET_LOCALERR /* Local failure. */
+};
+
+int getnfsargs(char *, struct nfs_args *);
+/* void set_rpc_maxgrouplist(int); */
+struct netconfig *getnetconf_cached(const char *netid);
+char *netidbytype(int af, int sotype);
+void usage(void) __dead2;
+int xdr_dir(XDR *, char *);
+int xdr_fh(XDR *, struct nfhret *);
+enum tryret nfs_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai,
+ char *hostp, char *spec, char **errstr);
+enum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr);
+
+/*
+ * Used to set mount flags with getmntopts. Call with dir=TRUE to
+ * initialize altflags from the current mount flags. Call with
+ * dir=FALSE to update mount flags with the new value of altflags after
+ * the call to getmntopts.
+ */
+static void
+set_flags(int* altflags, int* nfsflags, int dir)
+{
+#define F2(af, nf) \
+ if (dir) { \
+ if (*nfsflags & NFSMNT_##nf) \
+ *altflags |= ALTF_##af; \
+ else \
+ *altflags &= ~ALTF_##af; \
+ } else { \
+ if (*altflags & ALTF_##af) \
+ *nfsflags |= NFSMNT_##nf; \
+ else \
+ *nfsflags &= ~NFSMNT_##nf; \
+ }
+#define F(f) F2(f,f)
+
+ F(NOCONN);
+ F(DUMBTIMR);
+ F2(INTR, INT);
+ F(RDIRPLUS);
+ F(RESVPORT);
+ F(SOFT);
+ F(ACREGMIN);
+ F(ACREGMAX);
+ F(ACDIRMIN);
+ F(ACDIRMAX);
+ F(NOLOCKD);
+
+#undef F
+#undef F2
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ struct nfs_args *nfsargsp;
+ struct nfs_args nfsargs;
+ int mntflags, altflags, num;
+ char *name, *p, *spec;
+ char mntpath[MAXPATHLEN];
+
+ mntflags = 0;
+ altflags = 0;
+ nfsargs = nfsdefargs;
+ nfsargsp = &nfsargs;
+ while ((c = getopt(argc, argv,
+ "23a:bcdD:g:I:iLlNo:PR:r:sTt:w:x:U")) != -1)
+ switch (c) {
+ case '2':
+ mountmode = V2;
+ break;
+ case '3':
+ mountmode = V3;
+ break;
+ case 'a':
+ num = strtol(optarg, &p, 10);
+ if (*p || num < 0)
+ errx(1, "illegal -a value -- %s", optarg);
+ nfsargsp->readahead = num;
+ nfsargsp->flags |= NFSMNT_READAHEAD;
+ break;
+ case 'b':
+ opflags |= BGRND;
+ break;
+ case 'c':
+ nfsargsp->flags |= NFSMNT_NOCONN;
+ break;
+ case 'D':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -D value -- %s", optarg);
+ nfsargsp->deadthresh = num;
+ nfsargsp->flags |= NFSMNT_DEADTHRESH;
+ break;
+ case 'd':
+ nfsargsp->flags |= NFSMNT_DUMBTIMR;
+ break;
+#if 0 /* XXXX */
+ case 'g':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -g value -- %s", optarg);
+ set_rpc_maxgrouplist(num);
+ nfsargsp->maxgrouplist = num;
+ nfsargsp->flags |= NFSMNT_MAXGRPS;
+ break;
+#endif
+ case 'I':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -I value -- %s", optarg);
+ nfsargsp->readdirsize = num;
+ nfsargsp->flags |= NFSMNT_READDIRSIZE;
+ break;
+ case 'i':
+ nfsargsp->flags |= NFSMNT_INT;
+ break;
+ case 'L':
+ nfsargsp->flags |= NFSMNT_NOLOCKD;
+ break;
+ case 'l':
+ nfsargsp->flags |= NFSMNT_RDIRPLUS;
+ break;
+ case 'N':
+ nfsargsp->flags &= ~NFSMNT_RESVPORT;
+ break;
+ case 'o':
+ altflags = 0;
+ set_flags(&altflags, &nfsargsp->flags, TRUE);
+ if (mountmode == V2)
+ altflags |= ALTF_NFSV2;
+ else if (mountmode == V3)
+ altflags |= ALTF_NFSV3;
+ getmntopts(optarg, mopts, &mntflags, &altflags);
+ set_flags(&altflags, &nfsargsp->flags, FALSE);
+ /*
+ * Handle altflags which don't map directly to
+ * mount flags.
+ */
+ if (altflags & ALTF_BG)
+ opflags |= BGRND;
+ if (altflags & ALTF_NOINET4)
+ opflags |= OF_NOINET4;
+ if (altflags & ALTF_NOINET6)
+ opflags |= OF_NOINET6;
+ if (altflags & ALTF_MNTUDP)
+ mnttcp_ok = 0;
+ if (altflags & ALTF_TCP) {
+ nfsargsp->sotype = SOCK_STREAM;
+ nfsproto = IPPROTO_TCP;
+ }
+ if (altflags & ALTF_PORT) {
+ /*
+ * XXX Converting from a string to an int
+ * and back again is silly, and we should
+ * allow /etc/services names.
+ */
+ p = strstr(optarg, "port=");
+ if (p) {
+ asprintf(&portspec, "%d",
+ atoi(p + 5));
+ if (portspec == NULL)
+ err(1, "asprintf");
+ }
+ }
+ mountmode = ANY;
+ if (altflags & ALTF_NFSV2)
+ mountmode = V2;
+ if (altflags & ALTF_NFSV3)
+ mountmode = V3;
+ if (altflags & ALTF_ACREGMIN) {
+ p = strstr(optarg, "acregmin=");
+ if (p)
+ nfsargsp->acregmin = atoi(p + 9);
+ }
+ if (altflags & ALTF_ACREGMAX) {
+ p = strstr(optarg, "acregmax=");
+ if (p)
+ nfsargsp->acregmax = atoi(p + 9);
+ }
+ if (altflags & ALTF_ACDIRMIN) {
+ p = strstr(optarg, "acdirmin=");
+ if (p)
+ nfsargsp->acdirmin = atoi(p + 9);
+ }
+ if (altflags & ALTF_ACDIRMAX) {
+ p = strstr(optarg, "acdirmax=");
+ if (p)
+ nfsargsp->acdirmax = atoi(p + 9);
+ }
+ break;
+ case 'P':
+ /* obsolete for NFSMNT_RESVPORT, now default */
+ break;
+ case 'R':
+ num = strtol(optarg, &p, 10);
+ if (*p || num < 0)
+ errx(1, "illegal -R value -- %s", optarg);
+ retrycnt = num;
+ break;
+ case 'r':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -r value -- %s", optarg);
+ nfsargsp->rsize = num;
+ nfsargsp->flags |= NFSMNT_RSIZE;
+ break;
+ case 's':
+ nfsargsp->flags |= NFSMNT_SOFT;
+ break;
+ case 'T':
+ nfsargsp->sotype = SOCK_STREAM;
+ nfsproto = IPPROTO_TCP;
+ break;
+ case 't':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -t value -- %s", optarg);
+ nfsargsp->timeo = num;
+ nfsargsp->flags |= NFSMNT_TIMEO;
+ break;
+ case 'w':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -w value -- %s", optarg);
+ nfsargsp->wsize = num;
+ nfsargsp->flags |= NFSMNT_WSIZE;
+ break;
+ case 'x':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -x value -- %s", optarg);
+ nfsargsp->retrans = num;
+ nfsargsp->flags |= NFSMNT_RETRANS;
+ break;
+ case 'U':
+ mnttcp_ok = 0;
+ break;
+ default:
+ usage();
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2) {
+ usage();
+ /* NOTREACHED */
+ }
+
+ spec = *argv++;
+ name = *argv;
+
+ if (retrycnt == -1)
+ /* The default is to keep retrying forever. */
+ retrycnt = 0;
+ if (!getnfsargs(spec, nfsargsp))
+ exit(1);
+
+ /* resolve the mountpoint with realpath(3) */
+ (void)checkpath(name, mntpath);
+
+ if (mount("nfs", mntpath, mntflags, nfsargsp))
+ err(1, "%s", mntpath);
+
+ exit(0);
+}
+
+int
+getnfsargs(spec, nfsargsp)
+ char *spec;
+ struct nfs_args *nfsargsp;
+{
+ struct addrinfo hints, *ai_nfs, *ai;
+ enum tryret ret;
+ int ecode, speclen, remoteerr;
+ char *hostp, *delimp, *errstr;
+ size_t len;
+ static char nam[MNAMELEN + 1];
+
+ if ((delimp = strrchr(spec, ':')) != NULL) {
+ hostp = spec;
+ spec = delimp + 1;
+ } else if ((delimp = strrchr(spec, '@')) != NULL) {
+ warnx("path@server syntax is deprecated, use server:path");
+ hostp = delimp + 1;
+ } else {
+ warnx("no <host>:<dirpath> nfs-name");
+ return (0);
+ }
+ *delimp = '\0';
+
+ /*
+ * If there has been a trailing slash at mounttime it seems
+ * that some mountd implementations fail to remove the mount
+ * entries from their mountlist while unmounting.
+ */
+ for (speclen = strlen(spec);
+ speclen > 1 && spec[speclen - 1] == '/';
+ speclen--)
+ spec[speclen - 1] = '\0';
+ if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) {
+ warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG));
+ return (0);
+ }
+ /* Make both '@' and ':' notations equal */
+ if (*hostp != '\0') {
+ len = strlen(hostp);
+ memmove(nam, hostp, len);
+ nam[len] = ':';
+ memmove(nam + len + 1, spec, speclen);
+ nam[len + speclen + 1] = '\0';
+ }
+
+ /*
+ * Handle an internet host address.
+ */
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_NUMERICHOST;
+ hints.ai_socktype = nfsargsp->sotype;
+ if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) {
+ hints.ai_flags = 0;
+ if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs))
+ != 0) {
+ if (portspec == NULL)
+ errx(1, "%s: %s", hostp, gai_strerror(ecode));
+ else
+ errx(1, "%s:%s: %s", hostp, portspec,
+ gai_strerror(ecode));
+ return (0);
+ }
+ }
+
+ ret = TRYRET_LOCALERR;
+ for (;;) {
+ /*
+ * Try each entry returned by getaddrinfo(). Note the
+ * occurence of remote errors by setting `remoteerr'.
+ */
+ remoteerr = 0;
+ for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) {
+ if ((ai->ai_family == AF_INET6) &&
+ (opflags & OF_NOINET6))
+ continue;
+ if ((ai->ai_family == AF_INET) &&
+ (opflags & OF_NOINET4))
+ continue;
+ ret = nfs_tryproto(nfsargsp, ai, hostp, spec, &errstr);
+ if (ret == TRYRET_SUCCESS)
+ break;
+ if (ret != TRYRET_LOCALERR)
+ remoteerr = 1;
+ if ((opflags & ISBGRND) == 0)
+ fprintf(stderr, "%s\n", errstr);
+ }
+ if (ret == TRYRET_SUCCESS)
+ break;
+
+ /* Exit if all errors were local. */
+ if (!remoteerr)
+ exit(1);
+
+ /*
+ * If retrycnt == 0, we are to keep retrying forever.
+ * Otherwise decrement it, and exit if it hits zero.
+ */
+ if (retrycnt != 0 && --retrycnt == 0)
+ exit(1);
+
+ if ((opflags & (BGRND | ISBGRND)) == BGRND) {
+ warnx("Cannot immediately mount %s:%s, backgrounding",
+ hostp, spec);
+ opflags |= ISBGRND;
+ if (daemon(0, 0) != 0)
+ err(1, "daemon");
+ }
+ sleep(60);
+ }
+ freeaddrinfo(ai_nfs);
+ nfsargsp->hostname = nam;
+ /* Add mounted file system to PATH_MOUNTTAB */
+ if (!add_mtab(hostp, spec))
+ warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec);
+ return (1);
+}
+
+/*
+ * Try to set up the NFS arguments according to the address
+ * family, protocol (and possibly port) specified in `ai'.
+ *
+ * Returns TRYRET_SUCCESS if successful, or:
+ * TRYRET_TIMEOUT The server did not respond.
+ * TRYRET_REMOTEERR The server reported an error.
+ * TRYRET_LOCALERR Local failure.
+ *
+ * In all error cases, *errstr will be set to a statically-allocated string
+ * describing the error.
+ */
+enum tryret
+nfs_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai, char *hostp,
+ char *spec, char **errstr)
+{
+ static char errbuf[256];
+ struct sockaddr_storage nfs_ss;
+ struct netbuf nfs_nb;
+ struct nfhret nfhret;
+ struct timeval try;
+ struct rpc_err rpcerr;
+ CLIENT *clp;
+ struct netconfig *nconf, *nconf_mnt;
+ char *netid, *netid_mnt;
+ int doconnect, nfsvers, mntvers;
+ enum clnt_stat stat;
+ enum mountmode trymntmode;
+
+ trymntmode = mountmode;
+ errbuf[0] = '\0';
+ *errstr = errbuf;
+
+ if ((netid = netidbytype(ai->ai_family, nfsargsp->sotype)) == NULL) {
+ snprintf(errbuf, sizeof errbuf,
+ "af %d sotype %d not supported", ai->ai_family,
+ nfsargsp->sotype);
+ return (TRYRET_LOCALERR);
+ }
+ if ((nconf = getnetconf_cached(netid)) == NULL) {
+ snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror());
+ return (TRYRET_LOCALERR);
+ }
+ /* The RPCPROG_MNT netid may be different. */
+ if (mnttcp_ok) {
+ netid_mnt = netid;
+ nconf_mnt = nconf;
+ } else {
+ if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM))
+ == NULL) {
+ snprintf(errbuf, sizeof errbuf,
+ "af %d sotype SOCK_DGRAM not supported",
+ ai->ai_family);
+ return (TRYRET_LOCALERR);
+ }
+ if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) {
+ snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt,
+ nc_sperror());
+ return (TRYRET_LOCALERR);
+ }
+ }
+
+tryagain:
+ if (trymntmode == V2) {
+ nfsvers = 2;
+ mntvers = 1;
+ } else {
+ nfsvers = 3;
+ mntvers = 3;
+ }
+
+ if (portspec != NULL) {
+ /* `ai' contains the complete nfsd sockaddr. */
+ nfs_nb.buf = ai->ai_addr;
+ nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen;
+ } else {
+ /* Ask the remote rpcbind. */
+ nfs_nb.buf = &nfs_ss;
+ nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss;
+
+ if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb,
+ hostp)) {
+ if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH &&
+ trymntmode == ANY) {
+ trymntmode = V2;
+ goto tryagain;
+ }
+ snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s",
+ netid, hostp, spec,
+ clnt_spcreateerror("RPCPROG_NFS"));
+ return (returncode(rpc_createerr.cf_stat,
+ &rpc_createerr.cf_error));
+ }
+ }
+
+ /* Check that the server (nfsd) responds on the port we have chosen. */
+ clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, RPCPROG_NFS, nfsvers,
+ 0, 0);
+ if (clp == NULL) {
+ snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid,
+ hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS"));
+ return (returncode(rpc_createerr.cf_stat,
+ &rpc_createerr.cf_error));
+ }
+ if (nfsargsp->sotype == SOCK_DGRAM &&
+ !(nfsargsp->flags & NFSMNT_NOCONN)) {
+ /*
+ * Use connect(), to match what the kernel does. This
+ * catches cases where the server responds from the
+ * wrong source address.
+ */
+ doconnect = 1;
+ if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) {
+ clnt_destroy(clp);
+ snprintf(errbuf, sizeof errbuf,
+ "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp,
+ spec);
+ return (TRYRET_LOCALERR);
+ }
+ }
+
+ try.tv_sec = 10;
+ try.tv_usec = 0;
+ stat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL,
+ (xdrproc_t)xdr_void, NULL,
+ try);
+ if (stat != RPC_SUCCESS) {
+ if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
+ clnt_destroy(clp);
+ trymntmode = V2;
+ goto tryagain;
+ }
+ clnt_geterr(clp, &rpcerr);
+ snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid,
+ hostp, spec, clnt_sperror(clp, "NFSPROC_NULL"));
+ clnt_destroy(clp);
+ return (returncode(stat, &rpcerr));
+ }
+ clnt_destroy(clp);
+
+ /* Send the RPCMNT_MOUNT RPC to get the root filehandle. */
+ try.tv_sec = 10;
+ try.tv_usec = 0;
+ clp = clnt_tp_create(hostp, RPCPROG_MNT, mntvers, nconf_mnt);
+ if (clp == NULL) {
+ snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
+ hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create"));
+ return (returncode(rpc_createerr.cf_stat,
+ &rpc_createerr.cf_error));
+ }
+ clp->cl_auth = authsys_create_default();
+ nfhret.auth = RPCAUTH_UNIX;
+ nfhret.vers = mntvers;
+ stat = clnt_call(clp, RPCMNT_MOUNT, (xdrproc_t)xdr_dir, spec,
+ (xdrproc_t)xdr_fh, &nfhret,
+ try);
+ auth_destroy(clp->cl_auth);
+ if (stat != RPC_SUCCESS) {
+ if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
+ clnt_destroy(clp);
+ trymntmode = V2;
+ goto tryagain;
+ }
+ clnt_geterr(clp, &rpcerr);
+ snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
+ hostp, spec, clnt_sperror(clp, "RPCPROG_MNT"));
+ clnt_destroy(clp);
+ return (returncode(stat, &rpcerr));
+ }
+ clnt_destroy(clp);
+
+ if (nfhret.stat != 0) {
+ snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
+ hostp, spec, strerror(nfhret.stat));
+ return (TRYRET_REMOTEERR);
+ }
+
+ /*
+ * Store the filehandle and server address in nfsargsp, making
+ * sure to copy any locally allocated structures.
+ */
+ nfsargsp->addrlen = nfs_nb.len;
+ nfsargsp->addr = malloc(nfsargsp->addrlen);
+ nfsargsp->fhsize = nfhret.fhsize;
+ nfsargsp->fh = malloc(nfsargsp->fhsize);
+ if (nfsargsp->addr == NULL || nfsargsp->fh == NULL)
+ err(1, "malloc");
+ bcopy(nfs_nb.buf, nfsargsp->addr, nfsargsp->addrlen);
+ bcopy(nfhret.nfh, nfsargsp->fh, nfsargsp->fhsize);
+
+ if (nfsvers == 3)
+ nfsargsp->flags |= NFSMNT_NFSV3;
+ else
+ nfsargsp->flags &= ~NFSMNT_NFSV3;
+
+ return (TRYRET_SUCCESS);
+}
+
+
+/*
+ * Catagorise a RPC return status and error into an `enum tryret'
+ * return code.
+ */
+enum tryret
+returncode(enum clnt_stat stat, struct rpc_err *rpcerr)
+{
+ switch (stat) {
+ case RPC_TIMEDOUT:
+ return (TRYRET_TIMEOUT);
+ case RPC_PMAPFAILURE:
+ case RPC_PROGNOTREGISTERED:
+ case RPC_PROGVERSMISMATCH:
+ /* XXX, these can be local or remote. */
+ case RPC_CANTSEND:
+ case RPC_CANTRECV:
+ return (TRYRET_REMOTEERR);
+ case RPC_SYSTEMERROR:
+ switch (rpcerr->re_errno) {
+ case ETIMEDOUT:
+ return (TRYRET_TIMEOUT);
+ case ENOMEM:
+ break;
+ default:
+ return (TRYRET_REMOTEERR);
+ }
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ return (TRYRET_LOCALERR);
+}
+
+/*
+ * Look up a netid based on an address family and socket type.
+ * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM.
+ *
+ * XXX there should be a library function for this.
+ */
+char *
+netidbytype(int af, int sotype) {
+ struct nc_protos *p;
+
+ for (p = nc_protos; p->netid != NULL; p++) {
+ if (af != p->af || sotype != p->sotype)
+ continue;
+ return (p->netid);
+ }
+ return (NULL);
+}
+
+/*
+ * Look up a netconfig entry based on a netid, and cache the result so
+ * that we don't need to remember to call freenetconfigent().
+ *
+ * Otherwise it behaves just like getnetconfigent(), so nc_*error()
+ * work on failure.
+ */
+struct netconfig *
+getnetconf_cached(const char *netid) {
+ static struct nc_entry {
+ struct netconfig *nconf;
+ struct nc_entry *next;
+ } *head;
+ struct nc_entry *p;
+ struct netconfig *nconf;
+
+ for (p = head; p != NULL; p = p->next)
+ if (strcmp(netid, p->nconf->nc_netid) == 0)
+ return (p->nconf);
+
+ if ((nconf = getnetconfigent(netid)) == NULL)
+ return (NULL);
+ if ((p = malloc(sizeof(*p))) == NULL)
+ err(1, "malloc");
+ p->nconf = nconf;
+ p->next = head;
+ head = p;
+
+ return (p->nconf);
+}
+
+/*
+ * xdr routines for mount rpc's
+ */
+int
+xdr_dir(xdrsp, dirp)
+ XDR *xdrsp;
+ char *dirp;
+{
+ return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
+}
+
+int
+xdr_fh(xdrsp, np)
+ XDR *xdrsp;
+ struct nfhret *np;
+{
+ int i;
+ long auth, authcnt, authfnd = 0;
+
+ if (!xdr_u_long(xdrsp, &np->stat))
+ return (0);
+ if (np->stat)
+ return (1);
+ switch (np->vers) {
+ case 1:
+ np->fhsize = NFSX_V2FH;
+ return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH));
+ case 3:
+ if (!xdr_long(xdrsp, &np->fhsize))
+ return (0);
+ if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX)
+ return (0);
+ if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize))
+ return (0);
+ if (!xdr_long(xdrsp, &authcnt))
+ return (0);
+ for (i = 0; i < authcnt; i++) {
+ if (!xdr_long(xdrsp, &auth))
+ return (0);
+ if (auth == np->auth)
+ authfnd++;
+ }
+ /*
+ * Some servers, such as DEC's OSF/1 return a nil authenticator
+ * list to indicate RPCAUTH_UNIX.
+ */
+ if (!authfnd && (authcnt > 0 || np->auth != RPCAUTH_UNIX))
+ np->stat = EAUTH;
+ return (1);
+ };
+ return (0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
+"usage: mount_nfs [-23bcdiLlNPsTU] [-a maxreadahead] [-D deadthresh]",
+" [-g maxgroups] [-I readdirsize] [-o options] [-R retrycnt]",
+" [-r readsize] [-t timeout] [-w writesize] [-x retrans]",
+" rhost:path node");
+ exit(1);
+}
diff --git a/sbin/mount_nfs4/Makefile b/sbin/mount_nfs4/Makefile
new file mode 100644
index 0000000..881d119
--- /dev/null
+++ b/sbin/mount_nfs4/Makefile
@@ -0,0 +1,16 @@
+# ${.CURDIR}(#)Makefile 8.2 (Berkeley) 3/27/94
+#
+# $FreeBSD$
+
+PROG= mount_nfs4
+SRCS= mount_nfs4.c getmntopts.c mounttab.c
+MAN= mount_nfs4.8
+
+MOUNT= ${.CURDIR}/../mount
+UMNTALL= ${.CURDIR}/../../usr.sbin/rpc.umntall
+CFLAGS+= -DNFS -I${MOUNT} -I${UMNTALL}
+WARNS?= 2
+
+.PATH: ${MOUNT} ${UMNTALL}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_nfs4/mount_nfs4.8 b/sbin/mount_nfs4/mount_nfs4.8
new file mode 100644
index 0000000..8ef8245
--- /dev/null
+++ b/sbin/mount_nfs4/mount_nfs4.8
@@ -0,0 +1,201 @@
+.\" Copyright (c) 1992, 1993, 1994, 1995
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)mount_nfs.8 8.3 (Berkeley) 3/29/95
+.\" $FreeBSD$
+.\"
+.Dd November 14, 2003
+.Dt MOUNT_NFS4 8
+.Os
+.Sh NAME
+.Nm mount_nfs4
+.Nd mount NFSv4 file systems
+.Sh SYNOPSIS
+.Nm
+.Op Fl biNPsTU
+.Op Fl a Ar maxreadahead
+.Op Fl D Ar deadthresh
+.Op Fl I Ar readdirsize
+.Op Fl o Ar options
+.Op Fl R Ar retrycnt
+.Op Fl t Ar timeout
+.Op Fl x Ar retrans
+.Ar rhost : Ns Ar path node
+.Sh DESCRIPTION
+The
+.Nm
+utility calls the
+.Xr mount 2
+system call to prepare and graft a remote NFSv4 file system
+.Pq Ar rhost : Ns Ar path
+on to the file system tree at the point
+.Ar node .
+This command is normally executed by
+.Xr mount 8 .
+It implements the NFSv4 protocol as described in RFC 3530,
+.%T "NFS version 4 Protocol" .
+.Pp
+If the server becomes unresponsive while an NFSv4 file system is
+mounted, any new or outstanding file operations on that file system
+will hang uninterruptibly until the server comes back.
+To modify this default behaviour, see the
+.Fl i
+and
+.Fl s
+flags.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl D
+Set the
+.Dq "dead server threshold"
+to the specified number of round trip timeout intervals before a
+.Dq "server not responding"
+message is displayed.
+.It Fl I
+Set the readdir read size to the specified value.
+The value should normally
+be a multiple of
+.Dv DIRBLKSIZ
+that is \[<=] the read size for the mount.
+.It Fl N
+Do
+.Em not
+use a reserved socket port number (see below).
+.It Fl P
+Use a reserved socket port number.
+This flag is obsolete, and only retained for compatibility reasons.
+Reserved port numbers are used by default now.
+(For the rare case where the client has a trusted root account
+but untrustworthy users and the network cables are in secure areas this does
+help, but for normal desktop clients this does not apply.)
+.It Fl R
+Set the mount retry count to the specified value.
+The default is a retry count of zero, which means to keep retrying
+forever.
+There is a 60 second delay between each attempt.
+.It Fl T
+Use TCP transport.
+This is the default.
+.It Fl U
+Force the mount protocol to use UDP transport.
+This is not supported by the version 4 protocol and is provided only for
+debugging purposes.
+.It Fl a
+Set the read-ahead count to the specified value.
+This may be in the range of 0 - 4, and determines how many blocks
+will be read ahead when a large file is being read sequentially.
+Trying a value greater than 1 for this is suggested for
+mounts with a large bandwidth * delay product.
+.It Fl b
+If an initial attempt to contact the server fails, fork off a child to keep
+trying the mount in the background.
+Useful for
+.Xr fstab 5 ,
+where the file system mount is not critical to multiuser operation.
+.It Fl i
+Make the mount interruptible, which implies that file system calls that
+are delayed due to an unresponsive server will fail with
+.Er EINTR
+when a
+termination signal is posted for the process.
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+The following NFS specific options are also available:
+.Pp
+.Bl -tag -width indent -compact
+.It Cm port Ns = Ns Aq Ar port_number
+Use specified port number for NFSv4 requests.
+The default is to use port 2049.
+Set this to 0 to query the portmapper for the NFSv4 port.
+.Pp
+.It Cm acregmin Ns = Ns Aq Ar seconds
+.It Cm acregmax Ns = Ns Aq Ar seconds
+.It Cm acdirmin Ns = Ns Aq Ar seconds
+.It Cm acdirmax Ns = Ns Aq Ar seconds
+When attributes of files are cached, a timeout calculated to determine
+whether a given cache entry has expired.
+These four values determine the upper and lower bounds of the timeouts for
+.Dq directory
+attributes and
+.Dq regular
+(i.e., everything else).
+The default values are 3 -> 60 seconds
+for regular files, and 30 -> 60 seconds for directories.
+The algorithm to calculate the timeout is based on the age of the file.
+The older the file,
+the longer the cache is considered valid, subject to the limits above.
+.Pp
+.It Cm noinet4 , noinet6
+Disables
+.Dv AF_INET
+or
+.Dv AF_INET6
+connections.
+Useful for hosts that have
+both an A record and an AAAA record for the same name.
+.El
+.It Fl s
+A soft mount, which implies that file system calls will fail
+after
+.Ar retrycnt
+round trip timeout intervals.
+.It Fl t
+Set the initial retransmit timeout to the specified value.
+May be useful for fine tuning UDP mounts over internetworks
+with high packet loss rates or an overloaded server.
+Try increasing the interval if
+.Xr nfsstat 1
+shows high retransmit rates while the file system is active or reducing the
+value if there is a low retransmit rate but long response delay observed.
+(Normally, the
+.Fl d
+option should be specified when using this option to manually
+tune the timeout
+interval.)
+.It Fl x
+Set the retransmit timeout count for soft mounts to the specified value.
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr idmapd 8 ,
+.Xr mount 8 ,
+.Xr mount_nfs 8 ,
+.Xr nfsd 8 ,
+.Xr nfsiod 8
+.Sh BUGS
+This version of the NFSv4 client, while functional, is a long way
+from compliance with RFC 3530.
+It lacks lock state, reboot recovery,
+delegation, GSS, and many other mandatory items from the RFC.
diff --git a/sbin/mount_nfs4/mount_nfs4.c b/sbin/mount_nfs4/mount_nfs4.c
new file mode 100644
index 0000000..94d0d93
--- /dev/null
+++ b/sbin/mount_nfs4/mount_nfs4.c
@@ -0,0 +1,735 @@
+/* $Id: mount_nfs.c,v 1.5 2003/11/05 14:58:58 rees Exp $ */
+
+/*
+ * copyright (c) 2003
+ * the regents of the university of michigan
+ * all rights reserved
+ *
+ * permission is granted to use, copy, create derivative works and redistribute
+ * this software and such derivative works for any purpose, so long as the name
+ * of the university of michigan is not used in any advertising or publicity
+ * pertaining to the use or distribution of this software without specific,
+ * written prior authorization. if the above copyright notice or any other
+ * identification of the university of michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must also be included.
+ *
+ * this software is provided as is, without representation from the university
+ * of michigan as to its fitness for any purpose, and without warranty by the
+ * university of michigan of any kind, either express or implied, including
+ * without limitation the implied warranties of merchantability and fitness for
+ * a particular purpose. the regents of the university of michigan shall not be
+ * liable for any damages, including special, indirect, incidental, or
+ * consequential damages, with respect to any claim arising out of or in
+ * connection with the use of the software, even if it has been or is hereafter
+ * advised of the possibility of such damages.
+ */
+
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfsclient/nfs.h>
+#include <nfsclient/nfsargs.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+#include "mounttab.h"
+
+#define ALTF_BG 0x1
+#define ALTF_NOCONN 0x2
+#define ALTF_DUMBTIMR 0x4
+#define ALTF_INTR 0x8
+#define ALTF_NFSV3 0x20
+#define ALTF_RDIRPLUS 0x40
+#define ALTF_MNTUDP 0x80
+#define ALTF_RESVPORT 0x100
+#define ALTF_SEQPACKET 0x200
+#define ALTF_SOFT 0x800
+#define ALTF_TCP 0x1000
+#define ALTF_PORT 0x2000
+#define ALTF_NFSV2 0x4000
+#define ALTF_ACREGMIN 0x8000
+#define ALTF_ACREGMAX 0x10000
+#define ALTF_ACDIRMIN 0x20000
+#define ALTF_ACDIRMAX 0x40000
+#define ALTF_NOLOCKD 0x80000
+#define ALTF_NOINET4 0x100000
+#define ALTF_NOINET6 0x200000
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_FORCE,
+ MOPT_UPDATE,
+ MOPT_ASYNC,
+ { "bg", 0, ALTF_BG, 1 },
+ { "conn", 1, ALTF_NOCONN, 1 },
+ { "dumbtimer", 0, ALTF_DUMBTIMR, 1 },
+ { "intr", 0, ALTF_INTR, 1 },
+ { "nfsv3", 0, ALTF_NFSV3, 1 },
+ { "rdirplus", 0, ALTF_RDIRPLUS, 1 },
+ { "mntudp", 0, ALTF_MNTUDP, 1 },
+ { "resvport", 0, ALTF_RESVPORT, 1 },
+ { "soft", 0, ALTF_SOFT, 1 },
+ { "tcp", 0, ALTF_TCP, 1 },
+ { "port=", 0, ALTF_PORT, 1 },
+ { "nfsv2", 0, ALTF_NFSV2, 1 },
+ { "acregmin=", 0, ALTF_ACREGMIN, 1 },
+ { "acregmax=", 0, ALTF_ACREGMAX, 1 },
+ { "acdirmin=", 0, ALTF_ACDIRMIN, 1 },
+ { "acdirmax=", 0, ALTF_ACDIRMAX, 1 },
+ { "lockd", 1, ALTF_NOLOCKD, 1 },
+ { "inet4", 1, ALTF_NOINET4, 1 },
+ { "inet6", 1, ALTF_NOINET6, 1 },
+ MOPT_END
+};
+
+struct nfs_args nfsdefargs = {
+ NFS_ARGSVERSION,
+ NULL,
+ sizeof (struct sockaddr_in),
+ SOCK_STREAM,
+ 0,
+ NULL,
+ 0,
+ NFSMNT_RESVPORT,
+ NFS_WSIZE,
+ NFS_RSIZE,
+ NFS_READDIRSIZE,
+ 10,
+ NFS_RETRANS,
+ NFS_MAXGRPS,
+ NFS_DEFRAHEAD,
+ 0, /* was: NQ_DEFLEASE */
+ NFS_MAXDEADTHRESH, /* was: NQ_DEADTHRESH */
+ NULL,
+ /* args version 4 */
+ NFS_MINATTRTIMO,
+ NFS_MAXATTRTIMO,
+ NFS_MINDIRATTRTIMO,
+ NFS_MAXDIRATTRTIMO,
+};
+
+/* Table for af,sotype -> netid conversions. */
+struct nc_protos {
+ char *netid;
+ int af;
+ int sotype;
+} nc_protos[] = {
+ {"udp", AF_INET, SOCK_DGRAM},
+ {"tcp", AF_INET, SOCK_STREAM},
+ {"udp6", AF_INET6, SOCK_DGRAM},
+ {"tcp6", AF_INET6, SOCK_STREAM},
+ {NULL}
+};
+
+struct nfhret {
+ u_long stat;
+ long vers;
+ long auth;
+ long fhsize;
+ u_char nfh[NFSX_V3FHMAX];
+};
+#define BGRND 1
+#define ISBGRND 2
+#define OF_NOINET4 4
+#define OF_NOINET6 8
+int retrycnt = -1;
+int opflags = 0;
+int nfsproto = IPPROTO_TCP;
+char *portspec = "2049"; /* Server nfs port; "0" means look up via rpcbind. */
+enum mountmode {
+ ANY,
+ V2,
+ V3
+} mountmode = ANY;
+
+/* Return codes for nfs_tryproto. */
+enum tryret {
+ TRYRET_SUCCESS,
+ TRYRET_TIMEOUT, /* No response received. */
+ TRYRET_REMOTEERR, /* Error received from remote server. */
+ TRYRET_LOCALERR /* Local failure. */
+};
+
+int getnfsargs(char *, struct nfs_args *);
+/* void set_rpc_maxgrouplist(int); */
+struct netconfig *getnetconf_cached(const char *netid);
+char *netidbytype(int af, int sotype);
+void usage(void) __dead2;
+int xdr_dir(XDR *, char *);
+int xdr_fh(XDR *, struct nfhret *);
+enum tryret nfs_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai,
+ char *hostp, char *spec, char **errstr);
+enum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr);
+
+/*
+ * Used to set mount flags with getmntopts. Call with dir=TRUE to
+ * initialize altflags from the current mount flags. Call with
+ * dir=FALSE to update mount flags with the new value of altflags after
+ * the call to getmntopts.
+ */
+static void
+set_flags(int* altflags, int* nfsflags, int dir)
+{
+#define F2(af, nf) \
+ if (dir) { \
+ if (*nfsflags & NFSMNT_##nf) \
+ *altflags |= ALTF_##af; \
+ else \
+ *altflags &= ~ALTF_##af; \
+ } else { \
+ if (*altflags & ALTF_##af) \
+ *nfsflags |= NFSMNT_##nf; \
+ else \
+ *nfsflags &= ~NFSMNT_##nf; \
+ }
+#define F(f) F2(f,f)
+
+ F(NOCONN);
+ F(DUMBTIMR);
+ F2(INTR, INT);
+ F(RDIRPLUS);
+ F(RESVPORT);
+ F(SOFT);
+ F(ACREGMIN);
+ F(ACREGMAX);
+ F(ACDIRMIN);
+ F(ACDIRMAX);
+ F(NOLOCKD);
+
+#undef F
+#undef F2
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ struct nfs_args *nfsargsp;
+ struct nfs_args nfsargs;
+ int mntflags, altflags, num;
+ char *name, *p, *spec;
+ char mntpath[MAXPATHLEN];
+
+ mntflags = 0;
+ altflags = 0;
+ nfsargs = nfsdefargs;
+ nfsargsp = &nfsargs;
+ while ((c = getopt(argc, argv,
+ "a:bD:I:iNo:PR:sTt:x:U")) != -1)
+ switch (c) {
+ case 'a':
+ num = strtol(optarg, &p, 10);
+ if (*p || num < 0)
+ errx(1, "illegal -a value -- %s", optarg);
+ nfsargsp->readahead = num;
+ nfsargsp->flags |= NFSMNT_READAHEAD;
+ break;
+ case 'b':
+ opflags |= BGRND;
+ break;
+ case 'D':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -D value -- %s", optarg);
+ nfsargsp->deadthresh = num;
+ nfsargsp->flags |= NFSMNT_DEADTHRESH;
+ break;
+ case 'I':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -I value -- %s", optarg);
+ nfsargsp->readdirsize = num;
+ nfsargsp->flags |= NFSMNT_READDIRSIZE;
+ break;
+ case 'i':
+ nfsargsp->flags |= NFSMNT_INT;
+ break;
+ case 'N':
+ nfsargsp->flags &= ~NFSMNT_RESVPORT;
+ break;
+ case 'o':
+ altflags = 0;
+ set_flags(&altflags, &nfsargsp->flags, TRUE);
+ if (mountmode == V2)
+ altflags |= ALTF_NFSV2;
+ else if (mountmode == V3)
+ altflags |= ALTF_NFSV3;
+ getmntopts(optarg, mopts, &mntflags, &altflags);
+ set_flags(&altflags, &nfsargsp->flags, FALSE);
+ /*
+ * Handle altflags which don't map directly to
+ * mount flags.
+ */
+ if (altflags & ALTF_BG)
+ opflags |= BGRND;
+ if (altflags & ALTF_NOINET4)
+ opflags |= OF_NOINET4;
+ if (altflags & ALTF_NOINET6)
+ opflags |= OF_NOINET6;
+ if (altflags & ALTF_MNTUDP) {
+ nfsargsp->sotype = SOCK_DGRAM;
+ nfsproto = IPPROTO_UDP;
+ }
+ if (altflags & ALTF_PORT) {
+ /*
+ * XXX Converting from a string to an int
+ * and back again is silly, and we should
+ * allow /etc/services names.
+ */
+ p = strstr(optarg, "port=");
+ if (p) {
+ asprintf(&portspec, "%d",
+ atoi(p + 5));
+ if (portspec == NULL)
+ err(1, "asprintf");
+ }
+ }
+ mountmode = ANY;
+ if (altflags & ALTF_NFSV2)
+ mountmode = V2;
+ if (altflags & ALTF_NFSV3)
+ mountmode = V3;
+ if (altflags & ALTF_ACREGMIN) {
+ p = strstr(optarg, "acregmin=");
+ if (p)
+ nfsargsp->acregmin = atoi(p + 9);
+ }
+ if (altflags & ALTF_ACREGMAX) {
+ p = strstr(optarg, "acregmax=");
+ if (p)
+ nfsargsp->acregmax = atoi(p + 9);
+ }
+ if (altflags & ALTF_ACDIRMIN) {
+ p = strstr(optarg, "acdirmin=");
+ if (p)
+ nfsargsp->acdirmin = atoi(p + 9);
+ }
+ if (altflags & ALTF_ACDIRMAX) {
+ p = strstr(optarg, "acdirmax=");
+ if (p)
+ nfsargsp->acdirmax = atoi(p + 9);
+ }
+ break;
+ case 'P':
+ /* obsolete for NFSMNT_RESVPORT, now default */
+ break;
+ case 'R':
+ num = strtol(optarg, &p, 10);
+ if (*p || num < 0)
+ errx(1, "illegal -R value -- %s", optarg);
+ retrycnt = num;
+ break;
+ case 's':
+ nfsargsp->flags |= NFSMNT_SOFT;
+ break;
+ case 'T':
+ nfsargsp->sotype = SOCK_STREAM;
+ nfsproto = IPPROTO_TCP;
+ break;
+ case 't':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -t value -- %s", optarg);
+ nfsargsp->timeo = num;
+ nfsargsp->flags |= NFSMNT_TIMEO;
+ break;
+ case 'x':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -x value -- %s", optarg);
+ nfsargsp->retrans = num;
+ nfsargsp->flags |= NFSMNT_RETRANS;
+ break;
+ case 'U':
+ nfsargsp->sotype = SOCK_DGRAM;
+ nfsproto = IPPROTO_UDP;
+ break;
+ default:
+ usage();
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2) {
+ usage();
+ /* NOTREACHED */
+ }
+
+ spec = *argv++;
+ name = *argv;
+
+ if (retrycnt == -1)
+ /* The default is to keep retrying forever. */
+ retrycnt = 0;
+ if (!getnfsargs(spec, nfsargsp))
+ exit(1);
+
+ /* resolve the mountpoint with realpath(3) */
+ (void)checkpath(name, mntpath);
+
+ if (mount("nfs4", mntpath, mntflags, nfsargsp))
+ err(1, "%s", mntpath);
+
+ exit(0);
+}
+
+int
+getnfsargs(spec, nfsargsp)
+ char *spec;
+ struct nfs_args *nfsargsp;
+{
+ struct addrinfo hints, *ai_nfs, *ai;
+ enum tryret ret;
+ int ecode, speclen, remoteerr;
+ char *hostp, *delimp, *errstr;
+ size_t len;
+ static char nam[MNAMELEN + 1];
+
+ if ((delimp = strrchr(spec, ':')) != NULL) {
+ hostp = spec;
+ spec = delimp + 1;
+ } else if ((delimp = strrchr(spec, '@')) != NULL) {
+ warnx("path@server syntax is deprecated, use server:path");
+ hostp = delimp + 1;
+ } else {
+ warnx("no <host>:<dirpath> nfs-name");
+ return (0);
+ }
+ *delimp = '\0';
+
+ /*
+ * If there has been a trailing slash at mounttime it seems
+ * that some mountd implementations fail to remove the mount
+ * entries from their mountlist while unmounting.
+ */
+ for (speclen = strlen(spec);
+ speclen > 1 && spec[speclen - 1] == '/';
+ speclen--)
+ spec[speclen - 1] = '\0';
+ if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) {
+ warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG));
+ return (0);
+ }
+ /* Make both '@' and ':' notations equal */
+ if (*hostp != '\0') {
+ len = strlen(hostp);
+ memmove(nam, hostp, len);
+ nam[len] = ':';
+ memmove(nam + len + 1, spec, speclen);
+ nam[len + speclen + 1] = '\0';
+ }
+
+ /*
+ * Handle an internet host address.
+ */
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_NUMERICHOST;
+ hints.ai_socktype = nfsargsp->sotype;
+ if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) {
+ hints.ai_flags = 0;
+ if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs))
+ != 0) {
+ if (portspec == NULL)
+ errx(1, "%s: %s", hostp, gai_strerror(ecode));
+ else
+ errx(1, "%s:%s: %s", hostp, portspec,
+ gai_strerror(ecode));
+ return (0);
+ }
+ }
+
+ ret = TRYRET_LOCALERR;
+ for (;;) {
+ /*
+ * Try each entry returned by getaddrinfo(). Note the
+ * occurence of remote errors by setting `remoteerr'.
+ */
+ remoteerr = 0;
+ for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) {
+ if ((ai->ai_family == AF_INET6) &&
+ (opflags & OF_NOINET6))
+ continue;
+ if ((ai->ai_family == AF_INET) &&
+ (opflags & OF_NOINET4))
+ continue;
+ ret = nfs_tryproto(nfsargsp, ai, hostp, spec, &errstr);
+ if (ret == TRYRET_SUCCESS)
+ break;
+ if (ret != TRYRET_LOCALERR)
+ remoteerr = 1;
+ if ((opflags & ISBGRND) == 0)
+ fprintf(stderr, "%s\n", errstr);
+ }
+ if (ret == TRYRET_SUCCESS)
+ break;
+
+ /* Exit if all errors were local. */
+ if (!remoteerr)
+ exit(1);
+
+ /*
+ * If retrycnt == 0, we are to keep retrying forever.
+ * Otherwise decrement it, and exit if it hits zero.
+ */
+ if (retrycnt != 0 && --retrycnt == 0)
+ exit(1);
+
+ if ((opflags & (BGRND | ISBGRND)) == BGRND) {
+ warnx("Cannot immediately mount %s:%s, backgrounding",
+ hostp, spec);
+ opflags |= ISBGRND;
+ if (daemon(0, 0) != 0)
+ err(1, "daemon");
+ }
+ sleep(60);
+ }
+ freeaddrinfo(ai_nfs);
+ nfsargsp->hostname = nam;
+ /* Add mounted file system to PATH_MOUNTTAB */
+ if (!add_mtab(hostp, spec))
+ warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec);
+ return (1);
+}
+
+/*
+ * Try to set up the NFS arguments according to the address
+ * family, protocol (and possibly port) specified in `ai'.
+ *
+ * Returns TRYRET_SUCCESS if successful, or:
+ * TRYRET_TIMEOUT The server did not respond.
+ * TRYRET_REMOTEERR The server reported an error.
+ * TRYRET_LOCALERR Local failure.
+ *
+ * In all error cases, *errstr will be set to a statically-allocated string
+ * describing the error.
+ */
+enum tryret
+nfs_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai, char *hostp,
+ char *spec, char **errstr)
+{
+ static char errbuf[256];
+ struct sockaddr_storage nfs_ss;
+ struct netbuf nfs_nb;
+ struct netconfig *nconf;
+ char *netid;
+ int nfsvers;
+
+ errbuf[0] = '\0';
+ *errstr = errbuf;
+
+ if ((netid = netidbytype(ai->ai_family, nfsargsp->sotype)) == NULL) {
+ snprintf(errbuf, sizeof errbuf,
+ "af %d sotype %d not supported", ai->ai_family,
+ nfsargsp->sotype);
+ return (TRYRET_LOCALERR);
+ }
+ if ((nconf = getnetconf_cached(netid)) == NULL) {
+ snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror());
+ return (TRYRET_LOCALERR);
+ }
+
+ nfsvers = 4;
+
+ if (portspec != NULL && atoi(portspec) != 0) {
+ /* `ai' contains the complete nfsd sockaddr. */
+ nfs_nb.buf = ai->ai_addr;
+ nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen;
+ } else {
+ /* Ask the remote rpcbind. */
+ nfs_nb.buf = &nfs_ss;
+ nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss;
+
+ if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb,
+ hostp)) {
+ snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s",
+ netid, hostp, spec,
+ clnt_spcreateerror("RPCPROG_NFS"));
+ return (returncode(rpc_createerr.cf_stat,
+ &rpc_createerr.cf_error));
+ }
+ }
+
+ /*
+ * Store the filehandle and server address in nfsargsp, making
+ * sure to copy any locally allocated structures.
+ */
+ nfsargsp->addrlen = nfs_nb.len;
+ nfsargsp->addr = malloc(nfsargsp->addrlen);
+
+ if (nfsargsp->addr == NULL)
+ err(1, "malloc");
+ bcopy(nfs_nb.buf, nfsargsp->addr, nfsargsp->addrlen);
+
+ /* XXX hack */
+ nfsargsp->flags |= (NFSMNT_NFSV3 | NFSMNT_NFSV4);
+
+ return (TRYRET_SUCCESS);
+}
+
+
+/*
+ * Catagorise a RPC return status and error into an `enum tryret'
+ * return code.
+ */
+enum tryret
+returncode(enum clnt_stat stat, struct rpc_err *rpcerr)
+{
+ switch (stat) {
+ case RPC_TIMEDOUT:
+ return (TRYRET_TIMEOUT);
+ case RPC_PMAPFAILURE:
+ case RPC_PROGNOTREGISTERED:
+ case RPC_PROGVERSMISMATCH:
+ /* XXX, these can be local or remote. */
+ case RPC_CANTSEND:
+ case RPC_CANTRECV:
+ return (TRYRET_REMOTEERR);
+ case RPC_SYSTEMERROR:
+ switch (rpcerr->re_errno) {
+ case ETIMEDOUT:
+ return (TRYRET_TIMEOUT);
+ case ENOMEM:
+ break;
+ default:
+ return (TRYRET_REMOTEERR);
+ }
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ return (TRYRET_LOCALERR);
+}
+
+/*
+ * Look up a netid based on an address family and socket type.
+ * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM.
+ *
+ * XXX there should be a library function for this.
+ */
+char *
+netidbytype(int af, int sotype) {
+ struct nc_protos *p;
+
+ for (p = nc_protos; p->netid != NULL; p++) {
+ if (af != p->af || sotype != p->sotype)
+ continue;
+ return (p->netid);
+ }
+ return (NULL);
+}
+
+/*
+ * Look up a netconfig entry based on a netid, and cache the result so
+ * that we don't need to remember to call freenetconfigent().
+ *
+ * Otherwise it behaves just like getnetconfigent(), so nc_*error()
+ * work on failure.
+ */
+struct netconfig *
+getnetconf_cached(const char *netid) {
+ static struct nc_entry {
+ struct netconfig *nconf;
+ struct nc_entry *next;
+ } *head;
+ struct nc_entry *p;
+ struct netconfig *nconf;
+
+ for (p = head; p != NULL; p = p->next)
+ if (strcmp(netid, p->nconf->nc_netid) == 0)
+ return (p->nconf);
+
+ if ((nconf = getnetconfigent(netid)) == NULL)
+ return (NULL);
+ if ((p = malloc(sizeof(*p))) == NULL)
+ err(1, "malloc");
+ p->nconf = nconf;
+ p->next = head;
+ head = p;
+
+ return (p->nconf);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "%s\n%s\n%s\n",
+"usage: mount_nfs4 [-biNPsTU] [-a maxreadahead] [-D deadthresh] [-I readdirsize]",
+" [-o options] [-R retrycnt] [-t timeout] [-x retrans]",
+" rhost:path node");
+ exit(1);
+}
diff --git a/sbin/mount_ntfs/Makefile b/sbin/mount_ntfs/Makefile
new file mode 100644
index 0000000..5938b0d
--- /dev/null
+++ b/sbin/mount_ntfs/Makefile
@@ -0,0 +1,21 @@
+#
+# $FreeBSD$
+#
+
+PROG= mount_ntfs
+SRCS= mount_ntfs.c getmntopts.c
+MAN= mount_ntfs.8
+DPADD= ${LIBKICONV}
+LDADD= -lkiconv
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+=-I${MOUNT}
+WARNS?= 0
+
+# Needs to be dynamically linked for optional dlopen() access to
+# userland libiconv
+NO_SHARED?= NO
+
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_ntfs/mount_ntfs.8 b/sbin/mount_ntfs/mount_ntfs.8
new file mode 100644
index 0000000..15f49df
--- /dev/null
+++ b/sbin/mount_ntfs/mount_ntfs.8
@@ -0,0 +1,167 @@
+.\"
+.\" Copyright (c) 1993,1994 Christopher G. Demetriou
+.\" Copyright (c) 1999 Semen Ustimenko
+.\" 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 Christopher G. Demetriou.
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 3, 1999
+.Dt MOUNT_NTFS 8
+.Os
+.Sh NAME
+.Nm mount_ntfs
+.Nd mount an NTFS file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl i
+.Op Fl u Ar user
+.Op Fl g Ar group
+.Op Fl m Ar mask
+.Op Fl C Ar charset
+.Op Fl W Ar u2wtable
+.Pa special
+.Pa node
+.Sh DESCRIPTION
+The
+.Nm
+utility attaches the NTFS file system residing on the device
+.Pa special
+to the global file system namespace at the location
+indicated by
+.Pa node .
+This command is normally executed by
+.Xr mount 8
+at boot time, but can be used by any user to mount an
+NTFS file system on any directory that they own (provided,
+of course, that they have appropriate access to the device that
+contains the file system).
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Force behaviour to return MS-DOS 8.3 names also on
+.Fn readdir .
+.It Fl i
+Make name lookup case insensitive for all names except POSIX names.
+.It Fl u Ar user
+Set the owner of the files in the file system to
+.Ar user .
+The default owner is the owner of the directory
+on which the file system is being mounted.
+.It Fl g Ar group
+Set the group of the files in the file system to
+.Ar group .
+The default group is the group of the directory
+on which the file system is being mounted.
+.It Fl m Ar mask
+Specify the maximum file permissions for files
+in the file system.
+.It Fl C Ar charset
+Specify local
+.Ar charset
+to convert Unicode file names.
+Currently only reading is supported, thus the file system is to be
+mounted read-only.
+.It Fl W Ar u2wtable
+Specify
+.Ux
+to
+.Tn Unicode
+translation table.
+See
+.Xr mount_msdosfs 8
+for the description of this option.
+.Bf Em
+This option is preserved for backward compatibility purpose only,
+and will be removed in the future.
+Please do not use this option.
+.Ef
+.El
+.Sh FEATURES
+NTFS file attributes are accessed in following way:
+.Bd -literal -offset indent
+foo[[:ATTRTYPE]:ATTRNAME]
+.Ed
+.Pp
+.Sq ATTRTYPE
+is one of the identifiers listed in $AttrDef file of
+volume.
+Default is $DATA.
+.Sq ATTRNAME
+is an attribute name.
+Default is none.
+.Sh EXAMPLES
+To mount an NTFS volume located in
+.Pa /dev/ad1s1 :
+.Pp
+.Dl "mount_ntfs /dev/ad1s1 /mnt"
+.Pp
+To get the volume name (in Unicode):
+.Pp
+.Dl "cat /mnt/\e$Volume:\e$VOLUME_NAME"
+.Pp
+To read directory raw data:
+.Pp
+.Dl "cat /mnt/foodir:\e$INDEX_ROOT:\e$I30"
+.Pp
+To mount a Japanese NTFS volume located in
+.Pa /dev/ad0s1 :
+.Pp
+.Dl "mount_ntfs -C eucJP /dev/ad0s1 /mnt"
+.Sh WRITING
+There is limited writing ability.
+Limitations: file must be nonresident
+and must not contain any sparces (uninitialized areas); compressed
+files are also not supported.
+The file name must not contain multibyte characters.
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8 ,
+.Xr mount_msdosfs 8
+.Sh CAVEATS
+This utility is primarily used for read access to an NTFS volume.
+See the
+.Sx WRITING
+section for details about writing to an NTFS volume.
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 3.0 .
+.Pp
+The Unicode conversion routine was added by
+.An Ryuichiro Imura Aq imura@ryu16.org
+in 2003.
+.Sh AUTHORS
+The NTFS kernel implementation,
+.Nm
+utility, and manual were written by
+.An Semen Ustimenko Aq semenu@FreeBSD.org .
diff --git a/sbin/mount_ntfs/mount_ntfs.c b/sbin/mount_ntfs/mount_ntfs.c
new file mode 100644
index 0000000..4bd118f
--- /dev/null
+++ b/sbin/mount_ntfs/mount_ntfs.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou
+ * Copyright (c) 1999 Semen Ustimenko
+ * 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 Christopher G. Demetriou.
+ * 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.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#define NTFS
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/module.h>
+#include <sys/iconv.h>
+#include <sys/linker.h>
+#include <fs/ntfs/ntfsmount.h>
+#include <ctype.h>
+#include <err.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <libutil.h>
+
+#include "mntopts.h"
+
+#define TRANSITION_PERIOD_HACK
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_END
+};
+
+static gid_t a_gid(char *);
+static uid_t a_uid(char *);
+static mode_t a_mask(char *);
+static void usage(void) __dead2;
+
+static int set_charset(struct ntfs_args *);
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct ntfs_args args;
+ struct stat sb;
+ int c, mntflags, set_gid, set_uid, set_mask;
+ char *dev, *dir, mntpath[MAXPATHLEN];
+
+ mntflags = set_gid = set_uid = set_mask = 0;
+ (void)memset(&args, '\0', sizeof(args));
+ args.cs_ntfs = NULL;
+ args.cs_local = NULL;
+
+#ifdef TRANSITION_PERIOD_HACK
+ while ((c = getopt(argc, argv, "aiu:g:m:o:C:W:")) != -1) {
+#else
+ while ((c = getopt(argc, argv, "aiu:g:m:o:C:")) != -1) {
+#endif
+ switch (c) {
+ case 'u':
+ args.uid = a_uid(optarg);
+ set_uid = 1;
+ break;
+ case 'g':
+ args.gid = a_gid(optarg);
+ set_gid = 1;
+ break;
+ case 'm':
+ args.mode = a_mask(optarg);
+ set_mask = 1;
+ break;
+ case 'i':
+ args.flag |= NTFS_MFLAG_CASEINS;
+ break;
+ case 'a':
+ args.flag |= NTFS_MFLAG_ALLNAMES;
+ break;
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case 'C':
+ args.cs_local = malloc(ICONV_CSNMAXLEN);
+ if (args.cs_local == NULL)
+ err(EX_OSERR, "malloc()");
+ strncpy(args.cs_local,
+ kiconv_quirkcs(optarg, KICONV_VENDOR_MICSFT),
+ ICONV_CSNMAXLEN);
+ break;
+#ifdef TRANSITION_PERIOD_HACK
+ case 'W':
+ args.cs_local = malloc(ICONV_CSNMAXLEN);
+ if (args.cs_local == NULL)
+ err(EX_OSERR, "malloc()");
+ if (strcmp(optarg, "iso22dos") == 0) {
+ strcpy(args.cs_local, "ISO8859-2");
+ } else if (strcmp(optarg, "iso72dos") == 0) {
+ strcpy(args.cs_local, "ISO8859-7");
+ } else if (strcmp(optarg, "koi2dos") == 0) {
+ strcpy(args.cs_local, "KOI8-R");
+ } else if (strcmp(optarg, "koi8u2dos") == 0) {
+ strcpy(args.cs_local, "KOI8-U");
+ } else {
+ err(EX_NOINPUT, "%s", optarg);
+ }
+ break;
+#endif /* TRANSITION_PERIOD_HACK */
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (optind + 2 != argc)
+ usage();
+
+ dev = argv[optind];
+ dir = argv[optind + 1];
+
+ if (args.cs_local) {
+ if (set_charset(&args) == -1)
+ err(EX_OSERR, "ntfs_iconv");
+ args.flag |= NTFS_MFLAG_KICONV;
+ /*
+ * XXX
+ * Force to be MNT_RDONLY,
+ * since only reading is supported right now,
+ */
+ mntflags |= MNT_RDONLY;
+ }
+
+ /*
+ * Resolve the mountpoint with realpath(3) and remove unnecessary
+ * slashes from the devicename if there are any.
+ */
+ (void)checkpath(dir, mntpath);
+ (void)rmslashes(dev, dev);
+
+ args.fspec = dev;
+ args.export.ex_root = 65534; /* unchecked anyway on DOS fs */
+ if (mntflags & MNT_RDONLY)
+ args.export.ex_flags = MNT_EXRDONLY;
+ else
+ args.export.ex_flags = 0;
+ if (!set_gid || !set_uid || !set_mask) {
+ if (stat(mntpath, &sb) == -1)
+ err(EX_OSERR, "stat %s", mntpath);
+
+ if (!set_uid)
+ args.uid = sb.st_uid;
+ if (!set_gid)
+ args.gid = sb.st_gid;
+ if (!set_mask)
+ args.mode = sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ }
+
+ if (mount("ntfs", mntpath, mntflags, &args) < 0)
+ err(EX_OSERR, "%s", dev);
+
+ exit (0);
+}
+
+gid_t
+a_gid(s)
+ char *s;
+{
+ struct group *gr;
+ char *gname;
+ gid_t gid;
+
+ if ((gr = getgrnam(s)) != NULL)
+ gid = gr->gr_gid;
+ else {
+ for (gname = s; *s && isdigit(*s); ++s);
+ if (!*s)
+ gid = atoi(gname);
+ else
+ errx(EX_NOUSER, "unknown group id: %s", gname);
+ }
+ return (gid);
+}
+
+uid_t
+a_uid(s)
+ char *s;
+{
+ struct passwd *pw;
+ char *uname;
+ uid_t uid;
+
+ if ((pw = getpwnam(s)) != NULL)
+ uid = pw->pw_uid;
+ else {
+ for (uname = s; *s && isdigit(*s); ++s);
+ if (!*s)
+ uid = atoi(uname);
+ else
+ errx(EX_NOUSER, "unknown user id: %s", uname);
+ }
+ return (uid);
+}
+
+mode_t
+a_mask(s)
+ char *s;
+{
+ int done, rv=0;
+ char *ep;
+
+ done = 0;
+ if (*s >= '0' && *s <= '7') {
+ done = 1;
+ rv = strtol(optarg, &ep, 8);
+ }
+ if (!done || rv < 0 || *ep)
+ errx(EX_USAGE, "invalid file mode: %s", s);
+ return (rv);
+}
+
+void
+usage()
+{
+#ifdef TRANSITION_PERIOD_HACK
+ fprintf(stderr, "%s\n%s\n",
+ "usage: mount_ntfs [-a] [-i] [-u user] [-g group] [-m mask]",
+ " [-C charset] [-W u2wtable] special node");
+#else
+ fprintf(stderr, "usage: mount_ntfs [-a] [-i] [-u user] [-g group] [-m mask] [-C charset] special node\n");
+#endif
+ exit(EX_USAGE);
+}
+
+int
+set_charset(struct ntfs_args *pargs)
+{
+ int error;
+
+ if (modfind("ntfs_iconv") < 0)
+ if (kldload("ntfs_iconv") < 0 || modfind("ntfs_iconv") < 0) {
+ warnx( "cannot find or load \"ntfs_iconv\" kernel module");
+ return (-1);
+ }
+
+ if ((pargs->cs_ntfs = malloc(ICONV_CSNMAXLEN)) == NULL)
+ return (-1);
+ strncpy(pargs->cs_ntfs, ENCODING_UNICODE, ICONV_CSNMAXLEN);
+ error = kiconv_add_xlat16_cspairs(pargs->cs_ntfs, pargs->cs_local);
+ if (error)
+ return (-1);
+
+ return (0);
+}
diff --git a/sbin/mount_nullfs/Makefile b/sbin/mount_nullfs/Makefile
new file mode 100644
index 0000000..5e92c5b
--- /dev/null
+++ b/sbin/mount_nullfs/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+# $FreeBSD$
+
+PROG= mount_nullfs
+SRCS= mount_nullfs.c getmntopts.c
+MAN= mount_nullfs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+=-I${MOUNT}
+WARNS?= 6
+
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_nullfs/mount_nullfs.8 b/sbin/mount_nullfs/mount_nullfs.8
new file mode 100644
index 0000000..57c81dc
--- /dev/null
+++ b/sbin/mount_nullfs/mount_nullfs.8
@@ -0,0 +1,244 @@
+.\"
+.\" Copyright (c) 1992, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software donated to Berkeley by
+.\" John Heidemann of the UCLA Ficus project.
+.\"
+.\"
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)mount_null.8 8.6 (Berkeley) 5/1/95
+.\" $FreeBSD$
+.\"
+.Dd May 1, 1995
+.Dt MOUNT_NULLFS 8
+.Os
+.Sh NAME
+.Nm mount_nullfs
+.Nd "mount a loopback file system sub-tree; demonstrate the use of a null file system layer"
+.Sh SYNOPSIS
+.Nm
+.Op Fl o Ar options
+.Ar target
+.Ar mount-point
+.Sh DESCRIPTION
+The
+.Nm
+utility creates a
+null layer, duplicating a sub-tree of the file system
+name space under another part of the global file system namespace.
+This allows existing files and directories to be accessed
+using a different pathname.
+.Pp
+The primary differences between a virtual copy of the file system
+and a symbolic link are that the
+.Xr getcwd 3
+functions work correctly in the virtual copy, and that other file systems
+may be mounted on the virtual copy without affecting the original.
+A different device number for the virtual copy is returned by
+.Xr stat 2 ,
+but in other respects it is indistinguishable from the original.
+.Pp
+The
+.Nm
+file system differs from a traditional
+loopback file system in two respects: it is implemented using
+a stackable layers techniques, and its
+.Do null-node Dc Ns s
+stack above
+all lower-layer vnodes, not just over directory vnodes.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+.El
+.Pp
+The null layer has two purposes.
+First, it serves as a demonstration of layering by providing a layer
+which does nothing.
+(It actually does everything the loopback file system does,
+which is slightly more than nothing.)
+Second, the null layer can serve as a prototype layer.
+Since it provides all necessary layer framework,
+new file system layers can be created very easily by starting
+with a null layer.
+.Pp
+The remainder of this man page examines the null layer as a basis
+for constructing new layers.
+.\"
+.\"
+.Sh INSTANTIATING NEW NULL LAYERS
+New null layers are created with
+.Nm .
+The
+.Nm
+utility takes two arguments, the pathname
+of the lower vfs (target-pn) and the pathname where the null
+layer will appear in the namespace (mount-point-pn).
+After
+the null layer is put into place, the contents
+of target-pn subtree will be aliased under mount-point-pn.
+.\"
+.\"
+.Sh OPERATION OF A NULL LAYER
+The null layer is the minimum file system layer,
+simply bypassing all possible operations to the lower layer
+for processing there.
+The majority of its activity centers
+on the bypass routine, through which nearly all vnode operations
+pass.
+.Pp
+The bypass routine accepts arbitrary vnode operations for
+handling by the lower layer.
+It begins by examining vnode
+operation arguments and replacing any null-nodes by their
+lower-layer equivalents.
+It then invokes the operation
+on the lower layer.
+Finally, it replaces the null-nodes
+in the arguments and, if a vnode is returned by the operation,
+stacks a null-node on top of the returned vnode.
+.Pp
+Although bypass handles most operations,
+.Em vop_getattr ,
+.Em vop_inactive ,
+.Em vop_reclaim ,
+and
+.Em vop_print
+are not bypassed.
+.Em Vop_getattr
+must change the fsid being returned.
+.Em Vop_inactive
+and
+.Em vop_reclaim
+are not bypassed so that
+they can handle freeing null-layer specific data.
+.Em Vop_print
+is not bypassed to avoid excessive debugging
+information.
+.\"
+.\"
+.Sh INSTANTIATING VNODE STACKS
+Mounting associates the null layer with a lower layer,
+in effect stacking two VFSes.
+Vnode stacks are instead
+created on demand as files are accessed.
+.Pp
+The initial mount creates a single vnode stack for the
+root of the new null layer.
+All other vnode stacks
+are created as a result of vnode operations on
+this or other null vnode stacks.
+.Pp
+New vnode stacks come into existence as a result of
+an operation which returns a vnode.
+The bypass routine stacks a null-node above the new
+vnode before returning it to the caller.
+.Pp
+For example, imagine mounting a null layer with
+.Bd -literal -offset indent
+mount_nullfs /usr/include /dev/layer/null
+.Ed
+Changing directory to
+.Pa /dev/layer/null
+will assign
+the root null-node (which was created when the null layer was mounted).
+Now consider opening
+.Pa sys .
+A vop_lookup would be
+done on the root null-node.
+This operation would bypass through
+to the lower layer which would return a vnode representing
+the UFS
+.Pa sys .
+Null_bypass then builds a null-node
+aliasing the UFS
+.Pa sys
+and returns this to the caller.
+Later operations on the null-node
+.Pa sys
+will repeat this
+process when constructing other vnode stacks.
+.\"
+.\"
+.Sh CREATING OTHER FILE SYSTEM LAYERS
+One of the easiest ways to construct new file system layers is to make
+a copy of the null layer, rename all files and variables, and
+then begin modifying the copy.
+The
+.Xr sed 1
+utility can be used to easily rename
+all variables.
+.Pp
+The umap layer is an example of a layer descended from the
+null layer.
+.\"
+.\"
+.Sh INVOKING OPERATIONS ON LOWER LAYERS
+There are two techniques to invoke operations on a lower layer
+when the operation cannot be completely bypassed.
+Each method
+is appropriate in different situations.
+In both cases,
+it is the responsibility of the aliasing layer to make
+the operation arguments "correct" for the lower layer
+by mapping a vnode argument to the lower layer.
+.Pp
+The first approach is to call the aliasing layer's bypass routine.
+This method is most suitable when you wish to invoke the operation
+currently being handled on the lower layer.
+It has the advantage that
+the bypass routine already must do argument mapping.
+An example of this is
+.Em null_getattrs
+in the null layer.
+.Pp
+A second approach is to directly invoke vnode operations on
+the lower layer with the
+.Em VOP_OPERATIONNAME
+interface.
+The advantage of this method is that it is easy to invoke
+arbitrary operations on the lower layer.
+The disadvantage
+is that vnode arguments must be manually mapped.
+.\"
+.\"
+.Sh SEE ALSO
+.Xr mount 8
+.Pp
+UCLA Technical Report CSD-910056,
+.Em "Stackable Layers: an Architecture for File System Development" .
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
diff --git a/sbin/mount_nullfs/mount_nullfs.c b/sbin/mount_nullfs/mount_nullfs.c
new file mode 100644
index 0000000..a0ad5f6
--- /dev/null
+++ b/sbin/mount_nullfs/mount_nullfs.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mount_null.c 8.6 (Berkeley) 4/26/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/uio.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_END
+};
+
+int subdir(const char *, const char *);
+static void usage(void) __dead2;
+
+int
+main(int argc, char *argv[])
+{
+ struct iovec iov[6];
+ int ch, mntflags;
+ char source[MAXPATHLEN];
+ char target[MAXPATHLEN];
+
+ mntflags = 0;
+ while ((ch = getopt(argc, argv, "o:")) != -1)
+ switch(ch) {
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ /* resolve target and source with realpath(3) */
+ (void)checkpath(argv[0], target);
+ (void)checkpath(argv[1], source);
+
+ if (subdir(target, source) || subdir(source, target))
+ errx(EX_USAGE, "%s (%s) and %s are not distinct paths",
+ argv[0], target, argv[1]);
+
+ iov[0].iov_base = strdup("fstype");
+ iov[0].iov_len = sizeof("fstype");
+ iov[1].iov_base = strdup("nullfs");
+ iov[1].iov_len = strlen(iov[1].iov_base) + 1;
+ iov[2].iov_base = strdup("fspath");
+ iov[2].iov_len = sizeof("fspath");
+ iov[3].iov_base = source;
+ iov[3].iov_len = strlen(source) + 1;
+ iov[4].iov_base = strdup("target");
+ iov[4].iov_len = sizeof("target");
+ iov[5].iov_base = target;
+ iov[5].iov_len = strlen(target) + 1;
+
+ if (nmount(iov, 6, mntflags))
+ err(1, NULL);
+ exit(0);
+}
+
+int
+subdir(p, dir)
+ const char *p;
+ const char *dir;
+{
+ int l;
+
+ l = strlen(dir);
+ if (l <= 1)
+ return (1);
+
+ if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0'))
+ return (1);
+
+ return (0);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_nullfs [-o options] target mount-point\n");
+ exit(1);
+}
diff --git a/sbin/mount_reiserfs/Makefile b/sbin/mount_reiserfs/Makefile
new file mode 100644
index 0000000..bd11a4b
--- /dev/null
+++ b/sbin/mount_reiserfs/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG = mount_reiserfs
+SRCS = mount_reiserfs.c getmntopts.c
+MAN = mount_reiserfs.8
+
+# mount_reiserfs needs mntopts.h and getmntopts.c from src/sbin/mount/
+MOUNT ?= ${.CURDIR}/../mount
+CFLAGS += -I${MOUNT}
+WARNS ?= 6
+
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_reiserfs/mount_reiserfs.8 b/sbin/mount_reiserfs/mount_reiserfs.8
new file mode 100644
index 0000000..7306182
--- /dev/null
+++ b/sbin/mount_reiserfs/mount_reiserfs.8
@@ -0,0 +1,90 @@
+.\"
+.\" Copyright (c) 1993,1994 Christopher G. Demetriou
+.\" Copyright (c) 1999 Semen Ustimenko
+.\" Copyright (c) 2005 Jean-Sébastien Pédron
+.\" 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 Christopher G. Demetriou.
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 3, 2005
+.Dt MOUNT_REISERFS 8
+.Os
+.Sh NAME
+.Nm mount_reiserfs
+.Nd "mount a ReiserFS file system"
+.Sh SYNOPSIS
+.Nm
+.Ar special
+.Ar node
+.Sh DESCRIPTION
+The
+.Nm
+utility attaches the ReiserFS file system residing on the device
+.Ar special
+to the global file system namespace at the location
+indicated by
+.Ar node .
+.Pp
+This command is normally executed by
+.Xr mount 8
+at boot time, but can be used by any user to mount a
+ReiserFS file system on any directory that they own (provided,
+of course, that they have appropriate access to the device that
+contains the file system).
+.Sh EXAMPLES
+To mount a ReiserFS volume located in
+.Pa /dev/ad1s1 :
+.Pp
+.Dl "mount_reiserfs /dev/ad1s1 /mnt"
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh CAVEATS
+This utility is primarily used for read access to a ReiserFS volume.
+Writing to a volume is currently unsupported.
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 6.0 .
+.Sh AUTHORS
+.An -nosplit
+The ReiserFS kernel implementation was written by
+.An Hans Reiser
+.Pq Pa http://www.namesys.com/ ,
+and ported to
+.Fx
+by
+.An Jean-S\['e]bastien P\['e]dron Aq dumbbell@FreeBSD.org .
+.Pp
+The
+.Nm
+utility and manual were written by
+.An Jean-S\['e]bastien P\['e]dron Aq dumbbell@FreeBSD.org .
diff --git a/sbin/mount_reiserfs/mount_reiserfs.c b/sbin/mount_reiserfs/mount_reiserfs.c
new file mode 100644
index 0000000..5c4e6fc
--- /dev/null
+++ b/sbin/mount_reiserfs/mount_reiserfs.c
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 2005 Jean-Sébastien Pédron
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/uio.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_END
+};
+
+void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ struct iovec *iov;
+ int ch, mntflags, iovlen;
+ char *dev, *dir, mntpath[MAXPATHLEN];
+ char fstype[] = "reiserfs";
+
+ mntflags = 0;
+ while ((ch = getopt(argc, argv, "o:")) != -1) {
+ switch(ch) {
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ dev = argv[0];
+ dir = argv[1];
+
+ /*
+ * Resolve the mountpoint with realpath(3) and remove unnecessary
+ * slashes from the devicename if there are any.
+ */
+ (void)checkpath(dir, mntpath);
+ (void)rmslashes(dev, dev);
+
+ /* Read-only support for now */
+ mntflags |= MNT_RDONLY;
+
+ /* Prepare the options vector for nmount(). build_iovec() is declared
+ * in mntopts.h. */
+ iov = NULL;
+ iovlen = 0;
+ build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1);
+ build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1);
+ build_iovec(&iov, &iovlen, "from", dev, (size_t)-1);
+
+ if (nmount(iov, iovlen, mntflags) < 0)
+ err(EX_OSERR, "%s", dev);
+
+ exit(0);
+}
+
+void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: mount_reiserfs [-o options] special node\n");
+ exit(EX_USAGE);
+}
diff --git a/sbin/mount_std/Makefile b/sbin/mount_std/Makefile
new file mode 100644
index 0000000..110a525
--- /dev/null
+++ b/sbin/mount_std/Makefile
@@ -0,0 +1,23 @@
+# @(#)Makefile 8.2 (Berkeley) 3/27/94
+# $FreeBSD$
+
+PROG= mount_std
+SRCS= mount_std.c getmntopts.c
+MAN= mount_std.8
+MLINKS= mount_std.8 mount_devfs.8 \
+ mount_std.8 mount_fdescfs.8 \
+ mount_std.8 mount_linprocfs.8 \
+ mount_std.8 mount_procfs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${MOUNT}
+WARNS?= 0
+
+.PATH: ${MOUNT}
+
+LINKS= ${BINDIR}/mount_std ${BINDIR}/mount_devfs \
+ ${BINDIR}/mount_std ${BINDIR}/mount_fdescfs \
+ ${BINDIR}/mount_std ${BINDIR}/mount_linprocfs \
+ ${BINDIR}/mount_std ${BINDIR}/mount_procfs
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_std/mount_std.8 b/sbin/mount_std/mount_std.8
new file mode 100644
index 0000000..3a4df0f
--- /dev/null
+++ b/sbin/mount_std/mount_std.8
@@ -0,0 +1,167 @@
+.\"
+.\" Copyright (c) 1992, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software donated to Berkeley by
+.\" Jan-Simon Pendry.
+.\"
+.\" 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.
+.\" 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 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 26, 2004
+.Dt MOUNT_STD 8
+.Os
+.Sh NAME
+.Nm mount_std ,
+.Nm mount_devfs ,
+.Nm mount_fdescfs ,
+.Nm mount_linprocfs ,
+.Nm mount_procfs
+.Nd mount
+.Dq standard
+file systems
+.Sh SYNOPSIS
+.Nm mount_ Ns Ar fsname
+.Op Fl o Ar options
+.Ar "fs"
+.Ar mount_point
+.Sh DESCRIPTION
+The
+.Nm
+utility is a generic mechanism for attaching ``standard'' file systems to
+the file system.
+The
+.Nm
+utility currently supports the following file systems:
+.Nm devfs ,
+.Nm fdescfs ,
+.Nm linprocfs
+and
+.Nm procfs .
+A ``standard'' file system is one which:
+.Bl -enum -offset indent
+.It
+accepts only the standard
+.Fl o
+options
+.Dq ro ,
+.Dq rw ,
+.Dq noexec ,
+.Dq nosuid ,
+and
+.Dq union .
+.It
+has a kernel file system module name the same as its user-visible name.
+.It
+requires no other special processing on the part of the
+.Nm
+utility.
+.El
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+.El
+.Pp
+The
+.Nm
+utility examines its zeroth command-line argument (the name by which
+it was called) to determine the type of file system to be mounted.
+If
+it is called by a name which does not end in
+.Dq Li _ Ns Ar fsname ,
+.Nm
+will assume (for compatibility
+with
+.Xr mount 8 )
+that the zeroth argument contains only the name of the file system type.
+The
+.Nm
+utility is normally installed with appropriate links to commands for
+the distributed file systems which can be mounted in this way;
+for information on the function of each file system, see the manual page
+for that specific
+.Nm mount_ Ns Ar fsname
+utility.
+.Pp
+Refer to the following manual pages for detailed information
+on these file system:
+.Xr devfs 5 ,
+.Xr fdescfs 5 ,
+.Xr linprocfs 5
+and
+.Xr procfs 5 .
+.Sh DIAGNOSTICS
+.Bl -diag
+.It argv[0] must end in _fsname
+The
+.Nm
+utility was called with a zeroth argument of
+.Dq Li mount_std .
+.It %s file system not available
+The specified file system type was not present in the kernel and no
+loadable module for it was found.
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr getvfsbyname 3 ,
+.Xr devfs 5 ,
+.Xr fdescfs 5 ,
+.Xr fstab 5 ,
+.Xr linprocfs 5 ,
+.Xr procfs 5 ,
+.Xr mount 8
+.Sh CAVEATS
+None of the ``standard'' file systems may be NFS-exported.
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 2.2 .
+Loadable file system modules first appeared in
+.Fx 2.0 .
+The
+.Dq fdescfs
+and
+.Dq procfs
+file system types first appeared in
+.Fx 2.0 ;
+the
+.Dq devfs
+file system type first appeared in
+.Fx 2.2 ;
+the
+.Dq linprocfs
+file system type first appeared in
+.Fx 4.0 .
diff --git a/sbin/mount_std/mount_std.c b/sbin/mount_std/mount_std.c
new file mode 100644
index 0000000..ec8041d
--- /dev/null
+++ b/sbin/mount_std/mount_std.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 1990, 1992 Jan-Simon Pendry
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/uio.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_END
+};
+
+static char *fsname;
+static volatile sig_atomic_t caughtsig;
+
+static void usage(void) __dead2;
+
+static void
+catchsig(int s)
+{
+ caughtsig = 1;
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, mntflags;
+ char mntpath[MAXPATHLEN];
+ struct iovec iov[4];
+ int error;
+
+ /*
+ * XXX
+ * mount(8) calls the mount programs with an argv[0] which is
+ * /just/ the file system name. So, if there is no underscore
+ * in argv[0], we assume that we are being called from mount(8)
+ * and that argv[0] is thus the name of the file system type.
+ */
+ fsname = strrchr(argv[0], '_');
+ if (fsname) {
+ if (strcmp(fsname, "_std") == 0)
+ errx(EX_USAGE, "argv[0] must end in _fsname");
+ fsname++;
+ } else {
+ fsname = argv[0];
+ }
+
+ mntflags = 0;
+ while ((ch = getopt(argc, argv, "o:")) != -1)
+ switch (ch) {
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ /* resolve the mountpoint with realpath(3) */
+ (void)checkpath(argv[1], mntpath);
+
+ iov[0].iov_base = "fstype";
+ iov[0].iov_len = sizeof("fstype");
+ iov[1].iov_base = fsname;
+ iov[1].iov_len = strlen(iov[1].iov_base) + 1;
+ iov[2].iov_base = "fspath";
+ iov[2].iov_len = sizeof("fspath");
+ iov[3].iov_base = mntpath;
+ iov[3].iov_len = strlen(mntpath) + 1;
+
+ /*
+ * nmount(2) would kill us with SIGSYS if the kernel doesn't have it.
+ * This design bug is inconvenient. We must catch the signal and not
+ * just ignore it because of a plain bug: nmount(2) would return
+ * EINVAL instead of the correct ENOSYS if the kernel doesn't have it
+ * and we don't let the signal kill us. EINVAL is too ambiguous.
+ * This bug in 4.4BSD-Lite1 was fixed in 4.4BSD-Lite2 but is still in
+ * FreeBSD-5.0.
+ */
+ signal(SIGSYS, catchsig);
+ error = nmount(iov, 4, mntflags);
+ signal(SIGSYS, SIG_DFL);
+
+ /*
+ * Try with the old mount syscall in the case
+ * this file system has not been converted yet,
+ * or the user didn't recompile his kernel.
+ */
+ if (error && (errno == EOPNOTSUPP || errno == ENOSYS || caughtsig))
+ error = mount(fsname, mntpath, mntflags, NULL);
+
+ if (error)
+ err(EX_OSERR, NULL);
+ exit(0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_%s [-o options] what_to_mount mount_point\n",
+ fsname);
+ exit(EX_USAGE);
+}
diff --git a/sbin/mount_udf/Makefile b/sbin/mount_udf/Makefile
new file mode 100644
index 0000000..e83328a
--- /dev/null
+++ b/sbin/mount_udf/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+PROG= mount_udf
+SRCS= mount_udf.c getmntopts.c
+MAN= mount_udf.8
+DPADD= ${LIBKICONV}
+LDADD= -lkiconv
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${MOUNT} -I${.CURDIR}/../../sys -Wall
+.PATH: ${MOUNT}
+WARNS?= 1
+
+# Needs to be dynamically linked for optional dlopen() access to
+# userland libiconv
+NO_SHARED?= NO
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_udf/mount_udf.8 b/sbin/mount_udf/mount_udf.8
new file mode 100644
index 0000000..fb39419
--- /dev/null
+++ b/sbin/mount_udf/mount_udf.8
@@ -0,0 +1,76 @@
+.\" Copyright (c) 2002
+.\" Scott Long <scottl@FreeBSD.org>
+.\" Jeroen Ruigrok van der Werven <asmodai@wxs.nl>
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 23, 2002
+.Dt MOUNT_UDF 8
+.Os
+.Sh NAME
+.Nm mount_udf
+.Nd mount a UDF file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Op Fl o Ar options
+.Op Fl C Ar charset
+.Ar special node
+.Sh DESCRIPTION
+The
+.Nm
+utility attaches the UDF file system residing on the device
+.Ar special
+to the global file system namespace at the location indicated by
+.Ar node .
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+The following UDF specific options are available:
+.It Fl v
+Be verbose about mounting the UDF file system.
+.It Fl C Ar charset
+Specify local
+.Ar charset
+to convert Unicode file names.
+.El
+.Sh SEE ALSO
+.Xr cdcontrol 1 ,
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.0 .
diff --git a/sbin/mount_udf/mount_udf.c b/sbin/mount_udf/mount_udf.c
new file mode 100644
index 0000000..0175f20
--- /dev/null
+++ b/sbin/mount_udf/mount_udf.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2002 Scott Long
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * 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.
+ * 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 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * This is just a rip-off of mount_iso9660.c. It's been vastly simplified
+ * because UDF doesn't take any options at this time.
+ */
+
+#include <sys/cdio.h>
+#include <sys/file.h>
+#include <sys/iconv.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/mount.h>
+#include <sys/uio.h>
+
+#include <fs/udf/udf_mount.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_UPDATE,
+ MOPT_END
+};
+
+int set_charset(char **, char **, const char *);
+void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ struct iovec iov[12];
+ int ch, i, mntflags, opts, udf_flags;
+ char *dev, *dir, mntpath[MAXPATHLEN];
+ char *cs_disk, *cs_local;
+ int verbose;
+
+ i = mntflags = opts = udf_flags = verbose = 0;
+ cs_disk = cs_local = NULL;
+ while ((ch = getopt(argc, argv, "o:vC:")) != -1)
+ switch (ch) {
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, &opts);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'C':
+ if (set_charset(&cs_disk, &cs_local, optarg) == -1)
+ err(EX_OSERR, "udf_iconv");
+ udf_flags |= UDFMNT_KICONV;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ dev = argv[0];
+ dir = argv[1];
+
+ /*
+ * Resolve the mountpoint with realpath(3) and remove unnecessary
+ * slashes from the devicename if there are any.
+ */
+ (void)checkpath(dir, mntpath);
+ (void)rmslashes(dev, dev);
+
+ /*
+ * UDF file systems are not writeable.
+ */
+ mntflags |= MNT_RDONLY;
+
+ iov[i].iov_base = "fstype";
+ iov[i++].iov_len = sizeof("fstype");
+ iov[i].iov_base = "udf";
+ iov[i].iov_len = strlen(iov[i].iov_base) + 1;
+ i++;
+ iov[i].iov_base = "fspath";
+ iov[i++].iov_len = sizeof("fspath");
+ iov[i].iov_base = mntpath;
+ iov[i++].iov_len = strlen(mntpath) + 1;
+ iov[i].iov_base = "from";
+ iov[i++].iov_len = sizeof("from");
+ iov[i].iov_base = dev;
+ iov[i++].iov_len = strlen(dev) + 1;
+ iov[i].iov_base = "flags";
+ iov[i++].iov_len = sizeof("flags");
+ iov[i].iov_base = &udf_flags;
+ iov[i++].iov_len = sizeof(udf_flags);
+ if (udf_flags & UDFMNT_KICONV) {
+ iov[i].iov_base = "cs_disk";
+ iov[i++].iov_len = sizeof("cs_disk");
+ iov[i].iov_base = cs_disk;
+ iov[i++].iov_len = strlen(cs_disk) + 1;
+ iov[i].iov_base = "cs_local";
+ iov[i++].iov_len = sizeof("cs_local");
+ iov[i].iov_base = cs_local;
+ iov[i++].iov_len = strlen(cs_local) + 1;
+ }
+ if (nmount(iov, i, mntflags) < 0)
+ err(1, "%s", dev);
+ exit(0);
+}
+
+int
+set_charset(char **cs_disk, char **cs_local, const char *localcs)
+{
+ int error;
+
+ if (modfind("udf_iconv") < 0)
+ if (kldload("udf_iconv") < 0 || modfind("udf_iconv") < 0) {
+ warnx( "cannot find or load \"udf_iconv\" kernel module");
+ return (-1);
+ }
+
+ if ((*cs_disk = malloc(ICONV_CSNMAXLEN)) == NULL)
+ return (-1);
+ if ((*cs_local = malloc(ICONV_CSNMAXLEN)) == NULL)
+ return (-1);
+ strncpy(*cs_disk, ENCODING_UNICODE, ICONV_CSNMAXLEN);
+ strncpy(*cs_local, localcs, ICONV_CSNMAXLEN);
+ error = kiconv_add_xlat16_cspairs(*cs_disk, *cs_local);
+ if (error)
+ return (-1);
+
+ return (0);
+}
+
+void
+usage(void)
+{
+ (void)fprintf(stderr,
+ "usage: mount_udf [-v] [-o options] [-C charset] special node\n");
+ exit(EX_USAGE);
+}
diff --git a/sbin/mount_umapfs/Makefile b/sbin/mount_umapfs/Makefile
new file mode 100644
index 0000000..0f5891f
--- /dev/null
+++ b/sbin/mount_umapfs/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+# $FreeBSD$
+
+PROG= mount_umapfs
+SRCS= mount_umapfs.c getmntopts.c
+MAN= mount_umapfs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+=-I${MOUNT}
+WARNS?= 0
+
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_umapfs/mount_umapfs.8 b/sbin/mount_umapfs/mount_umapfs.8
new file mode 100644
index 0000000..1bbaadb
--- /dev/null
+++ b/sbin/mount_umapfs/mount_umapfs.8
@@ -0,0 +1,146 @@
+.\" Copyright (c) 1992, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software donated to Berkeley by
+.\" Jan-Simon Pendry and from John Heidemann of the UCLA Ficus project.
+.\"
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)mount_umap.8 8.4 (Berkeley) 5/1/95
+.\" $FreeBSD$
+.\"
+.Dd May 1, 1995
+.Dt MOUNT_UMAPFS 8
+.Os
+.Sh NAME
+.Nm mount_umapfs
+.Nd sample file system layer
+.Sh SYNOPSIS
+.Nm
+.Op Fl o Ar options
+.Fl u Ar uid-mapfile
+.Fl g Ar gid-mapfile
+.Ar target
+.Ar mount-point
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to mount a sub-tree of an existing file system
+that uses a different set of uids and gids than the local system.
+Such a file system could be mounted from a remote site via NFS or
+it could be a file system on removable media brought from some
+foreign location that uses a different password file.
+.Pp
+The
+.Nm
+utility uses a set of files provided by the user to make correspondences
+between uids and gids in the sub-tree's original environment and
+some other set of ids in the local environment.
+For instance, user
+smith might have uid 1000 in the original environment, while having
+uid 2000 in the local environment.
+The
+.Nm
+utility allows the subtree from smith's original environment to be
+mapped in such a way that all files with owning uid 1000 look like
+they are actually owned by uid 2000.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+.It Ar target
+Should be the current location of the sub-tree in the
+local system's name space.
+.It Ar mount-point
+Should be a directory
+where the mapped subtree is to be placed.
+.It Fl u Ar uid-mapfile
+.It Fl g Ar gid-mapfile
+Describe the mappings to be made between identifiers.
+Briefly, the format of these files is a count of the number of
+mappings on the first line, with each subsequent line containing
+a single mapping.
+Each of these mappings consists of an id in
+the local environment and the corresponding id from the original environment,
+separated by white space.
+.Ar Uid-mapfile
+should contain all uid
+mappings, and
+.Ar gid-mapfile
+should contain all gid mappings.
+Any uids not mapped in
+.Ar uid-mapfile
+will be treated as user NOBODY,
+and any gids not mapped in
+.Ar gid-mapfile
+will be treated as group
+NULLGROUP.
+At most 64 uids can be mapped for a given subtree, and
+at most 16 groups can be mapped by a given subtree.
+.El
+.Pp
+The mapfiles can be located anywhere in the file hierarchy, but they
+must be owned by root, and they must be writable only by root.
+The
+.Nm
+utility will refuse to map the sub-tree if the ownership or permissions on
+these files are improper.
+It will also balk if the count of mappings
+in the first line of the map files is not correct.
+.Pp
+The layer created by the
+.Nm
+utility is meant to serve as a simple example of file system layering.
+It is not meant for production use.
+The implementation is not very
+sophisticated.
+.Sh SEE ALSO
+.Xr mount 8 ,
+.Xr mount_nullfs 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
+.Sh BUGS
+THIS FILE SYSTEM TYPE IS NOT YET FULLY SUPPORTED (READ: IT DOESN'T WORK)
+AND USING IT MAY, IN FACT, DESTROY DATA ON YOUR SYSTEM.
+USE AT YOUR
+OWN RISK.
+BEWARE OF DOG.
+SLIPPERY WHEN WET.
+.Pp
+This code also needs an owner in order to be less dangerous - serious
+hackers can apply by sending mail to
+.Aq hackers@FreeBSD.org
+and announcing
+their intent to take it over.
diff --git a/sbin/mount_umapfs/mount_umapfs.c b/sbin/mount_umapfs/mount_umapfs.c
new file mode 100644
index 0000000..b16cf10
--- /dev/null
+++ b/sbin/mount_umapfs/mount_umapfs.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mount_umap.c 8.5 (Berkeley) 4/26/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <fs/umapfs/umap.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+#define ROOTUSER 0
+/*
+ * This define controls whether any user but the superuser can own and
+ * write mapfiles. If other users can, system security can be gravely
+ * compromised. If this is not a concern, undefine SECURITY.
+ */
+#define MAPSECURITY 1
+
+/*
+ * This routine provides the user interface to mounting a umap layer.
+ * It takes 4 mandatory parameters. The mandatory arguments are the place
+ * where the next lower level is mounted, the place where the umap layer is to
+ * be mounted, the name of the user mapfile, and the name of the group
+ * mapfile. The routine checks the ownerships and permissions on the
+ * mapfiles, then opens and reads them. Then it calls mount(), which
+ * will, in turn, call the umap version of mount.
+ */
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_END
+};
+
+static void usage(void) __dead2;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ static char not[] = "; not mounted";
+ struct stat statbuf;
+ struct umap_args args;
+ FILE *fp, *gfp;
+ u_long gmapdata[GMAPFILEENTRIES][2], mapdata[MAPFILEENTRIES][2];
+ int ch, count, gnentries, mntflags, nentries;
+ char *gmapfile, *mapfile, buf[20];
+ char source[MAXPATHLEN], target[MAXPATHLEN];
+
+ mntflags = 0;
+ mapfile = gmapfile = NULL;
+ while ((ch = getopt(argc, argv, "g:o:u:")) != -1)
+ switch (ch) {
+ case 'g':
+ gmapfile = optarg;
+ break;
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case 'u':
+ mapfile = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2 || mapfile == NULL || gmapfile == NULL)
+ usage();
+
+ /* resolve both target and source with realpath(3) */
+ (void)checkpath(argv[0], source);
+ (void)checkpath(argv[1], target);
+
+ /* Read in uid mapping data. */
+ if ((fp = fopen(mapfile, "r")) == NULL)
+ err(EX_NOINPUT, "%s%s", mapfile, not);
+
+#ifdef MAPSECURITY
+ /*
+ * Check that group and other don't have write permissions on
+ * this mapfile, and that the mapfile belongs to root.
+ */
+ if (fstat(fileno(fp), &statbuf))
+ err(EX_OSERR, "%s%s", mapfile, not);
+ if (statbuf.st_mode & S_IWGRP || statbuf.st_mode & S_IWOTH) {
+ strmode(statbuf.st_mode, buf);
+ err(EX_NOPERM, "%s: improper write permissions (%s)%s",
+ mapfile, buf, not);
+ }
+ if (statbuf.st_uid != ROOTUSER)
+ errx(EX_NOPERM, "%s does not belong to root%s", mapfile, not);
+#endif /* MAPSECURITY */
+
+ if ((fscanf(fp, "%d\n", &nentries)) != 1)
+ errx(EX_DATAERR, "%s: nentries not found%s", mapfile, not);
+ if (nentries > MAPFILEENTRIES)
+ errx(EX_DATAERR,
+ "maximum number of entries is %d%s", MAPFILEENTRIES, not);
+#if 0
+ (void)printf("reading %d entries\n", nentries);
+#endif
+ for (count = 0; count < nentries; ++count) {
+ if ((fscanf(fp, "%lu %lu\n",
+ &(mapdata[count][0]), &(mapdata[count][1]))) != 2) {
+ if (ferror(fp))
+ err(EX_OSERR, "%s%s", mapfile, not);
+ if (feof(fp))
+ errx(EX_DATAERR, "%s: unexpected end-of-file%s",
+ mapfile, not);
+ errx(EX_DATAERR, "%s: illegal format (line %d)%s",
+ mapfile, count + 2, not);
+ }
+#if 0
+ /* Fix a security hole. */
+ if (mapdata[count][1] == 0)
+ errx(1, "mapping id 0 not permitted (line %d)%s",
+ count + 2, not);
+#endif
+ }
+
+ /* Read in gid mapping data. */
+ if ((gfp = fopen(gmapfile, "r")) == NULL)
+ err(EX_NOINPUT, "%s%s", gmapfile, not);
+
+#ifdef MAPSECURITY
+ /*
+ * Check that group and other don't have write permissions on
+ * this group mapfile, and that the file belongs to root.
+ */
+ if (fstat(fileno(gfp), &statbuf))
+ err(EX_OSERR, "%s%s", gmapfile, not);
+ if (statbuf.st_mode & S_IWGRP || statbuf.st_mode & S_IWOTH) {
+ strmode(statbuf.st_mode, buf);
+ err(EX_NOPERM, "%s: improper write permissions (%s)%s",
+ gmapfile, buf, not);
+ }
+ if (statbuf.st_uid != ROOTUSER)
+ errx(EX_NOPERM, "%s does not belong to root%s", gmapfile, not);
+#endif /* MAPSECURITY */
+
+ if ((fscanf(gfp, "%d\n", &gnentries)) != 1)
+ errx(EX_DATAERR, "%s: nentries not found%s", gmapfile, not);
+ if (gnentries > MAPFILEENTRIES)
+ errx(EX_DATAERR,
+ "maximum number of entries is %d%s", GMAPFILEENTRIES, not);
+#if 0
+ (void)printf("reading %d group entries\n", gnentries);
+#endif
+
+ for (count = 0; count < gnentries; ++count)
+ if ((fscanf(gfp, "%lu %lu\n",
+ &(gmapdata[count][0]), &(gmapdata[count][1]))) != 2) {
+ if (ferror(gfp))
+ err(EX_OSERR, "%s%s", gmapfile, not);
+ if (feof(gfp))
+ errx(EX_DATAERR, "%s: unexpected end-of-file%s",
+ gmapfile, not);
+ errx(EX_DATAERR, "%s: illegal format (line %d)%s",
+ gmapfile, count + 2, not);
+ }
+
+
+ /* Setup mount call args. */
+ args.target = source;
+ args.nentries = nentries;
+ args.mapdata = mapdata;
+ args.gnentries = gnentries;
+ args.gmapdata = gmapdata;
+
+ if (mount("umapfs", argv[1], mntflags, &args))
+ err(1, NULL);
+ exit(0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: mount_umapfs [-o options] -u uid-mapfile -g gid-mapfile target mount-point\n");
+ exit(EX_USAGE);
+}
diff --git a/sbin/mount_umapfs/sample.group.mapfile b/sbin/mount_umapfs/sample.group.mapfile
new file mode 100644
index 0000000..69d3d77
--- /dev/null
+++ b/sbin/mount_umapfs/sample.group.mapfile
@@ -0,0 +1,2 @@
+1
+1200 1200
diff --git a/sbin/mount_umapfs/sample.user.mapfile b/sbin/mount_umapfs/sample.user.mapfile
new file mode 100644
index 0000000..03c59e9
--- /dev/null
+++ b/sbin/mount_umapfs/sample.user.mapfile
@@ -0,0 +1,3 @@
+2
+5217 5217
+3 3
diff --git a/sbin/mount_umapfs/umap_manual b/sbin/mount_umapfs/umap_manual
new file mode 100644
index 0000000..50a8ce5
--- /dev/null
+++ b/sbin/mount_umapfs/umap_manual
@@ -0,0 +1,175 @@
+
+\appendix
+\section{The umap Layer} \label{sect:umap}
+
+\subsection{Introduction}
+
+Normally, the file system is expected to span a single administrative domain.
+An administrative domain, for these purposes, is a machine or set of
+machines that share common password file information, usually through
+the yellow pages mechanism. File hierarchies that span more
+than one domain leads to certain problems, since the same numerical
+UID in one domain may correspond to a different user in another domain.
+If the system administrator is very careful to ensure that both domains
+contain identical user ID information, the umap layer can be used to
+run between those domains without changes
+
+The umap layer is a file system layer that sits on top of the normal
+file layer. The umap layer maps Unix-style UIDs from
+one domain into the UIDs in the other domain. By setting up the mappings
+properly, the same user with different UIDs in two domains can be seen
+as the same user, from the system point of view, or, conversely, two
+different users with the same UID in the two domains can be distinguished.
+
+First, we define some terms. ``User'' refers to the human (or daemon) that
+has privileges to login, run programs, and access files. ``UID''refers to
+the numerical identifier that uniquely identifies the user within a
+single domain. ``Login name'' refers to the character string the user
+types to log into the system. ``GID'' refers to the numerical group
+identifier used by Unix systems to identify groups of users. ``Group
+name'' is the character string name attached to a particular GID in the
+local {\sf /etc/groups} file or the yellow pages groups file.
+
+In order for the umap layer to work properly, all users
+in either domain must have password file entries in both domains.
+They do not, however, have to have the same numerical UID, nor even the
+same character string login name (the latter is highly recommended,
+if possible, however). Any user not having a UID in one domain will be
+treated as the special user NOBODY by the other domain, probably with
+undesirable consequences. Any user not owning any files in the shared
+sub-trees need not be given a UID in the other domain.
+
+Groups work similarly. The umap layer can translate group ID's between
+domains in the same manner as UID's. Again, any group that wishes to
+participate must have a group ID in both domains,
+though it need not be the same GID in both. If a group in one domain is not
+known in the other domain, that group will be treated as being NULLGROUP.
+The umap layer has no provisions for enrolling UID's from other domains
+as group members, but, since each user from each domain must have some
+UID in every domain, the UID in the local domain can be used to enroll
+the user in the local groups.
+
+NOBODY and NULLGROUP are special reserved UID's and GID's, respectively.
+NOBODY is user 32767. NULLGROUP is group 65534. If the system administrator
+wants to have an appropriate text string appear when these UID's are
+encountered by programs like {\sf ls -l}, he should add these values to
+the password and {\sf /etc/groups} file, or to the appropriate yellow pages.
+If these IDs are already in use in that domain, different values can be
+used for NOBODY and NULLGROUP, but that will require a recompilation of
+the umap layer code and, as a result, the entire kernel. These
+values are defined in the {\sf umap\_info.h} file, kept with the rest of the
+umap source code.
+
+When the umap layer is in use, one of the participating domains is declared
+to be the master. All UID and GID information stored for participating files
+will be stored in vnodes using its mappings, no matter what site the copies of
+the files are stored at. The master domain therefore need not run a copy
+of the umap layer, as it already has all of the correct mappings. All
+other domains must run a umap layer on top of any other layers they use.
+
+\subsection{Setting Up a umap Layer}
+
+The system administrator of a system needing to use the umap layer
+must take several actions.
+First, he must create files containing the necessary UID
+and GID mappings. There is a separate file for user and group IDs. The
+format of the files is the same. The first line contains the total number
+of entries in the file. Each subsequent line contains one mapping. A
+mapping line consists of two numerical UIDs, separated by white space.
+The first is the UID of a user on the local machine. The second is the
+UID for the same user on the master machine. The maximum number of users
+that can be mapped for a single shared sub-tree is 64. The maximum number of
+groups that can be mapped for a single sub-tree is 16. These constants
+are set in the {\sf umap\_info.h} file, and can be changed, but changing them
+requires recompilation. Separate mapping files can be used for each shared
+subtree, or the same mapping files can be shared by several sub-trees.
+
+Below is a sample UID mapping file. There are four entries. UID 5 is mapped
+to 5, 521 to 521, and 7000 to 7000. UID 2002 is mapped to 604. On this
+machine, the UID's for users 5, 521, and 7000 are the same as on the master,
+but UID 2002 is for a user whose UID on the master machine is 604. All
+files in the sub-tree belonging to that user have UID 604 in their inodes,
+even on this machine, but the umap layer will ensure that anyone running
+under UID 2002 will have all files in this sub-tree owned by 604 treated as if
+they were owned by 2002. An {\sf ls -l} on a file owned by 604 in this sub-tree
+will show the login name associated with UID 2002 as the owner.
+
+\noindent4\newline
+5 5\newline
+521 521\newline
+2002 604\newline
+7000 7000\newline
+
+The user and group mapping files should be owned by the root user, and
+should be writable only by that user. If they are not owned by root, or
+are writable by some other user, the umap mounting command will abort.
+
+Normally, the sub-tree is grafted directly into the place in
+the file hierarchy where the it should appear to users. Using the umap
+layer requires that the sub-tree be grafted somewhere else, and
+the umap layer be mounted in the desired position in the file hierarchy.
+Depending on the situation, the underlying sub-tree can be wherever is
+convenient.
+
+\subsection{Troubleshooting umap Layer Problems}
+
+The umap layer code was not built with special convenience or
+robustness in mind, as it is expected to be superseded with a better
+user ID mapping strategy in the near future. As a result, it is not
+very forgiving of errors in being set up. Here are some possible
+problems, and what to do about them.
+
+\begin{itemize}
+
+
+\item{Problem: A file belongs to NOBODY, or group NULLGROUP.
+
+Fixes: The mapping files don't know about this file's real user or group.
+Either they are not in the mapping files, or the counts on the number of
+entries in the mapping files are too low, so entries at the end (including
+these) are being ignored. Add the entries or fix the counts, and either
+unmount and remount the sub-tree, or reboot.}
+
+\item{Problem: A normal operation does not work.
+
+Fixes: Possibly, some mapping has not been set properly. Check to
+see which files are used by the operation and who they appear to be
+owned by. If they are owned by NOBODY or some other suspicious user,
+there may be a problem in the mapping files. Be sure to check groups,
+too. As above, if the counts of mappings in the mapping files are lower
+than the actual numbers of pairs, pairs at the end of the file will be
+ignored. If any changes are made in the mapping files, you will need to
+either unmount and remount or reboot before they will take effect.
+
+Another possible problem can arise because not all Unix utilities
+rely exclusively on numeric UID for identification. For instance,
+SCCS saves the login name in files. If a user's login name on two machines
+isn't the same, SCCS may veto an operation even though Unix file permissions,
+as checked by the umap layer, may say it's OK. There's not much to be
+done in such cases, unless the login name can be changed or one fiddles
+improperly with SCCS information. There may be other, undiscovered cases
+where similar problems arise, some of which may be even harder to handle.}
+
+\item{Problem: Someone has access permissions he should not have.
+
+Fixes: This is probably caused by a mistake in the mapping files. Check
+both user and group mapping files. If any changes are made in the mapping
+files, you will need to unmount and remount the sub-tree or reboot before they
+will take effect.}
+
+\item{Problem: {\sf ls -l} (or a similar program) shows the wrong user for a file.
+
+Fixes: Probably a mistake in the mapping files. In particular, if
+two local UIDs are mapped to a single master UID, stat calls will assign
+ownership to the first local UID occurring in the file, which may or may
+not be what was intended. (Generally speaking, mapping two local UIDs to
+a single master UID is a bad idea, but the software will not prevent it.
+Similarly, mapping a single local UID to two master UIDs is a bad idea,
+but will not be prevented. In this case, only the first mapping of the
+local UID will be done. The second, and all subsequent ones, will be
+ignored.) If any changes are made in the mapping files, you will need to
+unmount and remount the sub-tree or reboot before they will take effect.}
+
+\end{itemize}
+
+\end{document}
diff --git a/sbin/mount_unionfs/Makefile b/sbin/mount_unionfs/Makefile
new file mode 100644
index 0000000..0296504
--- /dev/null
+++ b/sbin/mount_unionfs/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+# $FreeBSD$
+
+PROG= mount_unionfs
+SRCS= mount_unionfs.c getmntopts.c
+MAN= mount_unionfs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+=-I${MOUNT}
+WARNS?= 0
+
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_unionfs/mount_unionfs.8 b/sbin/mount_unionfs/mount_unionfs.8
new file mode 100644
index 0000000..20f1451
--- /dev/null
+++ b/sbin/mount_unionfs/mount_unionfs.8
@@ -0,0 +1,216 @@
+.\" Copyright (c) 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software donated to Berkeley by
+.\" Jan-Simon Pendry.
+.\"
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)mount_union.8 8.6 (Berkeley) 3/27/94
+.\" $FreeBSD$
+.\"
+.Dd March 27, 1994
+.Dt MOUNT_UNIONFS 8
+.Os
+.Sh NAME
+.Nm mount_unionfs
+.Nd mount union file systems
+.Sh SYNOPSIS
+.Nm
+.Op Fl br
+.Op Fl o Ar options
+.Ar directory
+.Ar uniondir
+.Sh DESCRIPTION
+The
+.Nm
+utility attaches
+.Ar directory
+above
+.Ar uniondir
+in such a way that the contents of both directory trees remain visible.
+By default,
+.Ar directory
+becomes the
+.Em upper
+layer and
+.Ar uniondir
+becomes the
+.Em lower
+layer.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl b
+Invert the default position, so that
+.Ar directory
+becomes the lower layer and
+.Ar uniondir
+becomes the upper layer.
+However,
+.Ar uniondir
+remains the mount point.
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+.It Fl r
+Hide the lower layer completely in the same way as mounting with
+.Xr mount_nullfs 8 .
+.El
+.Pp
+To enforce file system security, the user mounting the file system
+must be superuser or else have write permission on the mounted-on
+directory.
+In addition, the
+.Va vfs.usermount
+.Xr sysctl 8
+variable must be set to 1 to permit file system mounting by ordinary users.
+.Pp
+Filenames are looked up in the upper layer and then in the
+lower layer.
+If a directory is found in the lower layer, and there is no entry
+in the upper layer, then a
+.Em shadow
+directory will be created in the upper layer.
+It will be owned by the user who originally did the union mount,
+with mode
+.Dq rwxrwxrwx
+(0777) modified by the umask in effect at that time.
+.Pp
+If a file exists in the upper layer then there is no way to access
+a file with the same name in the lower layer.
+If necessary, a combination of loopback and union mounts can be made
+which will still allow the lower files to be accessed by a different
+pathname.
+.Pp
+Except in the case of a directory,
+access to an object is granted via the normal file system access checks.
+For directories, the current user must have access to both the upper
+and lower directories (should they both exist).
+.Pp
+Requests to create or modify objects in
+.Ar uniondir
+are passed to the upper layer with the exception of a few special cases.
+An attempt to open for writing a file which exists in the lower layer
+causes a copy of the
+.Em entire
+file to be made to the upper layer, and then for the upper layer copy
+to be opened.
+Similarly, an attempt to truncate a lower layer file to zero length
+causes an empty file to be created in the upper layer.
+Any other operation which would ultimately require modification to
+the lower layer fails with
+.Er EROFS .
+.Pp
+The union file system manipulates the namespace, rather than
+individual file systems.
+The union operation applies recursively down the directory tree
+now rooted at
+.Ar uniondir .
+Thus any file systems which are mounted under
+.Ar uniondir
+will take part in the union operation.
+This differs from the
+.Em union
+option to
+.Xr mount 8
+which only applies the union operation to the mount point itself,
+and then only for lookups.
+.Sh EXAMPLES
+The commands
+.Bd -literal -offset indent
+mount -t cd9660 -o ro /dev/cd0a /usr/src
+mount -t unionfs /var/obj /usr/src
+.Ed
+.Pp
+mount the CD-ROM drive
+.Pa /dev/cd0a
+on
+.Pa /usr/src
+and then attaches
+.Pa /var/obj
+on top.
+For most purposes the effect of this is to make the
+source tree appear writable
+even though it is stored on a CD-ROM.
+.Pp
+The command
+.Bd -literal -offset indent
+mount -t unionfs -o -b /sys $HOME/sys
+.Ed
+.Pp
+attaches the system source tree below the
+.Pa sys
+directory in the user's home directory.
+This allows individual users to make private changes
+to the source, and build new kernels, without those
+changes becoming visible to other users.
+Note that the files in the lower layer remain
+accessible via
+.Pa /sys .
+.Sh SEE ALSO
+.Xr intro 2 ,
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8 ,
+.Xr mount_nullfs 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
+It first worked in
+.Fx Ns -(fill this in) .
+.Sh BUGS
+THIS FILE SYSTEM TYPE IS NOT YET FULLY SUPPORTED (READ: IT DOESN'T WORK)
+AND USING IT MAY, IN FACT, DESTROY DATA ON YOUR SYSTEM.
+USE AT YOUR
+OWN RISK.
+BEWARE OF DOG.
+SLIPPERY WHEN WET.
+.Pp
+This code also needs an owner in order to be less dangerous - serious
+hackers can apply by sending mail to
+.Aq hackers@FreeBSD.org
+and announcing
+their intent to take it over.
+.Pp
+Without whiteout support from the file system backing the upper layer,
+there is no way that delete and rename operations on lower layer
+objects can be done.
+.Er EROFS
+is returned for this kind of operations along with any others
+which would make modifications to the lower layer, such as
+.Xr chmod 1 .
+.Pp
+Running
+.Xr find 1
+over a union tree has the side-effect of creating
+a tree of shadow directories in the upper layer.
diff --git a/sbin/mount_unionfs/mount_unionfs.c b/sbin/mount_unionfs/mount_unionfs.c
new file mode 100644
index 0000000..7b619ea
--- /dev/null
+++ b/sbin/mount_unionfs/mount_unionfs.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mount_union.c 8.5 (Berkeley) 3/27/94";
+#else
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/uio.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_END
+};
+
+static int subdir(const char *, const char *);
+static void usage (void) __dead2;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct iovec iov[8];
+ int ch, mntflags;
+ char source[MAXPATHLEN];
+ char target[MAXPATHLEN];
+ int iovcnt;
+
+ iovcnt = 6;
+ mntflags = 0;
+ while ((ch = getopt(argc, argv, "bo:r")) != -1)
+ switch (ch) {
+ case 'b':
+ iov[6].iov_base = "below";
+ iov[6].iov_len = strlen(iov[6].iov_base) + 1;
+ iov[7].iov_base = NULL;
+ iov[7].iov_len = 0;
+ iovcnt = 8;
+ break;
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case 'r':
+ iov[6].iov_base = "replace";
+ iov[6].iov_len = strlen(iov[6].iov_base) + 1;
+ iov[7].iov_base = NULL;
+ iov[7].iov_len = 0;
+ iovcnt = 8;
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ /* resolve both target and source with realpath(3) */
+ (void)checkpath(argv[0], target);
+ (void)checkpath(argv[1], source);
+
+ if (subdir(target, source) || subdir(source, target))
+ errx(EX_USAGE, "%s (%s) and %s (%s) are not distinct paths",
+ argv[0], target, argv[1], source);
+
+ iov[0].iov_base = "fstype";
+ iov[0].iov_len = strlen(iov[0].iov_base) + 1;
+ iov[1].iov_base = "unionfs";
+ iov[1].iov_len = strlen(iov[1].iov_base) + 1;
+ iov[2].iov_base = "fspath";
+ iov[2].iov_len = strlen(iov[2].iov_base) + 1;
+ iov[3].iov_base = source;
+ iov[3].iov_len = strlen(source) + 1;
+ iov[4].iov_base = "target";
+ iov[4].iov_len = strlen(iov[4].iov_base) + 1;
+ iov[5].iov_base = target;
+ iov[5].iov_len = strlen(target) + 1;
+ if (nmount(iov, iovcnt, mntflags))
+ err(EX_OSERR, "%s", target);
+ exit(0);
+}
+
+int
+subdir(p, dir)
+ const char *p;
+ const char *dir;
+{
+ int l;
+
+ l = strlen(dir);
+ if (l <= 1)
+ return (1);
+
+ if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0'))
+ return (1);
+
+ return (0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_unionfs [-br] [-o options] directory uniondir\n");
+ exit(EX_USAGE);
+}
diff --git a/sbin/natd/HISTORY b/sbin/natd/HISTORY
new file mode 100644
index 0000000..f929e80
--- /dev/null
+++ b/sbin/natd/HISTORY
@@ -0,0 +1,146 @@
+* Version 0.1
+
+ Initial version of natd.
+
+* Version 0.2
+
+ - Alias address can now be set by giving interface name with
+ new (-n) command-line option.
+
+ - New Makefile based on bsd.prog.mk.
+
+ - Error messages are written to syslog
+ after natd has become a daemon.
+
+* Version 1.0
+
+ - Support for using only single socket (-p option)
+
+* Version 1.1
+
+ - -a option now understands a hostname also.
+ - -a option no longer dumps core.
+ - Packet aliasing software upgraded to v. 1.9
+ - added long option names (like -address)
+
+* Version 1.2
+
+ - Fixed core dump with -port option.
+ - Added -Wall to CFLAGS and some headers added to natd.c
+ to get clean compile by Brian Somers [brian@awfulhak.org].
+
+* Version 1.3
+
+ - Aliasing address initialization is delayed until first
+ packet arrives. This allows natd to start up before
+ interface address is set.
+ - SIGTERM is now catched to allow kernel to close
+ existing connections when system is shutting down.
+ - SIGHUP is now catched to allow natd to refresh aliasing
+ address from interface, which might be useful to tun devices.
+
+* Version 1.4
+
+ - Changed command line options to be compatible with
+ command names used in ppp+packetAlias package (which is the
+ original application using aliasing routines).
+
+ The options which map directly to packet aliasing options are:
+
+ -unregistered_only [yes|no]
+ -log [yes|no]
+ -deny_incoming [yes|no]
+ -use_sockets [yes|no]
+ -same_ports [yes|no]
+
+ The short option names are the same as in previous
+ releases.
+
+ - Command line parser rewritten to provide more flexible
+ way to support new packet aliasing options.
+
+ - Support for natd.cf configuration file has been added.
+
+ - SIGHUP no longer causes problems when running without
+ interface name option.
+
+ - When using -interface command line option, routing socket
+ is optionally listened for interface address changes. This
+ mode is activated by -dynamic option.
+
+ - Directory tree reorganized, alias package is now a library.
+
+ - Manual page written by Brian Somers <brian@awfulhak.org> added.
+ - README file updated.
+
+* Version 1.5
+
+ - Support for sending ICMP 'need fragmentation' messages
+ when packet size exceeds mtu size of outgoing network interface.
+
+ - ipfw rule example in manual page fixed.
+
+* Version 1.6
+
+ - Upgrade to new packet aliasing engine (2.1)
+ - redirect_port and redirect_address configuration
+ parameters added.
+ - It is no longer necessary to quote complex parameter values
+ in command line.
+ - Manual page fixed (same_port -> same_ports).
+
+* Version 1.7
+
+ - A bug in command-line parsing fixed (it appeared due
+ to changes made in 1.6).
+
+* Version 1.8
+
+ - Fixed problems with -dynamic option.
+ - Added /var/run/natd.pid
+
+* Version 1.9
+
+ - Changes to manual page by
+ Brian Somers <brian@awfulhak.org> integrated.
+ - Checksum for incoming packets is always recalculated
+ for FreeBSD 2.2 and never recalculated for newer
+ versions. This should fix the problem with wrong
+ checksum of fragmented packets.
+ - Buffer space problem found by Sergio Lenzi <lenzi@bsi.com.br>
+ fixed. Natd now waits with select(2) for buffer space
+ to become available if write fails.
+ - Packet aliasing library upgraded to 2.2.
+
+* Version 1.10
+
+ - Ignored incoming packets are now dropped when
+ deny_incoming option is set to yes.
+ - Packet aliasing library upgraded to 2.4.
+
+* Version 1.11
+
+ - Code cleanup work done in FreeBSD-current development merged.
+ - Port numbers are now unsigned as they should always have been.
+
+* Version 1.12
+
+ - Typos in comment fixed. Copyright message added to
+ source & header files that were missing it.
+ - A small patch to libalias to make static NAT work correctly.
+
+* Version 2.0
+
+ - Upgrade to libalias 3.0 which gives:
+ - Transparent proxy support.
+ - permanent_link is now obsolete, use redirect_port instead.
+ - Drop support for early FreeBSD 2.2 versions
+ - If separate input & output sockets are being used
+ use them to find out packet direction instead of
+ normal mechanism. This can be handy in complex environments
+ with multiple interfaces.
+ - libalias is no longer part of this distribution.
+ - New sample configuration file
+ from Ted Mittelstaedt <tedm@portsoft.com>.
+ - PPTP redirect support by Dru Nelson <dnelson@redwoodsoft.com> added.
+ - Logging enhancements from Martin Machacek <mm@i.cz> added.
diff --git a/sbin/natd/Makefile b/sbin/natd/Makefile
new file mode 100644
index 0000000..c6bf326
--- /dev/null
+++ b/sbin/natd/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG = natd
+SRCS = natd.c icmp.c
+WARNS?= 0
+LDADD = -lalias
+DPADD = ${LIBALIAS}
+MAN = natd.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/natd/README b/sbin/natd/README
new file mode 100644
index 0000000..d2e8a9a
--- /dev/null
+++ b/sbin/natd/README
@@ -0,0 +1,50 @@
+# $FreeBSD$
+
+ A Network Address Translation Daemon for FreeBSD
+
+
+1. WHAT IS NATD ?
+
+ This is a simple daemon based on FreeBSD divert sockets
+ which performs network address translation (or masquerading)
+ for IP packets (see related RFCs 1631 and 1918).
+ It is based on packet aliasing package (see README.alias)
+ written by Charles Mott <cm@linktel.net>.
+
+ This package works with any network interface (doesn't have
+ to be ppp). I run it on a computer having two ethernet cards,
+ one connected to internet and the other one to local network.
+
+2. GETTING IT RUNNING
+
+ 1) Get FreeBSD 2.2 - I think the divert sockets are
+ not available on earlier versions,
+
+ 2) Compile this software by executing "make".
+
+ 3) Install the software by executing "make install".
+
+ 4) See man natd for further instructions.
+
+3. FTP SITES FOR NATD
+
+ This package is available at ftp://ftp.suutari.iki.fi/pub/natd.
+
+4. AUTHORS
+
+ This program is the result of the efforts of many people
+ at different times:
+
+ Archie Cobbs <archie@whistle.com> Divert sockets
+ Charles Mott <cm@linktel.net> Packet aliasing engine
+ Eivind Eklund <eivind@dimaga.com> Packet aliasing engine
+ Ari Suutari <suutari@iki.fi> Natd
+ Brian Somers <brian@awfulhak.org> Manual page, glue and
+ bunch of good ideas.
+
+ Happy Networking - comments and fixes are welcome!
+
+ Ari S. (suutari@iki.fi)
+
+
+
diff --git a/sbin/natd/icmp.c b/sbin/natd/icmp.c
new file mode 100644
index 0000000..1509b96
--- /dev/null
+++ b/sbin/natd/icmp.c
@@ -0,0 +1,127 @@
+/*
+ * natd - Network Address Translation Daemon for FreeBSD.
+ *
+ * This software is provided free of charge, with no
+ * warranty of any kind, either expressed or implied.
+ * Use at your own risk.
+ *
+ * You may copy, modify and distribute this software (icmp.c) freely.
+ *
+ * Ari Suutari <suutari@iki.fi>
+ *
+ * $FreeBSD$
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <netdb.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <machine/in_cksum.h>
+
+#include <alias.h>
+
+#include "natd.h"
+
+int SendNeedFragIcmp (int sock, struct ip* failedDgram, int mtu)
+{
+ char icmpBuf[IP_MAXPACKET];
+ struct ip* ip;
+ struct icmp* icmp;
+ int icmpLen;
+ int failBytes;
+ int failHdrLen;
+ struct sockaddr_in addr;
+ int wrote;
+ struct in_addr swap;
+/*
+ * Don't send error if packet is
+ * not the first fragment.
+ */
+ if (ntohs (failedDgram->ip_off) & ~(IP_MF | IP_DF))
+ return 0;
+/*
+ * Dont respond if failed datagram is ICMP.
+ */
+ if (failedDgram->ip_p == IPPROTO_ICMP)
+ return 0;
+/*
+ * Start building the message.
+ */
+ ip = (struct ip*) icmpBuf;
+ icmp = (struct icmp*) (icmpBuf + sizeof (struct ip));
+/*
+ * Complete ICMP part.
+ */
+ icmp->icmp_type = ICMP_UNREACH;
+ icmp->icmp_code = ICMP_UNREACH_NEEDFRAG;
+ icmp->icmp_cksum = 0;
+ icmp->icmp_void = 0;
+ icmp->icmp_nextmtu = htons (mtu);
+/*
+ * Copy header + 64 bits of original datagram.
+ */
+ failHdrLen = (failedDgram->ip_hl << 2);
+ failBytes = failedDgram->ip_len - failHdrLen;
+ if (failBytes > 8)
+ failBytes = 8;
+
+ failBytes += failHdrLen;
+ icmpLen = ICMP_MINLEN + failBytes;
+
+ memcpy (&icmp->icmp_ip, failedDgram, failBytes);
+/*
+ * Calculate checksum.
+ */
+ icmp->icmp_cksum = LibAliasInternetChecksum (mla, (u_short*) icmp,
+ icmpLen);
+/*
+ * Add IP header using old IP header as template.
+ */
+ memcpy (ip, failedDgram, sizeof (struct ip));
+
+ ip->ip_v = 4;
+ ip->ip_hl = 5;
+ ip->ip_len = htons (sizeof (struct ip) + icmpLen);
+ ip->ip_p = IPPROTO_ICMP;
+ ip->ip_tos = 0;
+
+ swap = ip->ip_dst;
+ ip->ip_dst = ip->ip_src;
+ ip->ip_src = swap;
+
+ LibAliasIn (mla, (char*) ip, IP_MAXPACKET);
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr = ip->ip_dst;
+ addr.sin_port = 0;
+/*
+ * Put packet into processing queue.
+ */
+ wrote = sendto (sock,
+ icmp,
+ icmpLen,
+ 0,
+ (struct sockaddr*) &addr,
+ sizeof addr);
+
+ if (wrote != icmpLen)
+ Warn ("Cannot send ICMP message.");
+
+ return 1;
+}
+
+
diff --git a/sbin/natd/natd.8 b/sbin/natd/natd.8
new file mode 100644
index 0000000..254948a
--- /dev/null
+++ b/sbin/natd/natd.8
@@ -0,0 +1,640 @@
+.\" $FreeBSD$
+.Dd February 28, 2003
+.Dt NATD 8
+.Os
+.Sh NAME
+.Nm natd
+.Nd Network Address Translation daemon
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Op Fl unregistered_only | u
+.Op Fl log | l
+.Op Fl proxy_only
+.Op Fl reverse
+.Op Fl deny_incoming | d
+.Op Fl use_sockets | s
+.Op Fl same_ports | m
+.Op Fl verbose | v
+.Op Fl dynamic
+.Op Fl in_port | i Ar port
+.Op Fl out_port | o Ar port
+.Op Fl port | p Ar port
+.Op Fl alias_address | a Ar address
+.Op Fl target_address | t Ar address
+.Op Fl interface | n Ar interface
+.Op Fl proxy_rule Ar proxyspec
+.Op Fl redirect_port Ar linkspec
+.Op Fl redirect_proto Ar linkspec
+.Op Fl redirect_address Ar linkspec
+.Op Fl config | f Ar configfile
+.Op Fl log_denied
+.Op Fl log_facility Ar facility_name
+.Op Fl punch_fw Ar firewall_range
+.Op Fl skinny_port Ar port
+.Op Fl log_ipfw_denied
+.Op Fl pid_file | P Ar pidfile
+.Ek
+.Sh DESCRIPTION
+The
+.Nm
+utility provides a Network Address Translation facility for use
+with
+.Xr divert 4
+sockets under
+.Fx .
+.Pp
+(If you need NAT on a PPP link,
+.Xr ppp 8
+provides the
+.Fl nat
+option that gives most of the
+.Nm
+functionality, and uses the same
+.Xr libalias 3
+library.)
+.Pp
+The
+.Nm
+utility normally runs in the background as a daemon.
+It is passed raw IP packets as they travel into and out of the machine,
+and will possibly change these before re-injecting them back into the
+IP packet stream.
+.Pp
+It changes all packets destined for another host so that their source
+IP address is that of the current machine.
+For each packet changed in this manner, an internal table entry is
+created to record this fact.
+The source port number is also changed to indicate the table entry
+applying to the packet.
+Packets that are received with a target IP of the current host are
+checked against this internal table.
+If an entry is found, it is used to determine the correct target IP
+address and port to place in the packet.
+.Pp
+The following command line options are available:
+.Bl -tag -width Fl
+.It Fl log | l
+Log various aliasing statistics and information to the file
+.Pa /var/log/alias.log .
+This file is truncated each time
+.Nm
+is started.
+.It Fl deny_incoming | d
+Do not pass incoming packets that have no
+entry in the internal translation table.
+.Pp
+If this option is not used, then such a packet will be altered
+using the rules in
+.Fl target_address
+below, and the entry will be made in the internal translation table.
+.It Fl log_denied
+Log denied incoming packets via
+.Xr syslog 3
+(see also
+.Fl log_facility ) .
+.It Fl log_facility Ar facility_name
+Use specified log facility when logging information via
+.Xr syslog 3 .
+Argument
+.Ar facility_name
+is one of the keywords specified in
+.Xr syslog.conf 5 .
+.It Fl use_sockets | s
+Allocate a
+.Xr socket 2
+in order to establish an FTP data or IRC DCC send connection.
+This option uses more system resources, but guarantees successful
+connections when port numbers conflict.
+.It Fl same_ports | m
+Try to keep the same port number when altering outgoing packets.
+With this option, protocols such as RPC will have a better chance
+of working.
+If it is not possible to maintain the port number, it will be silently
+changed as per normal.
+.It Fl verbose | v
+Do not call
+.Xr daemon 3
+on startup.
+Instead, stay attached to the controlling terminal and display all packet
+alterations to the standard output.
+This option should only be used for debugging purposes.
+.It Fl unregistered_only | u
+Only alter outgoing packets with an
+.Em unregistered
+source address.
+According to RFC 1918, unregistered source addresses are 10.0.0.0/8,
+172.16.0.0/12 and 192.168.0.0/16.
+.It Fl redirect_port Ar proto Xo
+.Ar targetIP Ns : Ns Xo
+.Ar targetPORT Ns Op - Ns Ar targetPORT Xc
+.Op Ar aliasIP Ns : Ns Xo
+.Ar aliasPORT Ns Op - Ns Ar aliasPORT Xc
+.Oo Ar remoteIP Ns Oo : Ns
+.Ar remotePORT Ns Op - Ns Ar remotePORT
+.Oc Oc
+.Xc
+Redirect incoming connections arriving to given port(s) to another host
+and port(s).
+Argument
+.Ar proto
+is either
+.Ar tcp
+or
+.Ar udp ,
+.Ar targetIP
+is the desired target IP address,
+.Ar targetPORT
+is the desired target port number or range,
+.Ar aliasPORT
+is the requested port number or range, and
+.Ar aliasIP
+is the aliasing address.
+Arguments
+.Ar remoteIP
+and
+.Ar remotePORT
+can be used to specify the connection more accurately if necessary.
+If
+.Ar remotePORT
+is not specified, it is assumed to be all ports.
+.Pp
+Arguments
+.Ar targetIP , aliasIP
+and
+.Ar remoteIP
+can be given as IP addresses or as hostnames.
+The
+.Ar targetPORT , aliasPORT
+and
+.Ar remotePORT
+ranges need not be the same numerically, but must have the same size.
+When
+.Ar targetPORT , aliasPORT
+or
+.Ar remotePORT
+specifies a singular value (not a range), it can be given as a service
+name that is searched for in the
+.Xr services 5
+database.
+.Pp
+For example, the argument
+.Pp
+.Dl Ar tcp inside1:telnet 6666
+.Pp
+means that incoming TCP packets destined for port 6666 on this machine
+will be sent to the telnet port on the inside1 machine.
+.Pp
+.Dl Ar tcp inside2:2300-2399 3300-3399
+.Pp
+will redirect incoming connections on ports 3300-3399 to host
+inside2, ports 2300-2399.
+The mapping is 1:1 meaning port 3300 maps to 2300, 3301 maps to 2301, etc.
+.It Fl redirect_proto Ar proto localIP Oo
+.Ar publicIP Op Ar remoteIP
+.Oc
+Redirect incoming IP packets of protocol
+.Ar proto
+(see
+.Xr protocols 5 )
+destined for
+.Ar publicIP
+address to a
+.Ar localIP
+address and vice versa.
+.Pp
+If
+.Ar publicIP
+is not specified, then the default aliasing address is used.
+If
+.Ar remoteIP
+is specified, then only packets coming from/to
+.Ar remoteIP
+will match the rule.
+.It Fl redirect_address Ar localIP publicIP
+Redirect traffic for public IP address to a machine on the local
+network.
+This function is known as
+.Em static NAT .
+Normally static NAT is useful if your ISP has allocated a small block
+of IP addresses to you, but it can even be used in the case of single
+address:
+.Pp
+.Dl Ar redirect_address 10.0.0.8 0.0.0.0
+.Pp
+The above command would redirect all incoming traffic
+to machine 10.0.0.8.
+.Pp
+If several address aliases specify the same public address
+as follows
+.Bd -literal -offset indent
+redirect_address 192.168.0.2 public_addr
+redirect_address 192.168.0.3 public_addr
+redirect_address 192.168.0.4 public_addr
+.Ed
+.Pp
+the incoming traffic will be directed to the last
+translated local address (192.168.0.4), but outgoing
+traffic from the first two addresses will still be aliased
+to appear from the specified
+.Ar public_addr .
+.It Fl redirect_port Ar proto Xo
+.Ar targetIP Ns : Ns Xo
+.Ar targetPORT Ns Oo , Ns
+.Ar targetIP Ns : Ns Xo
+.Ar targetPORT Ns Oo , Ns
+.Ar ...\&
+.Oc Oc
+.Xc
+.Xc
+.Op Ar aliasIP Ns : Ns Xo
+.Ar aliasPORT
+.Xc
+.Oo Ar remoteIP Ns
+.Op : Ns Ar remotePORT
+.Oc
+.Xc
+.It Fl redirect_address Xo
+.Ar localIP Ns Oo , Ns
+.Ar localIP Ns Oo , Ns
+.Ar ...\&
+.Oc Oc
+.Ar publicIP
+.Xc
+These forms of
+.Fl redirect_port
+and
+.Fl redirect_address
+are used to transparently offload network load on a single server and
+distribute the load across a pool of servers.
+This function is known as
+.Em LSNAT
+(RFC 2391).
+For example, the argument
+.Pp
+.Dl Ar tcp www1:http,www2:http,www3:http www:http
+.Pp
+means that incoming HTTP requests for host www will be transparently
+redirected to one of the www1, www2 or www3, where a host is selected
+simply on a round-robin basis, without regard to load on the net.
+.It Fl dynamic
+If the
+.Fl n
+or
+.Fl interface
+option is used,
+.Nm
+will monitor the routing socket for alterations to the
+.Ar interface
+passed.
+If the interface's IP address is changed,
+.Nm
+will dynamically alter its concept of the alias address.
+.It Fl in_port | i Ar port
+Read from and write to
+.Xr divert 4
+port
+.Ar port ,
+treating all packets as
+.Dq incoming .
+.It Fl out_port | o Ar port
+Read from and write to
+.Xr divert 4
+port
+.Ar port ,
+treating all packets as
+.Dq outgoing .
+.It Fl port | p Ar port
+Read from and write to
+.Xr divert 4
+port
+.Ar port ,
+distinguishing packets as
+.Dq incoming
+or
+.Dq outgoing
+using the rules specified in
+.Xr divert 4 .
+If
+.Ar port
+is not numeric, it is searched for in the
+.Xr services 5
+database.
+If this option is not specified, the divert port named
+.Ar natd
+will be used as a default.
+.It Fl alias_address | a Ar address
+Use
+.Ar address
+as the aliasing address.
+Either this or the
+.Fl interface
+option must be used (but not both),
+if the
+.Fl proxy_only
+option is not specified.
+The specified address is usually the address assigned to the
+.Dq public
+network interface.
+.Pp
+All data passing
+.Em out
+will be rewritten with a source address equal to
+.Ar address .
+All data coming
+.Em in
+will be checked to see if it matches any already-aliased outgoing
+connection.
+If it does, the packet is altered accordingly.
+If not, all
+.Fl redirect_port ,
+.Fl redirect_proto
+and
+.Fl redirect_address
+assignments are checked and actioned.
+If no other action can be made and if
+.Fl deny_incoming
+is not specified, the packet is delivered to the local machine
+using the rules specified in
+.Fl target_address
+option below.
+.It Fl t | target_address Ar address
+Set the target address.
+When an incoming packet not associated with any pre-existing link
+arrives at the host machine, it will be sent to the specified
+.Ar address .
+.Pp
+The target address may be set to
+.Ar 255.255.255.255 ,
+in which case all new incoming packets go to the alias address set by
+.Fl alias_address
+or
+.Fl interface .
+.Pp
+If this option is not used, or called with the argument
+.Ar 0.0.0.0 ,
+then all new incoming packets go to the address specified in
+the packet.
+This allows external machines to talk directly to internal machines if
+they can route packets to the machine in question.
+.It Fl interface | n Ar interface
+Use
+.Ar interface
+to determine the aliasing address.
+If there is a possibility that the IP address associated with
+.Ar interface
+may change, the
+.Fl dynamic
+option should also be used.
+If this option is not specified, the
+.Fl alias_address
+option must be used.
+.Pp
+The specified
+.Ar interface
+is usually the
+.Dq public
+(or
+.Dq external )
+network interface.
+.It Fl config | f Ar file
+Read configuration from
+.Ar file .
+A
+.Ar file
+should contain a list of options, one per line, in the same form
+as the long form of the above command line options.
+For example, the line
+.Pp
+.Dl alias_address 158.152.17.1
+.Pp
+would specify an alias address of 158.152.17.1.
+Options that do not take an argument are specified with an argument of
+.Ar yes
+or
+.Ar no
+in the configuration file.
+For example, the line
+.Pp
+.Dl log yes
+.Pp
+is synonymous with
+.Fl log .
+.Pp
+Trailing spaces and empty lines are ignored.
+A
+.Ql \&#
+sign will mark the rest of the line as a comment.
+.It Fl reverse
+This option makes
+.Nm
+reverse the way it handles
+.Dq incoming
+and
+.Dq outgoing
+packets, allowing it to operate on the
+.Dq internal
+network interface rather than the
+.Dq external
+one.
+.Pp
+This can be useful in some transparent proxying situations
+when outgoing traffic is redirected to the local machine
+and
+.Nm
+is running on the internal interface (it usually runs on the
+external interface).
+.It Fl proxy_only
+Force
+.Nm
+to perform transparent proxying only.
+Normal address translation is not performed.
+.It Fl proxy_rule Xo
+.Op Ar type encode_ip_hdr | encode_tcp_stream
+.Ar port xxxx
+.Ar server a.b.c.d:yyyy
+.Xc
+Enable transparent proxying.
+Outgoing TCP packets with the given port going through this
+host to any other host are redirected to the given server and port.
+Optionally, the original target address can be encoded into the packet.
+Use
+.Ar encode_ip_hdr
+to put this information into the IP option field or
+.Ar encode_tcp_stream
+to inject the data into the beginning of the TCP stream.
+.It Fl punch_fw Xo
+.Ar basenumber Ns : Ns Ar count
+.Xc
+This option directs
+.Nm
+to
+.Dq punch holes
+in an
+.Xr ipfirewall 4
+based firewall for FTP/IRC DCC connections.
+This is done dynamically by installing temporary firewall rules which
+allow a particular connection (and only that connection) to go through
+the firewall.
+The rules are removed once the corresponding connection terminates.
+.Pp
+A maximum of
+.Ar count
+rules starting from the rule number
+.Ar basenumber
+will be used for punching firewall holes.
+The range will be cleared for all rules on startup.
+.It Fl skinny_port Ar port
+This option allows you to specify the TCP port used for
+the Skinny Station protocol.
+Skinny is used by Cisco IP phones to communicate with
+Cisco Call Managers to set up voice over IP calls.
+By default, Skinny aliasing is not performed.
+The typical port value for Skinny is 2000.
+.It Fl log_ipfw_denied
+Log when a packet cannot be re-injected because an
+.Xr ipfw 8
+rule blocks it.
+This is the default with
+.Fl verbose .
+.It Fl pid_file | P Ar file
+Specify an alternate file in which to store the process ID.
+The default is
+.Pa /var/run/natd.pid .
+.El
+.Sh RUNNING NATD
+The following steps are necessary before attempting to run
+.Nm :
+.Bl -enum
+.It
+Build a custom kernel with the following options:
+.Bd -literal -offset indent
+options IPFIREWALL
+options IPDIVERT
+.Ed
+.Pp
+Refer to the handbook for detailed instructions on building a custom
+kernel.
+.It
+Ensure that your machine is acting as a gateway.
+This can be done by specifying the line
+.Pp
+.Dl gateway_enable=YES
+.Pp
+in the
+.Pa /etc/rc.conf
+file or using the command
+.Pp
+.Dl "sysctl net.inet.ip.forwarding=1"
+.Pp
+.It
+If you use the
+.Fl interface
+option, make sure that your interface is already configured.
+If, for example, you wish to specify
+.Ql tun0
+as your
+.Ar interface ,
+and you are using
+.Xr ppp 8
+on that interface, you must make sure that you start
+.Nm ppp
+prior to starting
+.Nm .
+.El
+.Pp
+Running
+.Nm
+is fairly straight forward.
+The line
+.Pp
+.Dl natd -interface ed0
+.Pp
+should suffice in most cases (substituting the correct interface name).
+Please check
+.Xr rc.conf 5
+on how to configure it to be started automatically during boot.
+Once
+.Nm
+is running, you must ensure that traffic is diverted to
+.Nm :
+.Bl -enum
+.It
+You will need to adjust the
+.Pa /etc/rc.firewall
+script to taste.
+If you are not interested in having a firewall, the
+following lines will do:
+.Bd -literal -offset indent
+/sbin/ipfw -f flush
+/sbin/ipfw add divert natd all from any to any via ed0
+/sbin/ipfw add pass all from any to any
+.Ed
+.Pp
+The second line depends on your interface (change
+.Ql ed0
+as appropriate).
+.Pp
+You should be aware of the fact that, with these firewall settings,
+everyone on your local network can fake his source-address using your
+host as gateway.
+If there are other hosts on your local network, you are strongly
+encouraged to create firewall rules that only allow traffic to and
+from trusted hosts.
+.Pp
+If you specify real firewall rules, it is best to specify line 2 at
+the start of the script so that
+.Nm
+sees all packets before they are dropped by the firewall.
+.Pp
+After translation by
+.Nm ,
+packets re-enter the firewall at the rule number following the rule number
+that caused the diversion (not the next rule if there are several at the
+same number).
+.It
+Enable your firewall by setting
+.Pp
+.Dl firewall_enable=YES
+.Pp
+in
+.Pa /etc/rc.conf .
+This tells the system startup scripts to run the
+.Pa /etc/rc.firewall
+script.
+If you do not wish to reboot now, just run this by hand from the console.
+NEVER run this from a remote session unless you put it into the background.
+If you do, you will lock yourself out after the flush takes place, and
+execution of
+.Pa /etc/rc.firewall
+will stop at this point - blocking all accesses permanently.
+Running the script in the background should be enough to prevent this
+disaster.
+.El
+.Sh SEE ALSO
+.Xr libalias 3 ,
+.Xr divert 4 ,
+.Xr protocols 5 ,
+.Xr rc.conf 5 ,
+.Xr services 5 ,
+.Xr syslog.conf 5 ,
+.Xr ipfw 8 ,
+.Xr ppp 8
+.Sh AUTHORS
+This program is the result of the efforts of many people at different
+times:
+.Pp
+.An Archie Cobbs Aq archie@FreeBSD.org
+(divert sockets)
+.An Charles Mott Aq cm@linktel.net
+(packet aliasing)
+.An Eivind Eklund Aq perhaps@yes.no
+(IRC support & misc additions)
+.An Ari Suutari Aq suutari@iki.fi
+(natd)
+.An Dru Nelson Aq dnelson@redwoodsoft.com
+(early PPTP support)
+.An Brian Somers Aq brian@awfulhak.org
+(glue)
+.An Ruslan Ermilov Aq ru@FreeBSD.org
+(natd, packet aliasing, glue)
diff --git a/sbin/natd/natd.c b/sbin/natd/natd.c
new file mode 100644
index 0000000..e7c3600
--- /dev/null
+++ b/sbin/natd/natd.c
@@ -0,0 +1,1961 @@
+/*
+ * natd - Network Address Translation Daemon for FreeBSD.
+ *
+ * This software is provided free of charge, with no
+ * warranty of any kind, either expressed or implied.
+ * Use at your own risk.
+ *
+ * You may copy, modify and distribute this software (natd.c) freely.
+ *
+ * Ari Suutari <suutari@iki.fi>
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define SYSLOG_NAMES
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <machine/in_cksum.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip_icmp.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <arpa/inet.h>
+
+#include <alias.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "natd.h"
+
+struct instance {
+ const char *name;
+ struct libalias *la;
+ LIST_ENTRY(instance) list;
+
+ int ifIndex;
+ int assignAliasAddr;
+ char* ifName;
+ int logDropped;
+ u_short inPort;
+ u_short outPort;
+ u_short inOutPort;
+ struct in_addr aliasAddr;
+ int ifMTU;
+ int aliasOverhead;
+ int dropIgnoredIncoming;
+ int divertIn;
+ int divertOut;
+ int divertInOut;
+};
+
+static LIST_HEAD(, instance) root = LIST_HEAD_INITIALIZER(&root);
+
+struct libalias *mla;
+struct instance *mip;
+int ninstance = 1;
+
+/*
+ * Default values for input and output
+ * divert socket ports.
+ */
+
+#define DEFAULT_SERVICE "natd"
+
+/*
+ * Definition of a port range, and macros to deal with values.
+ * FORMAT: HI 16-bits == first port in range, 0 == all ports.
+ * LO 16-bits == number of ports in range
+ * NOTES: - Port values are not stored in network byte order.
+ */
+
+typedef u_long port_range;
+
+#define GETLOPORT(x) ((x) >> 0x10)
+#define GETNUMPORTS(x) ((x) & 0x0000ffff)
+#define GETHIPORT(x) (GETLOPORT((x)) + GETNUMPORTS((x)))
+
+/* Set y to be the low-port value in port_range variable x. */
+#define SETLOPORT(x,y) ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
+
+/* Set y to be the number of ports in port_range variable x. */
+#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
+
+/*
+ * Function prototypes.
+ */
+
+static void DoAliasing (int fd, int direction);
+static void DaemonMode (void);
+static void HandleRoutingInfo (int fd);
+static void Usage (void);
+static char* FormatPacket (struct ip*);
+static void PrintPacket (struct ip*);
+static void SyslogPacket (struct ip*, int priority, const char *label);
+static void SetAliasAddressFromIfName (const char *ifName);
+static void InitiateShutdown (int);
+static void Shutdown (int);
+static void RefreshAddr (int);
+static void ParseOption (const char* option, const char* parms);
+static void ReadConfigFile (const char* fileName);
+static void SetupPortRedirect (const char* parms);
+static void SetupProtoRedirect(const char* parms);
+static void SetupAddressRedirect (const char* parms);
+static void StrToAddr (const char* str, struct in_addr* addr);
+static u_short StrToPort (const char* str, const char* proto);
+static int StrToPortRange (const char* str, const char* proto, port_range *portRange);
+static int StrToProto (const char* str);
+static int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange);
+static void ParseArgs (int argc, char** argv);
+static void SetupPunchFW(const char *strValue);
+static void SetupSkinnyPort(const char *strValue);
+static void NewInstance(const char *name);
+static void DoGlobal (int fd);
+
+/*
+ * Globals.
+ */
+
+static int verbose;
+static int background;
+static int running;
+static int logFacility;
+
+static int dynamicMode;
+static int icmpSock;
+static int logIpfwDenied;
+static const char* pidName;
+static int routeSock;
+static int globalPort;
+static int divertGlobal;
+
+int main (int argc, char** argv)
+{
+ struct sockaddr_in addr;
+ fd_set readMask;
+ int fdMax;
+/*
+ * Initialize packet aliasing software.
+ * Done already here to be able to alter option bits
+ * during command line and configuration file processing.
+ */
+ NewInstance("default");
+
+/*
+ * Parse options.
+ */
+ verbose = 0;
+ background = 0;
+ running = 1;
+ dynamicMode = 0;
+ logFacility = LOG_DAEMON;
+ logIpfwDenied = -1;
+ pidName = PIDFILE;
+ routeSock = -1;
+ icmpSock = -1;
+ fdMax = -1;
+ divertGlobal = -1;
+
+ ParseArgs (argc, argv);
+/*
+ * Log ipfw(8) denied packets by default in verbose mode.
+ */
+ if (logIpfwDenied == -1)
+ logIpfwDenied = verbose;
+/*
+ * Open syslog channel.
+ */
+ openlog ("natd", LOG_CONS | LOG_PID | (verbose ? LOG_PERROR : 0),
+ logFacility);
+
+ LIST_FOREACH(mip, &root, list) {
+ mla = mip->la;
+/*
+ * If not doing the transparent proxying only,
+ * check that valid aliasing address has been given.
+ */
+ if (mip->aliasAddr.s_addr == INADDR_NONE && mip->ifName == NULL &&
+ !(LibAliasSetMode(mla, 0,0) & PKT_ALIAS_PROXY_ONLY))
+ errx (1, "instance %s: aliasing address not given", mip->name);
+
+ if (mip->aliasAddr.s_addr != INADDR_NONE && mip->ifName != NULL)
+ errx (1, "both alias address and interface "
+ "name are not allowed");
+/*
+ * Check that valid port number is known.
+ */
+ if (mip->inPort != 0 || mip->outPort != 0)
+ if (mip->inPort == 0 || mip->outPort == 0)
+ errx (1, "both input and output ports are required");
+
+ if (mip->inPort == 0 && mip->outPort == 0 && mip->inOutPort == 0)
+ ParseOption ("port", DEFAULT_SERVICE);
+
+/*
+ * Check if ignored packets should be dropped.
+ */
+ mip->dropIgnoredIncoming = LibAliasSetMode (mla, 0, 0);
+ mip->dropIgnoredIncoming &= PKT_ALIAS_DENY_INCOMING;
+/*
+ * Create divert sockets. Use only one socket if -p was specified
+ * on command line. Otherwise, create separate sockets for
+ * outgoing and incoming connnections.
+ */
+ if (mip->inOutPort) {
+
+ mip->divertInOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
+ if (mip->divertInOut == -1)
+ Quit ("Unable to create divert socket.");
+ if (mip->divertInOut > fdMax)
+ fdMax = mip->divertInOut;
+
+ mip->divertIn = -1;
+ mip->divertOut = -1;
+/*
+ * Bind socket.
+ */
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = mip->inOutPort;
+
+ if (bind (mip->divertInOut,
+ (struct sockaddr*) &addr,
+ sizeof addr) == -1)
+ Quit ("Unable to bind divert socket.");
+ }
+ else {
+
+ mip->divertIn = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
+ if (mip->divertIn == -1)
+ Quit ("Unable to create incoming divert socket.");
+ if (mip->divertIn > fdMax)
+ fdMax = mip->divertIn;
+
+
+ mip->divertOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
+ if (mip->divertOut == -1)
+ Quit ("Unable to create outgoing divert socket.");
+ if (mip->divertOut > fdMax)
+ fdMax = mip->divertOut;
+
+ mip->divertInOut = -1;
+
+/*
+ * Bind divert sockets.
+ */
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = mip->inPort;
+
+ if (bind (mip->divertIn,
+ (struct sockaddr*) &addr,
+ sizeof addr) == -1)
+ Quit ("Unable to bind incoming divert socket.");
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = mip->outPort;
+
+ if (bind (mip->divertOut,
+ (struct sockaddr*) &addr,
+ sizeof addr) == -1)
+ Quit ("Unable to bind outgoing divert socket.");
+ }
+/*
+ * Create routing socket if interface name specified and in dynamic mode.
+ */
+ if (mip->ifName) {
+ if (dynamicMode) {
+
+ if (routeSock == -1)
+ routeSock = socket (PF_ROUTE, SOCK_RAW, 0);
+ if (routeSock == -1)
+ Quit ("Unable to create routing info socket.");
+ if (routeSock > fdMax)
+ fdMax = routeSock;
+
+ mip->assignAliasAddr = 1;
+ }
+ else
+ SetAliasAddressFromIfName (mip->ifName);
+ }
+
+ }
+ if (globalPort) {
+
+ divertGlobal = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
+ if (divertGlobal == -1)
+ Quit ("Unable to create divert socket.");
+ if (divertGlobal > fdMax)
+ fdMax = divertGlobal;
+
+/*
+* Bind socket.
+*/
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = globalPort;
+
+ if (bind (divertGlobal,
+ (struct sockaddr*) &addr,
+ sizeof addr) == -1)
+ Quit ("Unable to bind global divert socket.");
+ }
+/*
+ * Create socket for sending ICMP messages.
+ */
+ icmpSock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (icmpSock == -1)
+ Quit ("Unable to create ICMP socket.");
+
+/*
+ * And disable reads for the socket, otherwise it slowly fills
+ * up with received icmps which we do not use.
+ */
+ shutdown(icmpSock, SHUT_RD);
+
+/*
+ * Become a daemon unless verbose mode was requested.
+ */
+ if (!verbose)
+ DaemonMode ();
+/*
+ * Catch signals to manage shutdown and
+ * refresh of interface address.
+ */
+ siginterrupt(SIGTERM, 1);
+ siginterrupt(SIGHUP, 1);
+ signal (SIGTERM, InitiateShutdown);
+ signal (SIGHUP, RefreshAddr);
+/*
+ * Set alias address if it has been given.
+ */
+ mip = LIST_FIRST(&root); /* XXX: simon */
+ LIST_FOREACH(mip, &root, list) {
+ mla = mip->la;
+ if (mip->aliasAddr.s_addr != INADDR_NONE)
+ LibAliasSetAddress (mla, mip->aliasAddr);
+ }
+
+ while (running) {
+ mip = LIST_FIRST(&root); /* XXX: simon */
+
+ if (mip->divertInOut != -1 && !mip->ifName && ninstance == 1) {
+/*
+ * When using only one socket, just call
+ * DoAliasing repeatedly to process packets.
+ */
+ DoAliasing (mip->divertInOut, DONT_KNOW);
+ continue;
+ }
+/*
+ * Build read mask from socket descriptors to select.
+ */
+ FD_ZERO (&readMask);
+/*
+ * Check if new packets are available.
+ */
+ LIST_FOREACH(mip, &root, list) {
+ if (mip->divertIn != -1)
+ FD_SET (mip->divertIn, &readMask);
+
+ if (mip->divertOut != -1)
+ FD_SET (mip->divertOut, &readMask);
+
+ if (mip->divertInOut != -1)
+ FD_SET (mip->divertInOut, &readMask);
+ }
+/*
+ * Routing info is processed always.
+ */
+ if (routeSock != -1)
+ FD_SET (routeSock, &readMask);
+
+ if (divertGlobal != -1)
+ FD_SET (divertGlobal, &readMask);
+
+ if (select (fdMax + 1,
+ &readMask,
+ NULL,
+ NULL,
+ NULL) == -1) {
+
+ if (errno == EINTR)
+ continue;
+
+ Quit ("Select failed.");
+ }
+
+ if (divertGlobal != -1)
+ if (FD_ISSET (divertGlobal, &readMask))
+ DoGlobal (divertGlobal);
+ LIST_FOREACH(mip, &root, list) {
+ mla = mip->la;
+ if (mip->divertIn != -1)
+ if (FD_ISSET (mip->divertIn, &readMask))
+ DoAliasing (mip->divertIn, INPUT);
+
+ if (mip->divertOut != -1)
+ if (FD_ISSET (mip->divertOut, &readMask))
+ DoAliasing (mip->divertOut, OUTPUT);
+
+ if (mip->divertInOut != -1)
+ if (FD_ISSET (mip->divertInOut, &readMask))
+ DoAliasing (mip->divertInOut, DONT_KNOW);
+
+ }
+ if (routeSock != -1)
+ if (FD_ISSET (routeSock, &readMask))
+ HandleRoutingInfo (routeSock);
+ }
+
+ if (background)
+ unlink (pidName);
+
+ return 0;
+}
+
+static void DaemonMode ()
+{
+ FILE* pidFile;
+
+ daemon (0, 0);
+ background = 1;
+
+ pidFile = fopen (pidName, "w");
+ if (pidFile) {
+
+ fprintf (pidFile, "%d\n", getpid ());
+ fclose (pidFile);
+ }
+}
+
+static void ParseArgs (int argc, char** argv)
+{
+ int arg;
+ char* opt;
+ char parmBuf[256];
+ int len; /* bounds checking */
+
+ for (arg = 1; arg < argc; arg++) {
+
+ opt = argv[arg];
+ if (*opt != '-') {
+
+ warnx ("invalid option %s", opt);
+ Usage ();
+ }
+
+ parmBuf[0] = '\0';
+ len = 0;
+
+ while (arg < argc - 1) {
+
+ if (argv[arg + 1][0] == '-')
+ break;
+
+ if (len) {
+ strncat (parmBuf, " ", sizeof(parmBuf) - (len + 1));
+ len += strlen(parmBuf + len);
+ }
+
+ ++arg;
+ strncat (parmBuf, argv[arg], sizeof(parmBuf) - (len + 1));
+ len += strlen(parmBuf + len);
+
+ }
+
+ ParseOption (opt + 1, (len ? parmBuf : NULL));
+
+ }
+}
+
+static void DoGlobal (int fd)
+{
+ int bytes;
+ int origBytes;
+ char buf[IP_MAXPACKET];
+ struct sockaddr_in addr;
+ int wrote;
+ socklen_t addrSize;
+ struct ip* ip;
+ char msgBuf[80];
+
+/*
+ * Get packet from socket.
+ */
+ addrSize = sizeof addr;
+ origBytes = recvfrom (fd,
+ buf,
+ sizeof buf,
+ 0,
+ (struct sockaddr*) &addr,
+ &addrSize);
+
+ if (origBytes == -1) {
+
+ if (errno != EINTR)
+ Warn ("read from divert socket failed");
+
+ return;
+ }
+
+#if 0
+ if (mip->assignAliasAddr) {
+ SetAliasAddressFromIfName (mip->ifName);
+ mip->assignAliasAddr = 0;
+ }
+#endif
+/*
+ * This is an IP packet.
+ */
+ ip = (struct ip*) buf;
+
+ if (verbose) {
+/*
+ * Print packet direction and protocol type.
+ */
+ printf ("Glb ");
+
+ switch (ip->ip_p) {
+ case IPPROTO_TCP:
+ printf ("[TCP] ");
+ break;
+
+ case IPPROTO_UDP:
+ printf ("[UDP] ");
+ break;
+
+ case IPPROTO_ICMP:
+ printf ("[ICMP] ");
+ break;
+
+ default:
+ printf ("[%d] ", ip->ip_p);
+ break;
+ }
+/*
+ * Print addresses.
+ */
+ PrintPacket (ip);
+ }
+
+ LIST_FOREACH(mip, &root, list) {
+ mla = mip->la;
+ if (LibAliasOutTry (mla, buf, IP_MAXPACKET, 0) != PKT_ALIAS_IGNORED)
+ break;
+ }
+/*
+ * Length might have changed during aliasing.
+ */
+ bytes = ntohs (ip->ip_len);
+/*
+ * Update alias overhead size for outgoing packets.
+ */
+ if (mip != NULL && bytes - origBytes > mip->aliasOverhead)
+ mip->aliasOverhead = bytes - origBytes;
+
+ if (verbose) {
+
+/*
+ * Print addresses after aliasing.
+ */
+ printf (" aliased to\n");
+ printf (" ");
+ PrintPacket (ip);
+ printf ("\n");
+ }
+
+/*
+ * Put packet back for processing.
+ */
+ wrote = sendto (fd,
+ buf,
+ bytes,
+ 0,
+ (struct sockaddr*) &addr,
+ sizeof addr);
+
+ if (wrote != bytes) {
+
+ if (errno == EMSGSIZE) {
+
+ if (mip->ifMTU != -1)
+ SendNeedFragIcmp (icmpSock,
+ (struct ip*) buf,
+ mip->ifMTU - mip->aliasOverhead);
+ }
+ else if (errno == EACCES && logIpfwDenied) {
+
+ sprintf (msgBuf, "failed to write packet back");
+ Warn (msgBuf);
+ }
+ }
+}
+
+
+static void DoAliasing (int fd, int direction)
+{
+ int bytes;
+ int origBytes;
+ char buf[IP_MAXPACKET];
+ struct sockaddr_in addr;
+ int wrote;
+ int status;
+ socklen_t addrSize;
+ struct ip* ip;
+ char msgBuf[80];
+
+ if (mip->assignAliasAddr) {
+
+ SetAliasAddressFromIfName (mip->ifName);
+ mip->assignAliasAddr = 0;
+ }
+/*
+ * Get packet from socket.
+ */
+ addrSize = sizeof addr;
+ origBytes = recvfrom (fd,
+ buf,
+ sizeof buf,
+ 0,
+ (struct sockaddr*) &addr,
+ &addrSize);
+
+ if (origBytes == -1) {
+
+ if (errno != EINTR)
+ Warn ("read from divert socket failed");
+
+ return;
+ }
+/*
+ * This is an IP packet.
+ */
+ ip = (struct ip*) buf;
+ if (direction == DONT_KNOW) {
+ if (addr.sin_addr.s_addr == INADDR_ANY)
+ direction = OUTPUT;
+ else
+ direction = INPUT;
+ }
+
+ if (verbose) {
+/*
+ * Print packet direction and protocol type.
+ */
+ printf (direction == OUTPUT ? "Out " : "In ");
+ if (ninstance > 1)
+ printf ("{%s}", mip->name);
+
+ switch (ip->ip_p) {
+ case IPPROTO_TCP:
+ printf ("[TCP] ");
+ break;
+
+ case IPPROTO_UDP:
+ printf ("[UDP] ");
+ break;
+
+ case IPPROTO_ICMP:
+ printf ("[ICMP] ");
+ break;
+
+ default:
+ printf ("[%d] ", ip->ip_p);
+ break;
+ }
+/*
+ * Print addresses.
+ */
+ PrintPacket (ip);
+ }
+
+ if (direction == OUTPUT) {
+/*
+ * Outgoing packets. Do aliasing.
+ */
+ LibAliasOut (mla, buf, IP_MAXPACKET);
+ }
+ else {
+
+/*
+ * Do aliasing.
+ */
+ status = LibAliasIn (mla, buf, IP_MAXPACKET);
+ if (status == PKT_ALIAS_IGNORED &&
+ mip->dropIgnoredIncoming) {
+
+ if (verbose)
+ printf (" dropped.\n");
+
+ if (mip->logDropped)
+ SyslogPacket (ip, LOG_WARNING, "denied");
+
+ return;
+ }
+ }
+/*
+ * Length might have changed during aliasing.
+ */
+ bytes = ntohs (ip->ip_len);
+/*
+ * Update alias overhead size for outgoing packets.
+ */
+ if (direction == OUTPUT &&
+ bytes - origBytes > mip->aliasOverhead)
+ mip->aliasOverhead = bytes - origBytes;
+
+ if (verbose) {
+
+/*
+ * Print addresses after aliasing.
+ */
+ printf (" aliased to\n");
+ printf (" ");
+ PrintPacket (ip);
+ printf ("\n");
+ }
+
+/*
+ * Put packet back for processing.
+ */
+ wrote = sendto (fd,
+ buf,
+ bytes,
+ 0,
+ (struct sockaddr*) &addr,
+ sizeof addr);
+
+ if (wrote != bytes) {
+
+ if (errno == EMSGSIZE) {
+
+ if (direction == OUTPUT &&
+ mip->ifMTU != -1)
+ SendNeedFragIcmp (icmpSock,
+ (struct ip*) buf,
+ mip->ifMTU - mip->aliasOverhead);
+ }
+ else if (errno == EACCES && logIpfwDenied) {
+
+ sprintf (msgBuf, "failed to write packet back");
+ Warn (msgBuf);
+ }
+ }
+}
+
+static void HandleRoutingInfo (int fd)
+{
+ int bytes;
+ struct if_msghdr ifMsg;
+/*
+ * Get packet from socket.
+ */
+ bytes = read (fd, &ifMsg, sizeof ifMsg);
+ if (bytes == -1) {
+
+ Warn ("read from routing socket failed");
+ return;
+ }
+
+ if (ifMsg.ifm_version != RTM_VERSION) {
+
+ Warn ("unexpected packet read from routing socket");
+ return;
+ }
+
+ if (verbose)
+ printf ("Routing message %#x received.\n", ifMsg.ifm_type);
+
+ if ((ifMsg.ifm_type == RTM_NEWADDR || ifMsg.ifm_type == RTM_IFINFO)) {
+ LIST_FOREACH(mip, &root, list) {
+ mla = mip->la;
+ if (ifMsg.ifm_index == mip->ifIndex) {
+ if (verbose)
+ printf("Interface address/MTU has probably changed.\n");
+ mip->assignAliasAddr = 1;
+ }
+ }
+ }
+}
+
+static void PrintPacket (struct ip* ip)
+{
+ printf ("%s", FormatPacket (ip));
+}
+
+static void SyslogPacket (struct ip* ip, int priority, const char *label)
+{
+ syslog (priority, "%s %s", label, FormatPacket (ip));
+}
+
+static char* FormatPacket (struct ip* ip)
+{
+ static char buf[256];
+ struct tcphdr* tcphdr;
+ struct udphdr* udphdr;
+ struct icmp* icmphdr;
+ char src[20];
+ char dst[20];
+
+ strcpy (src, inet_ntoa (ip->ip_src));
+ strcpy (dst, inet_ntoa (ip->ip_dst));
+
+ switch (ip->ip_p) {
+ case IPPROTO_TCP:
+ tcphdr = (struct tcphdr*) ((char*) ip + (ip->ip_hl << 2));
+ sprintf (buf, "[TCP] %s:%d -> %s:%d",
+ src,
+ ntohs (tcphdr->th_sport),
+ dst,
+ ntohs (tcphdr->th_dport));
+ break;
+
+ case IPPROTO_UDP:
+ udphdr = (struct udphdr*) ((char*) ip + (ip->ip_hl << 2));
+ sprintf (buf, "[UDP] %s:%d -> %s:%d",
+ src,
+ ntohs (udphdr->uh_sport),
+ dst,
+ ntohs (udphdr->uh_dport));
+ break;
+
+ case IPPROTO_ICMP:
+ icmphdr = (struct icmp*) ((char*) ip + (ip->ip_hl << 2));
+ sprintf (buf, "[ICMP] %s -> %s %u(%u)",
+ src,
+ dst,
+ icmphdr->icmp_type,
+ icmphdr->icmp_code);
+ break;
+
+ default:
+ sprintf (buf, "[%d] %s -> %s ", ip->ip_p, src, dst);
+ break;
+ }
+
+ return buf;
+}
+
+static void
+SetAliasAddressFromIfName(const char *ifn)
+{
+ size_t needed;
+ int mib[6];
+ char *buf, *lim, *next;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr_dl *sdl;
+ struct sockaddr_in *sin;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET; /* Only IP addresses please */
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0; /* ifIndex??? */
+/*
+ * Get interface data.
+ */
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
+ err(1, "iflist-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ errx(1, "malloc failed");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
+ err(1, "iflist-sysctl-get");
+ lim = buf + needed;
+/*
+ * Loop through interfaces until one with
+ * given name is found. This is done to
+ * find correct interface index for routing
+ * message processing.
+ */
+ mip->ifIndex = 0;
+ next = buf;
+ while (next < lim) {
+ ifm = (struct if_msghdr *)next;
+ next += ifm->ifm_msglen;
+ if (ifm->ifm_version != RTM_VERSION) {
+ if (verbose)
+ warnx("routing message version %d "
+ "not understood", ifm->ifm_version);
+ continue;
+ }
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ if (strlen(ifn) == sdl->sdl_nlen &&
+ strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
+ mip->ifIndex = ifm->ifm_index;
+ mip->ifMTU = ifm->ifm_data.ifi_mtu;
+ break;
+ }
+ }
+ }
+ if (!mip->ifIndex)
+ errx(1, "unknown interface name %s", ifn);
+/*
+ * Get interface address.
+ */
+ sin = NULL;
+ while (next < lim) {
+ ifam = (struct ifa_msghdr *)next;
+ next += ifam->ifam_msglen;
+ if (ifam->ifam_version != RTM_VERSION) {
+ if (verbose)
+ warnx("routing message version %d "
+ "not understood", ifam->ifam_version);
+ continue;
+ }
+ if (ifam->ifam_type != RTM_NEWADDR)
+ break;
+ if (ifam->ifam_addrs & RTA_IFA) {
+ int i;
+ char *cp = (char *)(ifam + 1);
+
+ for (i = 1; i < RTA_IFA; i <<= 1)
+ if (ifam->ifam_addrs & i)
+ cp += SA_SIZE((struct sockaddr *)cp);
+ if (((struct sockaddr *)cp)->sa_family == AF_INET) {
+ sin = (struct sockaddr_in *)cp;
+ break;
+ }
+ }
+ }
+ if (sin == NULL)
+ errx(1, "%s: cannot get interface address", ifn);
+
+ LibAliasSetAddress(mla, sin->sin_addr);
+ syslog(LOG_INFO, "Aliasing to %s, mtu %d bytes",
+ inet_ntoa(sin->sin_addr), mip->ifMTU);
+
+ free(buf);
+}
+
+void Quit (const char* msg)
+{
+ Warn (msg);
+ exit (1);
+}
+
+void Warn (const char* msg)
+{
+ if (background)
+ syslog (LOG_ALERT, "%s (%m)", msg);
+ else
+ warn ("%s", msg);
+}
+
+static void RefreshAddr (int sig __unused)
+{
+ if (mip->ifName)
+ mip->assignAliasAddr = 1;
+}
+
+static void InitiateShutdown (int sig __unused)
+{
+/*
+ * Start timer to allow kernel gracefully
+ * shutdown existing connections when system
+ * is shut down.
+ */
+ siginterrupt(SIGALRM, 1);
+ signal (SIGALRM, Shutdown);
+ alarm (10);
+}
+
+static void Shutdown (int sig __unused)
+{
+ running = 0;
+}
+
+/*
+ * Different options recognized by this program.
+ */
+
+enum Option {
+
+ LibAliasOption,
+ Instance,
+ Verbose,
+ InPort,
+ OutPort,
+ Port,
+ GlobalPort,
+ AliasAddress,
+ TargetAddress,
+ InterfaceName,
+ RedirectPort,
+ RedirectProto,
+ RedirectAddress,
+ ConfigFile,
+ DynamicMode,
+ ProxyRule,
+ LogDenied,
+ LogFacility,
+ PunchFW,
+ SkinnyPort,
+ LogIpfwDenied,
+ PidFile
+};
+
+enum Param {
+
+ YesNo,
+ Numeric,
+ String,
+ None,
+ Address,
+ Service
+};
+
+/*
+ * Option information structure (used by ParseOption).
+ */
+
+struct OptionInfo {
+
+ enum Option type;
+ int packetAliasOpt;
+ enum Param parm;
+ const char* parmDescription;
+ const char* description;
+ const char* name;
+ const char* shortName;
+};
+
+/*
+ * Table of known options.
+ */
+
+static struct OptionInfo optionTable[] = {
+
+ { LibAliasOption,
+ PKT_ALIAS_UNREGISTERED_ONLY,
+ YesNo,
+ "[yes|no]",
+ "alias only unregistered addresses",
+ "unregistered_only",
+ "u" },
+
+ { LibAliasOption,
+ PKT_ALIAS_LOG,
+ YesNo,
+ "[yes|no]",
+ "enable logging",
+ "log",
+ "l" },
+
+ { LibAliasOption,
+ PKT_ALIAS_PROXY_ONLY,
+ YesNo,
+ "[yes|no]",
+ "proxy only",
+ "proxy_only",
+ NULL },
+
+ { LibAliasOption,
+ PKT_ALIAS_REVERSE,
+ YesNo,
+ "[yes|no]",
+ "operate in reverse mode",
+ "reverse",
+ NULL },
+
+ { LibAliasOption,
+ PKT_ALIAS_DENY_INCOMING,
+ YesNo,
+ "[yes|no]",
+ "allow incoming connections",
+ "deny_incoming",
+ "d" },
+
+ { LibAliasOption,
+ PKT_ALIAS_USE_SOCKETS,
+ YesNo,
+ "[yes|no]",
+ "use sockets to inhibit port conflict",
+ "use_sockets",
+ "s" },
+
+ { LibAliasOption,
+ PKT_ALIAS_SAME_PORTS,
+ YesNo,
+ "[yes|no]",
+ "try to keep original port numbers for connections",
+ "same_ports",
+ "m" },
+
+ { Verbose,
+ 0,
+ YesNo,
+ "[yes|no]",
+ "verbose mode, dump packet information",
+ "verbose",
+ "v" },
+
+ { DynamicMode,
+ 0,
+ YesNo,
+ "[yes|no]",
+ "dynamic mode, automatically detect interface address changes",
+ "dynamic",
+ NULL },
+
+ { InPort,
+ 0,
+ Service,
+ "number|service_name",
+ "set port for incoming packets",
+ "in_port",
+ "i" },
+
+ { OutPort,
+ 0,
+ Service,
+ "number|service_name",
+ "set port for outgoing packets",
+ "out_port",
+ "o" },
+
+ { Port,
+ 0,
+ Service,
+ "number|service_name",
+ "set port (defaults to natd/divert)",
+ "port",
+ "p" },
+
+ { GlobalPort,
+ 0,
+ Service,
+ "number|service_name",
+ "set globalport",
+ "globalport",
+ NULL },
+
+ { AliasAddress,
+ 0,
+ Address,
+ "x.x.x.x",
+ "address to use for aliasing",
+ "alias_address",
+ "a" },
+
+ { TargetAddress,
+ 0,
+ Address,
+ "x.x.x.x",
+ "address to use for incoming sessions",
+ "target_address",
+ "t" },
+
+ { InterfaceName,
+ 0,
+ String,
+ "network_if_name",
+ "take aliasing address from interface",
+ "interface",
+ "n" },
+
+ { ProxyRule,
+ 0,
+ String,
+ "[type encode_ip_hdr|encode_tcp_stream] port xxxx server "
+ "a.b.c.d:yyyy",
+ "add transparent proxying / destination NAT",
+ "proxy_rule",
+ NULL },
+
+ { RedirectPort,
+ 0,
+ String,
+ "tcp|udp local_addr:local_port_range[,...] [public_addr:]public_port_range"
+ " [remote_addr[:remote_port_range]]",
+ "redirect a port (or ports) for incoming traffic",
+ "redirect_port",
+ NULL },
+
+ { RedirectProto,
+ 0,
+ String,
+ "proto local_addr [public_addr] [remote_addr]",
+ "redirect packets of a given proto",
+ "redirect_proto",
+ NULL },
+
+ { RedirectAddress,
+ 0,
+ String,
+ "local_addr[,...] public_addr",
+ "define mapping between local and public addresses",
+ "redirect_address",
+ NULL },
+
+ { ConfigFile,
+ 0,
+ String,
+ "file_name",
+ "read options from configuration file",
+ "config",
+ "f" },
+
+ { LogDenied,
+ 0,
+ YesNo,
+ "[yes|no]",
+ "enable logging of denied incoming packets",
+ "log_denied",
+ NULL },
+
+ { LogFacility,
+ 0,
+ String,
+ "facility",
+ "name of syslog facility to use for logging",
+ "log_facility",
+ NULL },
+
+ { PunchFW,
+ 0,
+ String,
+ "basenumber:count",
+ "punch holes in the firewall for incoming FTP/IRC DCC connections",
+ "punch_fw",
+ NULL },
+
+ { SkinnyPort,
+ 0,
+ String,
+ "port",
+ "set the TCP port for use with the Skinny Station protocol",
+ "skinny_port",
+ NULL },
+
+ { LogIpfwDenied,
+ 0,
+ YesNo,
+ "[yes|no]",
+ "log packets converted by natd, but denied by ipfw",
+ "log_ipfw_denied",
+ NULL },
+
+ { PidFile,
+ 0,
+ String,
+ "file_name",
+ "store PID in an alternate file",
+ "pid_file",
+ "P" },
+ { Instance,
+ 0,
+ String,
+ "instance name",
+ "name of aliasing engine instance",
+ "instance",
+ NULL },
+};
+
+static void ParseOption (const char* option, const char* parms)
+{
+ int i;
+ struct OptionInfo* info;
+ int yesNoValue;
+ int aliasValue;
+ int numValue;
+ u_short uNumValue;
+ const char* strValue;
+ struct in_addr addrValue;
+ int max;
+ char* end;
+ CODE* fac_record = NULL;
+/*
+ * Find option from table.
+ */
+ max = sizeof (optionTable) / sizeof (struct OptionInfo);
+ for (i = 0, info = optionTable; i < max; i++, info++) {
+
+ if (!strcmp (info->name, option))
+ break;
+
+ if (info->shortName)
+ if (!strcmp (info->shortName, option))
+ break;
+ }
+
+ if (i >= max) {
+
+ warnx ("unknown option %s", option);
+ Usage ();
+ }
+
+ uNumValue = 0;
+ yesNoValue = 0;
+ numValue = 0;
+ strValue = NULL;
+/*
+ * Check parameters.
+ */
+ switch (info->parm) {
+ case YesNo:
+ if (!parms)
+ parms = "yes";
+
+ if (!strcmp (parms, "yes"))
+ yesNoValue = 1;
+ else
+ if (!strcmp (parms, "no"))
+ yesNoValue = 0;
+ else
+ errx (1, "%s needs yes/no parameter", option);
+ break;
+
+ case Service:
+ if (!parms)
+ errx (1, "%s needs service name or "
+ "port number parameter",
+ option);
+
+ uNumValue = StrToPort (parms, "divert");
+ break;
+
+ case Numeric:
+ if (parms)
+ numValue = strtol (parms, &end, 10);
+ else
+ end = NULL;
+
+ if (end == parms)
+ errx (1, "%s needs numeric parameter", option);
+ break;
+
+ case String:
+ strValue = parms;
+ if (!strValue)
+ errx (1, "%s needs parameter", option);
+ break;
+
+ case None:
+ if (parms)
+ errx (1, "%s does not take parameters", option);
+ break;
+
+ case Address:
+ if (!parms)
+ errx (1, "%s needs address/host parameter", option);
+
+ StrToAddr (parms, &addrValue);
+ break;
+ }
+
+ switch (info->type) {
+ case LibAliasOption:
+
+ aliasValue = yesNoValue ? info->packetAliasOpt : 0;
+ LibAliasSetMode (mla, aliasValue, info->packetAliasOpt);
+ break;
+
+ case Verbose:
+ verbose = yesNoValue;
+ break;
+
+ case DynamicMode:
+ dynamicMode = yesNoValue;
+ break;
+
+ case InPort:
+ mip->inPort = uNumValue;
+ break;
+
+ case OutPort:
+ mip->outPort = uNumValue;
+ break;
+
+ case Port:
+ mip->inOutPort = uNumValue;
+ break;
+
+ case GlobalPort:
+ globalPort = uNumValue;
+ break;
+
+ case AliasAddress:
+ memcpy (&mip->aliasAddr, &addrValue, sizeof (struct in_addr));
+ break;
+
+ case TargetAddress:
+ LibAliasSetTarget(mla, addrValue);
+ break;
+
+ case RedirectPort:
+ SetupPortRedirect (strValue);
+ break;
+
+ case RedirectProto:
+ SetupProtoRedirect(strValue);
+ break;
+
+ case RedirectAddress:
+ SetupAddressRedirect (strValue);
+ break;
+
+ case ProxyRule:
+ LibAliasProxyRule (mla, strValue);
+ break;
+
+ case InterfaceName:
+ if (mip->ifName)
+ free (mip->ifName);
+
+ mip->ifName = strdup (strValue);
+ break;
+
+ case ConfigFile:
+ ReadConfigFile (strValue);
+ break;
+
+ case LogDenied:
+ mip->logDropped = yesNoValue;
+ break;
+
+ case LogFacility:
+
+ fac_record = facilitynames;
+ while (fac_record->c_name != NULL) {
+
+ if (!strcmp (fac_record->c_name, strValue)) {
+
+ logFacility = fac_record->c_val;
+ break;
+
+ }
+ else
+ fac_record++;
+ }
+
+ if(fac_record->c_name == NULL)
+ errx(1, "Unknown log facility name: %s", strValue);
+
+ break;
+
+ case PunchFW:
+ SetupPunchFW(strValue);
+ break;
+
+ case SkinnyPort:
+ SetupSkinnyPort(strValue);
+ break;
+
+ case LogIpfwDenied:
+ logIpfwDenied = yesNoValue;;
+ break;
+
+ case PidFile:
+ pidName = strdup (strValue);
+ break;
+ case Instance:
+ NewInstance(strValue);
+ break;
+ }
+}
+
+void ReadConfigFile (const char* fileName)
+{
+ FILE* file;
+ char *buf;
+ size_t len;
+ char *ptr, *p;
+ char* option;
+
+ file = fopen (fileName, "r");
+ if (!file)
+ err(1, "cannot open config file %s", fileName);
+
+ while ((buf = fgetln(file, &len)) != NULL) {
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ else
+ errx(1, "config file format error: "
+ "last line should end with newline");
+
+/*
+ * Check for comments, strip off trailing spaces.
+ */
+ if ((ptr = strchr(buf, '#')))
+ *ptr = '\0';
+ for (ptr = buf; isspace(*ptr); ++ptr)
+ continue;
+ if (*ptr == '\0')
+ continue;
+ for (p = strchr(buf, '\0'); isspace(*--p);)
+ continue;
+ *++p = '\0';
+
+/*
+ * Extract option name.
+ */
+ option = ptr;
+ while (*ptr && !isspace (*ptr))
+ ++ptr;
+
+ if (*ptr != '\0') {
+
+ *ptr = '\0';
+ ++ptr;
+ }
+/*
+ * Skip white space between name and parms.
+ */
+ while (*ptr && isspace (*ptr))
+ ++ptr;
+
+ ParseOption (option, *ptr ? ptr : NULL);
+ }
+
+ fclose (file);
+}
+
+static void Usage ()
+{
+ int i;
+ int max;
+ struct OptionInfo* info;
+
+ fprintf (stderr, "Recognized options:\n\n");
+
+ max = sizeof (optionTable) / sizeof (struct OptionInfo);
+ for (i = 0, info = optionTable; i < max; i++, info++) {
+
+ fprintf (stderr, "-%-20s %s\n", info->name,
+ info->parmDescription);
+
+ if (info->shortName)
+ fprintf (stderr, "-%-20s %s\n", info->shortName,
+ info->parmDescription);
+
+ fprintf (stderr, " %s\n\n", info->description);
+ }
+
+ exit (1);
+}
+
+void SetupPortRedirect (const char* parms)
+{
+ char buf[128];
+ char* ptr;
+ char* serverPool;
+ struct in_addr localAddr;
+ struct in_addr publicAddr;
+ struct in_addr remoteAddr;
+ port_range portRange;
+ u_short localPort = 0;
+ u_short publicPort = 0;
+ u_short remotePort = 0;
+ u_short numLocalPorts = 0;
+ u_short numPublicPorts = 0;
+ u_short numRemotePorts = 0;
+ int proto;
+ char* protoName;
+ char* separator;
+ int i;
+ struct alias_link *aliaslink = NULL;
+
+ strlcpy (buf, parms, sizeof(buf));
+/*
+ * Extract protocol.
+ */
+ protoName = strtok (buf, " \t");
+ if (!protoName)
+ errx (1, "redirect_port: missing protocol");
+
+ proto = StrToProto (protoName);
+/*
+ * Extract local address.
+ */
+ ptr = strtok (NULL, " \t");
+ if (!ptr)
+ errx (1, "redirect_port: missing local address");
+
+ separator = strchr(ptr, ',');
+ if (separator) { /* LSNAT redirection syntax. */
+ localAddr.s_addr = INADDR_NONE;
+ localPort = ~0;
+ numLocalPorts = 1;
+ serverPool = ptr;
+ } else {
+ if ( StrToAddrAndPortRange (ptr, &localAddr, protoName, &portRange) != 0 )
+ errx (1, "redirect_port: invalid local port range");
+
+ localPort = GETLOPORT(portRange);
+ numLocalPorts = GETNUMPORTS(portRange);
+ serverPool = NULL;
+ }
+
+/*
+ * Extract public port and optionally address.
+ */
+ ptr = strtok (NULL, " \t");
+ if (!ptr)
+ errx (1, "redirect_port: missing public port");
+
+ separator = strchr (ptr, ':');
+ if (separator) {
+ if (StrToAddrAndPortRange (ptr, &publicAddr, protoName, &portRange) != 0 )
+ errx (1, "redirect_port: invalid public port range");
+ }
+ else {
+ publicAddr.s_addr = INADDR_ANY;
+ if (StrToPortRange (ptr, protoName, &portRange) != 0)
+ errx (1, "redirect_port: invalid public port range");
+ }
+
+ publicPort = GETLOPORT(portRange);
+ numPublicPorts = GETNUMPORTS(portRange);
+
+/*
+ * Extract remote address and optionally port.
+ */
+ ptr = strtok (NULL, " \t");
+ if (ptr) {
+ separator = strchr (ptr, ':');
+ if (separator) {
+ if (StrToAddrAndPortRange (ptr, &remoteAddr, protoName, &portRange) != 0)
+ errx (1, "redirect_port: invalid remote port range");
+ } else {
+ SETLOPORT(portRange, 0);
+ SETNUMPORTS(portRange, 1);
+ StrToAddr (ptr, &remoteAddr);
+ }
+ }
+ else {
+ SETLOPORT(portRange, 0);
+ SETNUMPORTS(portRange, 1);
+ remoteAddr.s_addr = INADDR_ANY;
+ }
+
+ remotePort = GETLOPORT(portRange);
+ numRemotePorts = GETNUMPORTS(portRange);
+
+/*
+ * Make sure port ranges match up, then add the redirect ports.
+ */
+ if (numLocalPorts != numPublicPorts)
+ errx (1, "redirect_port: port ranges must be equal in size");
+
+ /* Remote port range is allowed to be '0' which means all ports. */
+ if (numRemotePorts != numLocalPorts && (numRemotePorts != 1 || remotePort != 0))
+ errx (1, "redirect_port: remote port must be 0 or equal to local port range in size");
+
+ for (i = 0 ; i < numPublicPorts ; ++i) {
+ /* If remotePort is all ports, set it to 0. */
+ u_short remotePortCopy = remotePort + i;
+ if (numRemotePorts == 1 && remotePort == 0)
+ remotePortCopy = 0;
+
+ aliaslink = LibAliasRedirectPort (mla, localAddr,
+ htons(localPort + i),
+ remoteAddr,
+ htons(remotePortCopy),
+ publicAddr,
+ htons(publicPort + i),
+ proto);
+ }
+
+/*
+ * Setup LSNAT server pool.
+ */
+ if (serverPool != NULL && aliaslink != NULL) {
+ ptr = strtok(serverPool, ",");
+ while (ptr != NULL) {
+ if (StrToAddrAndPortRange(ptr, &localAddr, protoName, &portRange) != 0)
+ errx(1, "redirect_port: invalid local port range");
+
+ localPort = GETLOPORT(portRange);
+ if (GETNUMPORTS(portRange) != 1)
+ errx(1, "redirect_port: local port must be single in this context");
+ LibAliasAddServer(mla, aliaslink, localAddr, htons(localPort));
+ ptr = strtok(NULL, ",");
+ }
+ }
+}
+
+void
+SetupProtoRedirect(const char* parms)
+{
+ char buf[128];
+ char* ptr;
+ struct in_addr localAddr;
+ struct in_addr publicAddr;
+ struct in_addr remoteAddr;
+ int proto;
+ char* protoName;
+ struct protoent *protoent;
+
+ strlcpy (buf, parms, sizeof(buf));
+/*
+ * Extract protocol.
+ */
+ protoName = strtok(buf, " \t");
+ if (!protoName)
+ errx(1, "redirect_proto: missing protocol");
+
+ protoent = getprotobyname(protoName);
+ if (protoent == NULL)
+ errx(1, "redirect_proto: unknown protocol %s", protoName);
+ else
+ proto = protoent->p_proto;
+/*
+ * Extract local address.
+ */
+ ptr = strtok(NULL, " \t");
+ if (!ptr)
+ errx(1, "redirect_proto: missing local address");
+ else
+ StrToAddr(ptr, &localAddr);
+/*
+ * Extract optional public address.
+ */
+ ptr = strtok(NULL, " \t");
+ if (ptr)
+ StrToAddr(ptr, &publicAddr);
+ else
+ publicAddr.s_addr = INADDR_ANY;
+/*
+ * Extract optional remote address.
+ */
+ ptr = strtok(NULL, " \t");
+ if (ptr)
+ StrToAddr(ptr, &remoteAddr);
+ else
+ remoteAddr.s_addr = INADDR_ANY;
+/*
+ * Create aliasing link.
+ */
+ (void)LibAliasRedirectProto(mla, localAddr, remoteAddr, publicAddr,
+ proto);
+}
+
+void SetupAddressRedirect (const char* parms)
+{
+ char buf[128];
+ char* ptr;
+ char* separator;
+ struct in_addr localAddr;
+ struct in_addr publicAddr;
+ char* serverPool;
+ struct alias_link *aliaslink;
+
+ strlcpy (buf, parms, sizeof(buf));
+/*
+ * Extract local address.
+ */
+ ptr = strtok (buf, " \t");
+ if (!ptr)
+ errx (1, "redirect_address: missing local address");
+
+ separator = strchr(ptr, ',');
+ if (separator) { /* LSNAT redirection syntax. */
+ localAddr.s_addr = INADDR_NONE;
+ serverPool = ptr;
+ } else {
+ StrToAddr (ptr, &localAddr);
+ serverPool = NULL;
+ }
+/*
+ * Extract public address.
+ */
+ ptr = strtok (NULL, " \t");
+ if (!ptr)
+ errx (1, "redirect_address: missing public address");
+
+ StrToAddr (ptr, &publicAddr);
+ aliaslink = LibAliasRedirectAddr(mla, localAddr, publicAddr);
+
+/*
+ * Setup LSNAT server pool.
+ */
+ if (serverPool != NULL && aliaslink != NULL) {
+ ptr = strtok(serverPool, ",");
+ while (ptr != NULL) {
+ StrToAddr(ptr, &localAddr);
+ LibAliasAddServer(mla, aliaslink, localAddr, htons(~0));
+ ptr = strtok(NULL, ",");
+ }
+ }
+}
+
+void StrToAddr (const char* str, struct in_addr* addr)
+{
+ struct hostent* hp;
+
+ if (inet_aton (str, addr))
+ return;
+
+ hp = gethostbyname (str);
+ if (!hp)
+ errx (1, "unknown host %s", str);
+
+ memcpy (addr, hp->h_addr, sizeof (struct in_addr));
+}
+
+u_short StrToPort (const char* str, const char* proto)
+{
+ u_short port;
+ struct servent* sp;
+ char* end;
+
+ port = strtol (str, &end, 10);
+ if (end != str)
+ return htons (port);
+
+ sp = getservbyname (str, proto);
+ if (!sp)
+ errx (1, "%s/%s: unknown service", str, proto);
+
+ return sp->s_port;
+}
+
+int StrToPortRange (const char* str, const char* proto, port_range *portRange)
+{
+ char* sep;
+ struct servent* sp;
+ char* end;
+ u_short loPort;
+ u_short hiPort;
+
+ /* First see if this is a service, return corresponding port if so. */
+ sp = getservbyname (str,proto);
+ if (sp) {
+ SETLOPORT(*portRange, ntohs(sp->s_port));
+ SETNUMPORTS(*portRange, 1);
+ return 0;
+ }
+
+ /* Not a service, see if it's a single port or port range. */
+ sep = strchr (str, '-');
+ if (sep == NULL) {
+ SETLOPORT(*portRange, strtol(str, &end, 10));
+ if (end != str) {
+ /* Single port. */
+ SETNUMPORTS(*portRange, 1);
+ return 0;
+ }
+
+ /* Error in port range field. */
+ errx (1, "%s/%s: unknown service", str, proto);
+ }
+
+ /* Port range, get the values and sanity check. */
+ sscanf (str, "%hu-%hu", &loPort, &hiPort);
+ SETLOPORT(*portRange, loPort);
+ SETNUMPORTS(*portRange, 0); /* Error by default */
+ if (loPort <= hiPort)
+ SETNUMPORTS(*portRange, hiPort - loPort + 1);
+
+ if (GETNUMPORTS(*portRange) == 0)
+ errx (1, "invalid port range %s", str);
+
+ return 0;
+}
+
+
+int StrToProto (const char* str)
+{
+ if (!strcmp (str, "tcp"))
+ return IPPROTO_TCP;
+
+ if (!strcmp (str, "udp"))
+ return IPPROTO_UDP;
+
+ errx (1, "unknown protocol %s. Expected tcp or udp", str);
+}
+
+int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange)
+{
+ char* ptr;
+
+ ptr = strchr (str, ':');
+ if (!ptr)
+ errx (1, "%s is missing port number", str);
+
+ *ptr = '\0';
+ ++ptr;
+
+ StrToAddr (str, addr);
+ return StrToPortRange (ptr, proto, portRange);
+}
+
+static void
+SetupPunchFW(const char *strValue)
+{
+ unsigned int base, num;
+
+ if (sscanf(strValue, "%u:%u", &base, &num) != 2)
+ errx(1, "punch_fw: basenumber:count parameter required");
+
+ LibAliasSetFWBase(mla, base, num);
+ (void)LibAliasSetMode(mla, PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW);
+}
+
+static void
+SetupSkinnyPort(const char *strValue)
+{
+ unsigned int port;
+
+ if (sscanf(strValue, "%u", &port) != 1)
+ errx(1, "skinny_port: port parameter required");
+
+ LibAliasSetSkinnyPort(mla, port);
+}
+
+static void
+NewInstance(const char *name)
+{
+ struct instance *ip;
+
+ LIST_FOREACH(ip, &root, list) {
+ if (!strcmp(ip->name, name)) {
+ mla = ip->la;
+ mip = ip;
+ return;
+ }
+ }
+ ninstance++;
+ ip = calloc(sizeof *ip, 1);
+ ip->name = strdup(name);
+ ip->la = LibAliasInit (ip->la);
+ ip->assignAliasAddr = 0;
+ ip->ifName = NULL;
+ ip->logDropped = 0;
+ ip->inPort = 0;
+ ip->outPort = 0;
+ ip->inOutPort = 0;
+ ip->aliasAddr.s_addr = INADDR_NONE;
+ ip->ifMTU = -1;
+ ip->aliasOverhead = 12;
+ LIST_INSERT_HEAD(&root, ip, list);
+ mla = ip->la;
+ mip = ip;
+}
diff --git a/sbin/natd/natd.h b/sbin/natd/natd.h
new file mode 100644
index 0000000..a05d1f7
--- /dev/null
+++ b/sbin/natd/natd.h
@@ -0,0 +1,24 @@
+/*
+ * natd - Network Address Translation Daemon for FreeBSD.
+ *
+ * This software is provided free of charge, with no
+ * warranty of any kind, either expressed or implied.
+ * Use at your own risk.
+ *
+ * You may copy, modify and distribute this software (natd.h) freely.
+ *
+ * Ari Suutari <suutari@iki.fi>
+ *
+ * $FreeBSD$
+ */
+
+#define PIDFILE "/var/run/natd.pid"
+#define INPUT 1
+#define OUTPUT 2
+#define DONT_KNOW 3
+
+extern void Quit (const char* msg);
+extern void Warn (const char* msg);
+extern int SendNeedFragIcmp (int sock, struct ip* failedDgram, int mtu);
+extern struct libalias *mla;
+
diff --git a/sbin/natd/samples/natd.cf.sample b/sbin/natd/samples/natd.cf.sample
new file mode 100644
index 0000000..d4dcd71
--- /dev/null
+++ b/sbin/natd/samples/natd.cf.sample
@@ -0,0 +1,92 @@
+#
+# $FreeBSD$
+#
+#
+# Configuration file for natd.
+#
+#
+# Enable logging to file /var/log/alias.log
+#
+log no
+#
+# Incoming connections. Should NEVER be set to "yes" if redirect_port
+# or redirect_address statements are activated in this file!
+#
+# Setting to yes provides additional anti-crack protection
+#
+deny_incoming no
+#
+# Use sockets to avoid port clashes. Uses additional system resources, but
+# guarantees successful connections when port numbers conflict
+#
+use_sockets no
+#
+# Avoid port changes if possible when altering outbound packets. Makes rlogin
+# work in most cases.
+#
+same_ports yes
+#
+# Verbose mode. Enables dumping of packets and disables
+# forking to background. Only set to yes for debugging.
+#
+verbose no
+#
+# Divert port. Can be a name in /etc/services or numeric value.
+#
+port 32000
+#
+# Interface name or address being aliased. Either one,
+# not both is required.
+#
+# Obtain interface name from the command output of "ifconfig -a"
+#
+# alias_address 192.168.0.1
+interface ep0
+#
+# Alias unregistered addresses or all addresses. Set this to yes if
+# the inside network is all RFC1918 addresses.
+#
+unregistered_only no
+#
+# Configure permanent links. If you use host names instead
+# of addresses here, be sure that name server works BEFORE
+# natd is up - this is usually not the case. So either use
+# numeric addresses or hosts that are in /etc/hosts.
+#
+# Note: Current versions of FreeBSD all call /etc/rc.firewall
+# BEFORE running named, so if the DNS server and NAT are on the same
+# machine, the nameserver won't be up if natd is called from /etc/rc.firewall
+#
+# Map connections coming to port 30000 to telnet in my_private_host.
+# Remember to allow the connection /etc/rc.firewall also.
+#
+#redirect_port tcp my_private_host:telnet 30000
+#
+# Map connections coming from host.xyz.com to port 30001 to
+# telnet in another_host.
+#redirect_port tcp another_host:telnet 30001 host.xyz.com
+#
+# Static NAT address mapping:
+#
+# ipconfig must apply any legal IP numbers that inside hosts
+# will be known by to the outside interface. These are sometimes known as
+# virtual IP numbers. It's suggested to use the "interface" directive
+# instead of the "alias_address" directive to make it more clear what is
+# going on. (although both will work)
+#
+# DNS in this situation can get hairy. For example, an inside host
+# named aweb.company.com is located at 192.168.1.56, and needs to be
+# accessible through a legal IP number like 198.105.232.1. If both
+# 192.168.1.56 and 198.105.232.1 are set up as address records in the DNS
+# for aweb.company.com, then external hosts attempting to access
+# aweb.company.com may use address 192.168.1.56 which is inaccessible to them.
+#
+# The obvious solution is to use only a single address for the name, the
+# outside address. However, this creates needless traffic through the
+# NAT, because inside hosts will go through the NAT to get to the legal
+# number, even when the inside number is on the same subnet as they are!
+#
+# It's probably not a good idea to use DNS names in redirect_address statements
+#
+#The following mapping points outside address 198.105.232.1 to 192.168.1.56
+#redirect_address 192.168.1.56 198.105.232.1
diff --git a/sbin/natd/samples/natd.test b/sbin/natd/samples/natd.test
new file mode 100644
index 0000000..cfdbd15
--- /dev/null
+++ b/sbin/natd/samples/natd.test
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+ if [ $# != 1 ]
+ then
+ echo "usage: natd.test ifname"
+ exit 1
+ fi
+
+ ipfw flush
+ ipfw add divert 32000 ip from any to any via $1
+ ipfw add pass ip from any to any
+
+ ./natd -port 32000 -interface $1 -verbose
+
diff --git a/sbin/newfs/Makefile b/sbin/newfs/Makefile
new file mode 100644
index 0000000..efbe7ab
--- /dev/null
+++ b/sbin/newfs/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.2 (Berkeley) 3/27/94
+# $FreeBSD$
+
+PROG= newfs
+DPADD= ${LIBUFS}
+LDADD= -lufs
+SRCS= newfs.c mkfs.c
+WARNS?= 2
+MAN= newfs.8
+
+.include <bsd.prog.mk>
+
+test: ${PROG}
+ sh ${.CURDIR}/runtest01.sh
+ sh ${.CURDIR}/runtest00.sh | tee _.test
+ diff --ignore-matching-lines=FreeBSD _.test ${.CURDIR}/ref.test
+ echo All Tests Passed
diff --git a/sbin/newfs/mkfs.c b/sbin/newfs/mkfs.c
new file mode 100644
index 0000000..2090685
--- /dev/null
+++ b/sbin/newfs/mkfs.c
@@ -0,0 +1,1080 @@
+/*
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and Network Associates Laboratories, the Security
+ * Research Division of Network Associates, Inc. under DARPA/SPAWAR
+ * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+ * research program.
+ *
+ * Copyright (c) 1980, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)mkfs.c 8.11 (Berkeley) 5/3/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <grp.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <sys/disklabel.h>
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include "newfs.h"
+
+/*
+ * make file system for cylinder-group style file systems
+ */
+#define UMASK 0755
+#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
+
+static struct csum *fscs;
+#define sblock disk.d_fs
+#define acg disk.d_cg
+
+union dinode {
+ struct ufs1_dinode dp1;
+ struct ufs2_dinode dp2;
+};
+#define DIP(dp, field) \
+ ((sblock.fs_magic == FS_UFS1_MAGIC) ? \
+ (dp)->dp1.field : (dp)->dp2.field)
+
+static caddr_t iobuf;
+static long iobufsize;
+static ufs2_daddr_t alloc(int size, int mode);
+static int charsperline(void);
+static void clrblock(struct fs *, unsigned char *, int);
+static void fsinit(time_t);
+static int ilog2(int);
+static void initcg(int, time_t);
+static int isblock(struct fs *, unsigned char *, int);
+static void iput(union dinode *, ino_t);
+static int makedir(struct direct *, int);
+static void setblock(struct fs *, unsigned char *, int);
+static void wtfs(ufs2_daddr_t, int, char *);
+static u_int32_t newfs_random(void);
+
+void
+mkfs(struct partition *pp, char *fsys)
+{
+ int fragsperinode, optimalfpg, origdensity, minfpg, lastminfpg;
+ long i, j, cylno, csfrags;
+ time_t utime;
+ quad_t sizepb;
+ int width;
+ char tmpbuf[100]; /* XXX this will break in about 2,500 years */
+ union {
+ struct fs fdummy;
+ char cdummy[SBLOCKSIZE];
+ } dummy;
+#define fsdummy dummy.fdummy
+#define chdummy dummy.cdummy
+
+ /*
+ * Our blocks == sector size, and the version of UFS we are using is
+ * specified by Oflag.
+ */
+ disk.d_bsize = sectorsize;
+ disk.d_ufs = Oflag;
+ if (Rflag) {
+ utime = 1000000000;
+ } else {
+ time(&utime);
+ arc4random_stir();
+ }
+ sblock.fs_old_flags = FS_FLAGS_UPDATED;
+ sblock.fs_flags = 0;
+ if (Uflag)
+ sblock.fs_flags |= FS_DOSOFTDEP;
+ if (Lflag)
+ strlcpy(sblock.fs_volname, volumelabel, MAXVOLLEN);
+ if (lflag)
+ sblock.fs_flags |= FS_MULTILABEL;
+ /*
+ * Validate the given file system size.
+ * Verify that its last block can actually be accessed.
+ * Convert to file system fragment sized units.
+ */
+ if (fssize <= 0) {
+ printf("preposterous size %jd\n", (intmax_t)fssize);
+ exit(13);
+ }
+ wtfs(fssize - (realsectorsize / DEV_BSIZE), realsectorsize,
+ (char *)&sblock);
+ /*
+ * collect and verify the file system density info
+ */
+ sblock.fs_avgfilesize = avgfilesize;
+ sblock.fs_avgfpdir = avgfilesperdir;
+ if (sblock.fs_avgfilesize <= 0)
+ printf("illegal expected average file size %d\n",
+ sblock.fs_avgfilesize), exit(14);
+ if (sblock.fs_avgfpdir <= 0)
+ printf("illegal expected number of files per directory %d\n",
+ sblock.fs_avgfpdir), exit(15);
+ /*
+ * collect and verify the block and fragment sizes
+ */
+ sblock.fs_bsize = bsize;
+ sblock.fs_fsize = fsize;
+ if (!POWEROF2(sblock.fs_bsize)) {
+ printf("block size must be a power of 2, not %d\n",
+ sblock.fs_bsize);
+ exit(16);
+ }
+ if (!POWEROF2(sblock.fs_fsize)) {
+ printf("fragment size must be a power of 2, not %d\n",
+ sblock.fs_fsize);
+ exit(17);
+ }
+ if (sblock.fs_fsize < sectorsize) {
+ printf("increasing fragment size from %d to sector size (%d)\n",
+ sblock.fs_fsize, sectorsize);
+ sblock.fs_fsize = sectorsize;
+ }
+ if (sblock.fs_bsize > MAXBSIZE) {
+ printf("decreasing block size from %d to maximum (%d)\n",
+ sblock.fs_bsize, MAXBSIZE);
+ sblock.fs_bsize = MAXBSIZE;
+ }
+ if (sblock.fs_bsize < MINBSIZE) {
+ printf("increasing block size from %d to minimum (%d)\n",
+ sblock.fs_bsize, MINBSIZE);
+ sblock.fs_bsize = MINBSIZE;
+ }
+ if (sblock.fs_fsize > MAXBSIZE) {
+ printf("decreasing fragment size from %d to maximum (%d)\n",
+ sblock.fs_fsize, MAXBSIZE);
+ sblock.fs_fsize = MAXBSIZE;
+ }
+ if (sblock.fs_bsize < sblock.fs_fsize) {
+ printf("increasing block size from %d to fragment size (%d)\n",
+ sblock.fs_bsize, sblock.fs_fsize);
+ sblock.fs_bsize = sblock.fs_fsize;
+ }
+ if (sblock.fs_fsize * MAXFRAG < sblock.fs_bsize) {
+ printf(
+ "increasing fragment size from %d to block size / %d (%d)\n",
+ sblock.fs_fsize, MAXFRAG, sblock.fs_bsize / MAXFRAG);
+ sblock.fs_fsize = sblock.fs_bsize / MAXFRAG;
+ }
+ if (maxbsize < bsize || !POWEROF2(maxbsize)) {
+ sblock.fs_maxbsize = sblock.fs_bsize;
+ printf("Extent size set to %d\n", sblock.fs_maxbsize);
+ } else if (sblock.fs_maxbsize > FS_MAXCONTIG * sblock.fs_bsize) {
+ sblock.fs_maxbsize = FS_MAXCONTIG * sblock.fs_bsize;
+ printf("Extent size reduced to %d\n", sblock.fs_maxbsize);
+ } else {
+ sblock.fs_maxbsize = maxbsize;
+ }
+ sblock.fs_maxcontig = maxcontig;
+ if (sblock.fs_maxcontig < sblock.fs_maxbsize / sblock.fs_bsize) {
+ sblock.fs_maxcontig = sblock.fs_maxbsize / sblock.fs_bsize;
+ printf("Maxcontig raised to %d\n", sblock.fs_maxbsize);
+ }
+ if (sblock.fs_maxcontig > 1)
+ sblock.fs_contigsumsize = MIN(sblock.fs_maxcontig,FS_MAXCONTIG);
+ sblock.fs_bmask = ~(sblock.fs_bsize - 1);
+ sblock.fs_fmask = ~(sblock.fs_fsize - 1);
+ sblock.fs_qbmask = ~sblock.fs_bmask;
+ sblock.fs_qfmask = ~sblock.fs_fmask;
+ sblock.fs_bshift = ilog2(sblock.fs_bsize);
+ sblock.fs_fshift = ilog2(sblock.fs_fsize);
+ sblock.fs_frag = numfrags(&sblock, sblock.fs_bsize);
+ sblock.fs_fragshift = ilog2(sblock.fs_frag);
+ if (sblock.fs_frag > MAXFRAG) {
+ printf("fragment size %d is still too small (can't happen)\n",
+ sblock.fs_bsize / MAXFRAG);
+ exit(21);
+ }
+ sblock.fs_fsbtodb = ilog2(sblock.fs_fsize / sectorsize);
+ sblock.fs_size = fssize = dbtofsb(&sblock, fssize);
+
+ /*
+ * Before the filesystem is finally initialized, mark it
+ * as incompletely initialized.
+ */
+ sblock.fs_magic = FS_BAD_MAGIC;
+
+ if (Oflag == 1) {
+ sblock.fs_sblockloc = SBLOCK_UFS1;
+ sblock.fs_nindir = sblock.fs_bsize / sizeof(ufs1_daddr_t);
+ sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs1_dinode);
+ sblock.fs_maxsymlinklen = ((NDADDR + NIADDR) *
+ sizeof(ufs1_daddr_t));
+ sblock.fs_old_inodefmt = FS_44INODEFMT;
+ sblock.fs_old_cgoffset = 0;
+ sblock.fs_old_cgmask = 0xffffffff;
+ sblock.fs_old_size = sblock.fs_size;
+ sblock.fs_old_rotdelay = 0;
+ sblock.fs_old_rps = 60;
+ sblock.fs_old_nspf = sblock.fs_fsize / sectorsize;
+ sblock.fs_old_cpg = 1;
+ sblock.fs_old_interleave = 1;
+ sblock.fs_old_trackskew = 0;
+ sblock.fs_old_cpc = 0;
+ sblock.fs_old_postblformat = 1;
+ sblock.fs_old_nrpos = 1;
+ } else {
+ sblock.fs_sblockloc = SBLOCK_UFS2;
+ sblock.fs_nindir = sblock.fs_bsize / sizeof(ufs2_daddr_t);
+ sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs2_dinode);
+ sblock.fs_maxsymlinklen = ((NDADDR + NIADDR) *
+ sizeof(ufs2_daddr_t));
+ }
+ sblock.fs_sblkno =
+ roundup(howmany(sblock.fs_sblockloc + SBLOCKSIZE, sblock.fs_fsize),
+ sblock.fs_frag);
+ sblock.fs_cblkno = sblock.fs_sblkno +
+ roundup(howmany(SBLOCKSIZE, sblock.fs_fsize), sblock.fs_frag);
+ sblock.fs_iblkno = sblock.fs_cblkno + sblock.fs_frag;
+ sblock.fs_maxfilesize = sblock.fs_bsize * NDADDR - 1;
+ for (sizepb = sblock.fs_bsize, i = 0; i < NIADDR; i++) {
+ sizepb *= NINDIR(&sblock);
+ sblock.fs_maxfilesize += sizepb;
+ }
+
+ /*
+ * It's impossible to create a snapshot in case that fs_maxfilesize
+ * is smaller than the fssize.
+ */
+ if (sblock.fs_maxfilesize < (u_quad_t)fssize) {
+ warnx("WARNING: You will be unable to create snapshots on this "
+ "file system. Correct by using a larger blocksize.");
+ }
+
+ /*
+ * Calculate the number of blocks to put into each cylinder group.
+ *
+ * This algorithm selects the number of blocks per cylinder
+ * group. The first goal is to have at least enough data blocks
+ * in each cylinder group to meet the density requirement. Once
+ * this goal is achieved we try to expand to have at least
+ * MINCYLGRPS cylinder groups. Once this goal is achieved, we
+ * pack as many blocks into each cylinder group map as will fit.
+ *
+ * We start by calculating the smallest number of blocks that we
+ * can put into each cylinder group. If this is too big, we reduce
+ * the density until it fits.
+ */
+ origdensity = density;
+ for (;;) {
+ fragsperinode = MAX(numfrags(&sblock, density), 1);
+ minfpg = fragsperinode * INOPB(&sblock);
+ if (minfpg > sblock.fs_size)
+ minfpg = sblock.fs_size;
+ sblock.fs_ipg = INOPB(&sblock);
+ sblock.fs_fpg = roundup(sblock.fs_iblkno +
+ sblock.fs_ipg / INOPF(&sblock), sblock.fs_frag);
+ if (sblock.fs_fpg < minfpg)
+ sblock.fs_fpg = minfpg;
+ sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+ INOPB(&sblock));
+ sblock.fs_fpg = roundup(sblock.fs_iblkno +
+ sblock.fs_ipg / INOPF(&sblock), sblock.fs_frag);
+ if (sblock.fs_fpg < minfpg)
+ sblock.fs_fpg = minfpg;
+ sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+ INOPB(&sblock));
+ if (CGSIZE(&sblock) < (unsigned long)sblock.fs_bsize)
+ break;
+ density -= sblock.fs_fsize;
+ }
+ if (density != origdensity)
+ printf("density reduced from %d to %d\n", origdensity, density);
+ /*
+ * Start packing more blocks into the cylinder group until
+ * it cannot grow any larger, the number of cylinder groups
+ * drops below MINCYLGRPS, or we reach the size requested.
+ */
+ for ( ; sblock.fs_fpg < maxblkspercg; sblock.fs_fpg += sblock.fs_frag) {
+ sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+ INOPB(&sblock));
+ if (sblock.fs_size / sblock.fs_fpg < MINCYLGRPS)
+ break;
+ if (CGSIZE(&sblock) < (unsigned long)sblock.fs_bsize)
+ continue;
+ if (CGSIZE(&sblock) == (unsigned long)sblock.fs_bsize)
+ break;
+ sblock.fs_fpg -= sblock.fs_frag;
+ sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+ INOPB(&sblock));
+ break;
+ }
+ /*
+ * Check to be sure that the last cylinder group has enough blocks
+ * to be viable. If it is too small, reduce the number of blocks
+ * per cylinder group which will have the effect of moving more
+ * blocks into the last cylinder group.
+ */
+ optimalfpg = sblock.fs_fpg;
+ for (;;) {
+ sblock.fs_ncg = howmany(sblock.fs_size, sblock.fs_fpg);
+ lastminfpg = roundup(sblock.fs_iblkno +
+ sblock.fs_ipg / INOPF(&sblock), sblock.fs_frag);
+ if (sblock.fs_size < lastminfpg) {
+ printf("Filesystem size %jd < minimum size of %d\n",
+ (intmax_t)sblock.fs_size, lastminfpg);
+ exit(28);
+ }
+ if (sblock.fs_size % sblock.fs_fpg >= lastminfpg ||
+ sblock.fs_size % sblock.fs_fpg == 0)
+ break;
+ sblock.fs_fpg -= sblock.fs_frag;
+ sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+ INOPB(&sblock));
+ }
+ if (optimalfpg != sblock.fs_fpg)
+ printf("Reduced frags per cylinder group from %d to %d %s\n",
+ optimalfpg, sblock.fs_fpg, "to enlarge last cyl group");
+ sblock.fs_cgsize = fragroundup(&sblock, CGSIZE(&sblock));
+ sblock.fs_dblkno = sblock.fs_iblkno + sblock.fs_ipg / INOPF(&sblock);
+ if (Oflag == 1) {
+ sblock.fs_old_spc = sblock.fs_fpg * sblock.fs_old_nspf;
+ sblock.fs_old_nsect = sblock.fs_old_spc;
+ sblock.fs_old_npsect = sblock.fs_old_spc;
+ sblock.fs_old_ncyl = sblock.fs_ncg;
+ }
+ /*
+ * fill in remaining fields of the super block
+ */
+ sblock.fs_csaddr = cgdmin(&sblock, 0);
+ sblock.fs_cssize =
+ fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum));
+ fscs = (struct csum *)calloc(1, sblock.fs_cssize);
+ if (fscs == NULL)
+ errx(31, "calloc failed");
+ sblock.fs_sbsize = fragroundup(&sblock, sizeof(struct fs));
+ if (sblock.fs_sbsize > SBLOCKSIZE)
+ sblock.fs_sbsize = SBLOCKSIZE;
+ sblock.fs_minfree = minfree;
+ sblock.fs_maxbpg = maxbpg;
+ sblock.fs_optim = opt;
+ sblock.fs_cgrotor = 0;
+ sblock.fs_pendingblocks = 0;
+ sblock.fs_pendinginodes = 0;
+ sblock.fs_fmod = 0;
+ sblock.fs_ronly = 0;
+ sblock.fs_state = 0;
+ sblock.fs_clean = 1;
+ sblock.fs_id[0] = (long)utime;
+ sblock.fs_id[1] = newfs_random();
+ sblock.fs_fsmnt[0] = '\0';
+ csfrags = howmany(sblock.fs_cssize, sblock.fs_fsize);
+ sblock.fs_dsize = sblock.fs_size - sblock.fs_sblkno -
+ sblock.fs_ncg * (sblock.fs_dblkno - sblock.fs_sblkno);
+ sblock.fs_cstotal.cs_nbfree =
+ fragstoblks(&sblock, sblock.fs_dsize) -
+ howmany(csfrags, sblock.fs_frag);
+ sblock.fs_cstotal.cs_nffree =
+ fragnum(&sblock, sblock.fs_size) +
+ (fragnum(&sblock, csfrags) > 0 ?
+ sblock.fs_frag - fragnum(&sblock, csfrags) : 0);
+ sblock.fs_cstotal.cs_nifree = sblock.fs_ncg * sblock.fs_ipg - ROOTINO;
+ sblock.fs_cstotal.cs_ndir = 0;
+ sblock.fs_dsize -= csfrags;
+ sblock.fs_time = utime;
+ if (Oflag == 1) {
+ sblock.fs_old_time = utime;
+ sblock.fs_old_dsize = sblock.fs_dsize;
+ sblock.fs_old_csaddr = sblock.fs_csaddr;
+ sblock.fs_old_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir;
+ sblock.fs_old_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree;
+ sblock.fs_old_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree;
+ sblock.fs_old_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree;
+ }
+
+ /*
+ * Dump out summary information about file system.
+ */
+# define B2MBFACTOR (1 / (1024.0 * 1024.0))
+ printf("%s: %.1fMB (%jd sectors) block size %d, fragment size %d\n",
+ fsys, (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR,
+ (intmax_t)fsbtodb(&sblock, sblock.fs_size), sblock.fs_bsize,
+ sblock.fs_fsize);
+ printf("\tusing %d cylinder groups of %.2fMB, %d blks, %d inodes.\n",
+ sblock.fs_ncg, (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR,
+ sblock.fs_fpg / sblock.fs_frag, sblock.fs_ipg);
+ if (sblock.fs_flags & FS_DOSOFTDEP)
+ printf("\twith soft updates\n");
+# undef B2MBFACTOR
+
+ /*
+ * Wipe out old UFS1 superblock(s) if necessary.
+ */
+ if (!Nflag && Oflag != 1) {
+ i = bread(&disk, SBLOCK_UFS1 / disk.d_bsize, chdummy, SBLOCKSIZE);
+ if (i == -1)
+ err(1, "can't read old UFS1 superblock: %s", disk.d_error);
+
+ if (fsdummy.fs_magic == FS_UFS1_MAGIC) {
+ fsdummy.fs_magic = 0;
+ bwrite(&disk, SBLOCK_UFS1 / disk.d_bsize, chdummy, SBLOCKSIZE);
+ for (i = 0; i < fsdummy.fs_ncg; i++)
+ bwrite(&disk, fsbtodb(&fsdummy, cgsblock(&fsdummy, i)),
+ chdummy, SBLOCKSIZE);
+ }
+ }
+ if (!Nflag)
+ sbwrite(&disk, 0);
+ if (Eflag == 1) {
+ printf("** Exiting on Eflag 1\n");
+ exit(0);
+ }
+ if (Eflag == 2)
+ printf("** Leaving BAD MAGIC on Eflag 2\n");
+ else
+ sblock.fs_magic = (Oflag != 1) ? FS_UFS2_MAGIC : FS_UFS1_MAGIC;
+
+ /*
+ * Now build the cylinders group blocks and
+ * then print out indices of cylinder groups.
+ */
+ printf("super-block backups (for fsck -b #) at:\n");
+ i = 0;
+ width = charsperline();
+ /*
+ * allocate space for superblock, cylinder group map, and
+ * two sets of inode blocks.
+ */
+ if (sblock.fs_bsize < SBLOCKSIZE)
+ iobufsize = SBLOCKSIZE + 3 * sblock.fs_bsize;
+ else
+ iobufsize = 4 * sblock.fs_bsize;
+ if ((iobuf = malloc(iobufsize)) == 0) {
+ printf("Cannot allocate I/O buffer\n");
+ exit(38);
+ }
+ bzero(iobuf, iobufsize);
+ /*
+ * Make a copy of the superblock into the buffer that we will be
+ * writing out in each cylinder group.
+ */
+ bcopy((char *)&sblock, iobuf, SBLOCKSIZE);
+ for (cylno = 0; cylno < sblock.fs_ncg; cylno++) {
+ initcg(cylno, utime);
+ j = snprintf(tmpbuf, sizeof(tmpbuf), " %jd%s",
+ (intmax_t)fsbtodb(&sblock, cgsblock(&sblock, cylno)),
+ cylno < (sblock.fs_ncg-1) ? "," : "");
+ if (j < 0)
+ tmpbuf[j = 0] = '\0';
+ if (i + j >= width) {
+ printf("\n");
+ i = 0;
+ }
+ i += j;
+ printf("%s", tmpbuf);
+ fflush(stdout);
+ }
+ printf("\n");
+ if (Nflag)
+ exit(0);
+ /*
+ * Now construct the initial file system,
+ * then write out the super-block.
+ */
+ fsinit(utime);
+ if (Oflag == 1) {
+ sblock.fs_old_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir;
+ sblock.fs_old_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree;
+ sblock.fs_old_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree;
+ sblock.fs_old_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree;
+ }
+ if (Eflag == 3) {
+ printf("** Exiting on Eflag 3\n");
+ exit(0);
+ }
+ if (!Nflag)
+ sbwrite(&disk, 0);
+ for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize)
+ wtfs(fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)),
+ sblock.fs_cssize - i < sblock.fs_bsize ?
+ sblock.fs_cssize - i : sblock.fs_bsize,
+ ((char *)fscs) + i);
+ /*
+ * Update information about this partion in pack
+ * label, to that it may be updated on disk.
+ */
+ if (pp != NULL) {
+ pp->p_fstype = FS_BSDFFS;
+ pp->p_fsize = sblock.fs_fsize;
+ pp->p_frag = sblock.fs_frag;
+ pp->p_cpg = sblock.fs_fpg;
+ }
+}
+
+/*
+ * Initialize a cylinder group.
+ */
+void
+initcg(int cylno, time_t utime)
+{
+ long i, j, d, dlower, dupper, blkno, start;
+ ufs2_daddr_t cbase, dmax;
+ struct ufs1_dinode *dp1;
+ struct ufs2_dinode *dp2;
+ struct csum *cs;
+
+ /*
+ * Determine block bounds for cylinder group.
+ * Allow space for super block summary information in first
+ * cylinder group.
+ */
+ cbase = cgbase(&sblock, cylno);
+ dmax = cbase + sblock.fs_fpg;
+ if (dmax > sblock.fs_size)
+ dmax = sblock.fs_size;
+ dlower = cgsblock(&sblock, cylno) - cbase;
+ dupper = cgdmin(&sblock, cylno) - cbase;
+ if (cylno == 0)
+ dupper += howmany(sblock.fs_cssize, sblock.fs_fsize);
+ cs = &fscs[cylno];
+ memset(&acg, 0, sblock.fs_cgsize);
+ acg.cg_time = utime;
+ acg.cg_magic = CG_MAGIC;
+ acg.cg_cgx = cylno;
+ acg.cg_niblk = sblock.fs_ipg;
+ acg.cg_initediblk = sblock.fs_ipg < 2 * INOPB(&sblock) ?
+ sblock.fs_ipg : 2 * INOPB(&sblock);
+ acg.cg_ndblk = dmax - cbase;
+ if (sblock.fs_contigsumsize > 0)
+ acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag;
+ start = &acg.cg_space[0] - (u_char *)(&acg.cg_firstfield);
+ if (Oflag == 2) {
+ acg.cg_iusedoff = start;
+ } else {
+ acg.cg_old_ncyl = sblock.fs_old_cpg;
+ acg.cg_old_time = acg.cg_time;
+ acg.cg_time = 0;
+ acg.cg_old_niblk = acg.cg_niblk;
+ acg.cg_niblk = 0;
+ acg.cg_initediblk = 0;
+ acg.cg_old_btotoff = start;
+ acg.cg_old_boff = acg.cg_old_btotoff +
+ sblock.fs_old_cpg * sizeof(int32_t);
+ acg.cg_iusedoff = acg.cg_old_boff +
+ sblock.fs_old_cpg * sizeof(u_int16_t);
+ }
+ acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT);
+ acg.cg_nextfreeoff = acg.cg_freeoff + howmany(sblock.fs_fpg, CHAR_BIT);
+ if (sblock.fs_contigsumsize > 0) {
+ acg.cg_clustersumoff =
+ roundup(acg.cg_nextfreeoff, sizeof(u_int32_t));
+ acg.cg_clustersumoff -= sizeof(u_int32_t);
+ acg.cg_clusteroff = acg.cg_clustersumoff +
+ (sblock.fs_contigsumsize + 1) * sizeof(u_int32_t);
+ acg.cg_nextfreeoff = acg.cg_clusteroff +
+ howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT);
+ }
+ if (acg.cg_nextfreeoff > sblock.fs_cgsize) {
+ printf("Panic: cylinder group too big\n");
+ exit(37);
+ }
+ acg.cg_cs.cs_nifree += sblock.fs_ipg;
+ if (cylno == 0)
+ for (i = 0; i < (long)ROOTINO; i++) {
+ setbit(cg_inosused(&acg), i);
+ acg.cg_cs.cs_nifree--;
+ }
+ if (cylno > 0) {
+ /*
+ * In cylno 0, beginning space is reserved
+ * for boot and super blocks.
+ */
+ for (d = 0; d < dlower; d += sblock.fs_frag) {
+ blkno = d / sblock.fs_frag;
+ setblock(&sblock, cg_blksfree(&acg), blkno);
+ if (sblock.fs_contigsumsize > 0)
+ setbit(cg_clustersfree(&acg), blkno);
+ acg.cg_cs.cs_nbfree++;
+ }
+ }
+ if ((i = dupper % sblock.fs_frag)) {
+ acg.cg_frsum[sblock.fs_frag - i]++;
+ for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) {
+ setbit(cg_blksfree(&acg), dupper);
+ acg.cg_cs.cs_nffree++;
+ }
+ }
+ for (d = dupper; d + sblock.fs_frag <= acg.cg_ndblk;
+ d += sblock.fs_frag) {
+ blkno = d / sblock.fs_frag;
+ setblock(&sblock, cg_blksfree(&acg), blkno);
+ if (sblock.fs_contigsumsize > 0)
+ setbit(cg_clustersfree(&acg), blkno);
+ acg.cg_cs.cs_nbfree++;
+ }
+ if (d < acg.cg_ndblk) {
+ acg.cg_frsum[acg.cg_ndblk - d]++;
+ for (; d < acg.cg_ndblk; d++) {
+ setbit(cg_blksfree(&acg), d);
+ acg.cg_cs.cs_nffree++;
+ }
+ }
+ if (sblock.fs_contigsumsize > 0) {
+ int32_t *sump = cg_clustersum(&acg);
+ u_char *mapp = cg_clustersfree(&acg);
+ int map = *mapp++;
+ int bit = 1;
+ int run = 0;
+
+ for (i = 0; i < acg.cg_nclusterblks; i++) {
+ if ((map & bit) != 0)
+ run++;
+ else if (run != 0) {
+ if (run > sblock.fs_contigsumsize)
+ run = sblock.fs_contigsumsize;
+ sump[run]++;
+ run = 0;
+ }
+ if ((i & (CHAR_BIT - 1)) != CHAR_BIT - 1)
+ bit <<= 1;
+ else {
+ map = *mapp++;
+ bit = 1;
+ }
+ }
+ if (run != 0) {
+ if (run > sblock.fs_contigsumsize)
+ run = sblock.fs_contigsumsize;
+ sump[run]++;
+ }
+ }
+ *cs = acg.cg_cs;
+ /*
+ * Write out the duplicate super block, the cylinder group map
+ * and two blocks worth of inodes in a single write.
+ */
+ start = sblock.fs_bsize > SBLOCKSIZE ? sblock.fs_bsize : SBLOCKSIZE;
+ bcopy((char *)&acg, &iobuf[start], sblock.fs_cgsize);
+ start += sblock.fs_bsize;
+ dp1 = (struct ufs1_dinode *)(&iobuf[start]);
+ dp2 = (struct ufs2_dinode *)(&iobuf[start]);
+ for (i = 0; i < acg.cg_initediblk; i++) {
+ if (sblock.fs_magic == FS_UFS1_MAGIC) {
+ dp1->di_gen = newfs_random();
+ dp1++;
+ } else {
+ dp2->di_gen = newfs_random();
+ dp2++;
+ }
+ }
+ wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)), iobufsize, iobuf);
+ /*
+ * For the old file system, we have to initialize all the inodes.
+ */
+ if (Oflag == 1) {
+ for (i = 2 * sblock.fs_frag;
+ i < sblock.fs_ipg / INOPF(&sblock);
+ i += sblock.fs_frag) {
+ dp1 = (struct ufs1_dinode *)(&iobuf[start]);
+ for (j = 0; j < INOPB(&sblock); j++) {
+ dp1->di_gen = newfs_random();
+ dp1++;
+ }
+ wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno) + i),
+ sblock.fs_bsize, &iobuf[start]);
+ }
+ }
+}
+
+/*
+ * initialize the file system
+ */
+#define ROOTLINKCNT 3
+
+struct direct root_dir[] = {
+ { ROOTINO, sizeof(struct direct), DT_DIR, 1, "." },
+ { ROOTINO, sizeof(struct direct), DT_DIR, 2, ".." },
+ { ROOTINO + 1, sizeof(struct direct), DT_DIR, 5, ".snap" },
+};
+
+#define SNAPLINKCNT 2
+
+struct direct snap_dir[] = {
+ { ROOTINO + 1, sizeof(struct direct), DT_DIR, 1, "." },
+ { ROOTINO, sizeof(struct direct), DT_DIR, 2, ".." },
+};
+
+void
+fsinit(time_t utime)
+{
+ union dinode node;
+ struct group *grp;
+ gid_t gid;
+ int entries;
+
+ memset(&node, 0, sizeof node);
+ if ((grp = getgrnam("operator")) != NULL) {
+ gid = grp->gr_gid;
+ } else {
+ warnx("Cannot retrieve operator gid.");
+ gid = 0;
+ }
+ entries = (nflag) ? ROOTLINKCNT - 1: ROOTLINKCNT;
+ if (sblock.fs_magic == FS_UFS1_MAGIC) {
+ /*
+ * initialize the node
+ */
+ node.dp1.di_atime = utime;
+ node.dp1.di_mtime = utime;
+ node.dp1.di_ctime = utime;
+ /*
+ * create the root directory
+ */
+ node.dp1.di_mode = IFDIR | UMASK;
+ node.dp1.di_nlink = entries;
+ node.dp1.di_size = makedir(root_dir, entries);
+ node.dp1.di_db[0] = alloc(sblock.fs_fsize, node.dp1.di_mode);
+ node.dp1.di_blocks =
+ btodb(fragroundup(&sblock, node.dp1.di_size));
+ wtfs(fsbtodb(&sblock, node.dp1.di_db[0]), sblock.fs_fsize,
+ iobuf);
+ iput(&node, ROOTINO);
+ if (!nflag) {
+ /*
+ * create the .snap directory
+ */
+ node.dp1.di_mode |= 020;
+ node.dp1.di_gid = gid;
+ node.dp1.di_nlink = SNAPLINKCNT;
+ node.dp1.di_size = makedir(snap_dir, SNAPLINKCNT);
+ node.dp1.di_db[0] =
+ alloc(sblock.fs_fsize, node.dp1.di_mode);
+ node.dp1.di_blocks =
+ btodb(fragroundup(&sblock, node.dp1.di_size));
+ wtfs(fsbtodb(&sblock, node.dp1.di_db[0]),
+ sblock.fs_fsize, iobuf);
+ iput(&node, ROOTINO + 1);
+ }
+ } else {
+ /*
+ * initialize the node
+ */
+ node.dp2.di_atime = utime;
+ node.dp2.di_mtime = utime;
+ node.dp2.di_ctime = utime;
+ node.dp2.di_birthtime = utime;
+ /*
+ * create the root directory
+ */
+ node.dp2.di_mode = IFDIR | UMASK;
+ node.dp2.di_nlink = entries;
+ node.dp2.di_size = makedir(root_dir, entries);
+ node.dp2.di_db[0] = alloc(sblock.fs_fsize, node.dp2.di_mode);
+ node.dp2.di_blocks =
+ btodb(fragroundup(&sblock, node.dp2.di_size));
+ wtfs(fsbtodb(&sblock, node.dp2.di_db[0]), sblock.fs_fsize,
+ iobuf);
+ iput(&node, ROOTINO);
+ if (!nflag) {
+ /*
+ * create the .snap directory
+ */
+ node.dp2.di_mode |= 020;
+ node.dp2.di_gid = gid;
+ node.dp2.di_nlink = SNAPLINKCNT;
+ node.dp2.di_size = makedir(snap_dir, SNAPLINKCNT);
+ node.dp2.di_db[0] =
+ alloc(sblock.fs_fsize, node.dp2.di_mode);
+ node.dp2.di_blocks =
+ btodb(fragroundup(&sblock, node.dp2.di_size));
+ wtfs(fsbtodb(&sblock, node.dp2.di_db[0]),
+ sblock.fs_fsize, iobuf);
+ iput(&node, ROOTINO + 1);
+ }
+ }
+}
+
+/*
+ * construct a set of directory entries in "iobuf".
+ * return size of directory.
+ */
+int
+makedir(struct direct *protodir, int entries)
+{
+ char *cp;
+ int i, spcleft;
+
+ spcleft = DIRBLKSIZ;
+ memset(iobuf, 0, DIRBLKSIZ);
+ for (cp = iobuf, i = 0; i < entries - 1; i++) {
+ protodir[i].d_reclen = DIRSIZ(0, &protodir[i]);
+ memmove(cp, &protodir[i], protodir[i].d_reclen);
+ cp += protodir[i].d_reclen;
+ spcleft -= protodir[i].d_reclen;
+ }
+ protodir[i].d_reclen = spcleft;
+ memmove(cp, &protodir[i], DIRSIZ(0, &protodir[i]));
+ return (DIRBLKSIZ);
+}
+
+/*
+ * allocate a block or frag
+ */
+ufs2_daddr_t
+alloc(int size, int mode)
+{
+ int i, d, blkno, frag;
+
+ bread(&disk, fsbtodb(&sblock, cgtod(&sblock, 0)), (char *)&acg,
+ sblock.fs_cgsize);
+ if (acg.cg_magic != CG_MAGIC) {
+ printf("cg 0: bad magic number\n");
+ exit(38);
+ }
+ if (acg.cg_cs.cs_nbfree == 0) {
+ printf("first cylinder group ran out of space\n");
+ exit(39);
+ }
+ for (d = 0; d < acg.cg_ndblk; d += sblock.fs_frag)
+ if (isblock(&sblock, cg_blksfree(&acg), d / sblock.fs_frag))
+ goto goth;
+ printf("internal error: can't find block in cyl 0\n");
+ exit(40);
+goth:
+ blkno = fragstoblks(&sblock, d);
+ clrblock(&sblock, cg_blksfree(&acg), blkno);
+ if (sblock.fs_contigsumsize > 0)
+ clrbit(cg_clustersfree(&acg), blkno);
+ acg.cg_cs.cs_nbfree--;
+ sblock.fs_cstotal.cs_nbfree--;
+ fscs[0].cs_nbfree--;
+ if (mode & IFDIR) {
+ acg.cg_cs.cs_ndir++;
+ sblock.fs_cstotal.cs_ndir++;
+ fscs[0].cs_ndir++;
+ }
+ if (size != sblock.fs_bsize) {
+ frag = howmany(size, sblock.fs_fsize);
+ fscs[0].cs_nffree += sblock.fs_frag - frag;
+ sblock.fs_cstotal.cs_nffree += sblock.fs_frag - frag;
+ acg.cg_cs.cs_nffree += sblock.fs_frag - frag;
+ acg.cg_frsum[sblock.fs_frag - frag]++;
+ for (i = frag; i < sblock.fs_frag; i++)
+ setbit(cg_blksfree(&acg), d + i);
+ }
+ /* XXX cgwrite(&disk, 0)??? */
+ wtfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize,
+ (char *)&acg);
+ return ((ufs2_daddr_t)d);
+}
+
+/*
+ * Allocate an inode on the disk
+ */
+void
+iput(union dinode *ip, ino_t ino)
+{
+ ufs2_daddr_t d;
+ int c;
+
+ c = ino_to_cg(&sblock, ino);
+ bread(&disk, fsbtodb(&sblock, cgtod(&sblock, 0)), (char *)&acg,
+ sblock.fs_cgsize);
+ if (acg.cg_magic != CG_MAGIC) {
+ printf("cg 0: bad magic number\n");
+ exit(31);
+ }
+ acg.cg_cs.cs_nifree--;
+ setbit(cg_inosused(&acg), ino);
+ wtfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize,
+ (char *)&acg);
+ sblock.fs_cstotal.cs_nifree--;
+ fscs[0].cs_nifree--;
+ if (ino >= (unsigned long)sblock.fs_ipg * sblock.fs_ncg) {
+ printf("fsinit: inode value out of range (%d).\n", ino);
+ exit(32);
+ }
+ d = fsbtodb(&sblock, ino_to_fsba(&sblock, ino));
+ bread(&disk, d, (char *)iobuf, sblock.fs_bsize);
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ ((struct ufs1_dinode *)iobuf)[ino_to_fsbo(&sblock, ino)] =
+ ip->dp1;
+ else
+ ((struct ufs2_dinode *)iobuf)[ino_to_fsbo(&sblock, ino)] =
+ ip->dp2;
+ wtfs(d, sblock.fs_bsize, (char *)iobuf);
+}
+
+/*
+ * possibly write to disk
+ */
+static void
+wtfs(ufs2_daddr_t bno, int size, char *bf)
+{
+ if (Nflag)
+ return;
+ if (bwrite(&disk, bno, bf, size) < 0)
+ err(36, "wtfs: %d bytes at sector %jd", size, (intmax_t)bno);
+}
+
+/*
+ * check if a block is available
+ */
+static int
+isblock(struct fs *fs, unsigned char *cp, int h)
+{
+ unsigned char mask;
+
+ switch (fs->fs_frag) {
+ case 8:
+ return (cp[h] == 0xff);
+ case 4:
+ mask = 0x0f << ((h & 0x1) << 2);
+ return ((cp[h >> 1] & mask) == mask);
+ case 2:
+ mask = 0x03 << ((h & 0x3) << 1);
+ return ((cp[h >> 2] & mask) == mask);
+ case 1:
+ mask = 0x01 << (h & 0x7);
+ return ((cp[h >> 3] & mask) == mask);
+ default:
+ fprintf(stderr, "isblock bad fs_frag %d\n", fs->fs_frag);
+ return (0);
+ }
+}
+
+/*
+ * take a block out of the map
+ */
+static void
+clrblock(struct fs *fs, unsigned char *cp, int h)
+{
+ switch ((fs)->fs_frag) {
+ case 8:
+ cp[h] = 0;
+ return;
+ case 4:
+ cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2));
+ return;
+ case 2:
+ cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1));
+ return;
+ case 1:
+ cp[h >> 3] &= ~(0x01 << (h & 0x7));
+ return;
+ default:
+ fprintf(stderr, "clrblock bad fs_frag %d\n", fs->fs_frag);
+ return;
+ }
+}
+
+/*
+ * put a block into the map
+ */
+static void
+setblock(struct fs *fs, unsigned char *cp, int h)
+{
+ switch (fs->fs_frag) {
+ case 8:
+ cp[h] = 0xff;
+ return;
+ case 4:
+ cp[h >> 1] |= (0x0f << ((h & 0x1) << 2));
+ return;
+ case 2:
+ cp[h >> 2] |= (0x03 << ((h & 0x3) << 1));
+ return;
+ case 1:
+ cp[h >> 3] |= (0x01 << (h & 0x7));
+ return;
+ default:
+ fprintf(stderr, "setblock bad fs_frag %d\n", fs->fs_frag);
+ return;
+ }
+}
+
+/*
+ * Determine the number of characters in a
+ * single line.
+ */
+
+static int
+charsperline(void)
+{
+ int columns;
+ char *cp;
+ struct winsize ws;
+
+ columns = 0;
+ if (ioctl(0, TIOCGWINSZ, &ws) != -1)
+ columns = ws.ws_col;
+ if (columns == 0 && (cp = getenv("COLUMNS")))
+ columns = atoi(cp);
+ if (columns == 0)
+ columns = 80; /* last resort */
+ return (columns);
+}
+
+static int
+ilog2(int val)
+{
+ u_int n;
+
+ for (n = 0; n < sizeof(n) * CHAR_BIT; n++)
+ if (1 << n == val)
+ return (n);
+ errx(1, "ilog2: %d is not a power of 2\n", val);
+}
+
+/*
+ * For the regression test, return predictable random values.
+ * Otherwise use a true random number generator.
+ */
+static u_int32_t
+newfs_random(void)
+{
+ static int nextnum = 1;
+
+ if (Rflag)
+ return (nextnum++);
+ return (arc4random());
+}
diff --git a/sbin/newfs/newfs.8 b/sbin/newfs/newfs.8
new file mode 100644
index 0000000..f907642
--- /dev/null
+++ b/sbin/newfs/newfs.8
@@ -0,0 +1,264 @@
+.\" Copyright (c) 1983, 1987, 1991, 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.
+.\" 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 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.
+.\"
+.\" @(#)newfs.8 8.6 (Berkeley) 5/3/95
+.\" $FreeBSD$
+.\"
+.Dd January 21, 2005
+.Dt NEWFS 8
+.Os
+.Sh NAME
+.Nm newfs
+.Nd construct a new UFS1/UFS2 file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl NUln
+.Op Fl L Ar volname
+.Op Fl O Ar filesystem-type
+.Op Fl S Ar sector-size
+.Op Fl T Ar disktype
+.Op Fl a Ar maxcontig
+.Op Fl b Ar block-size
+.Op Fl c Ar blocks-per-cylinder-group
+.Op Fl d Ar max-extent-size
+.Op Fl e Ar maxbpg
+.Op Fl f Ar frag-size
+.Op Fl g Ar avgfilesize
+.Op Fl h Ar avgfpdir
+.Op Fl i Ar bytes
+.Op Fl m Ar free-space
+.Op Fl o Ar optimization
+.Op Fl s Ar size
+.Ar special
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to initialize and clear file systems before first use.
+Before running
+.Nm
+the disk must be labeled using
+.Xr bsdlabel 8 .
+The
+.Nm
+utility builds a file system on the specified special file.
+(We often refer to the
+.Dq special file
+as the
+.Dq disk ,
+although the special file need not be a physical disk.
+In fact, it need not even be special.)
+Typically the defaults are reasonable, however
+.Nm
+has numerous options to allow the defaults to be selectively overridden.
+.Pp
+The following options define the general layout policies:
+.Bl -tag -width indent
+.It Fl L Ar volname
+Add a volume label to the new file system.
+.It Fl N
+Cause the file system parameters to be printed out
+without really creating the file system.
+.It Fl O Ar filesystem-type
+Use 1 to specify that a UFS1 format file system be built;
+use 2 to specify that a UFS2 format file system be built.
+The default format is UFS2.
+.It Fl T Ar disktype
+For backward compatibility.
+.It Fl U
+Enable soft updates on the new file system.
+.It Fl a Ar maxcontig
+Specify the maximum number of contiguous blocks that will be
+laid out before forcing a rotational delay.
+The default value is 16.
+See
+.Xr tunefs 8
+for more details on how to set this option.
+.It Fl b Ar block-size
+The block size of the file system, in bytes.
+It must be a power of 2.
+The
+default size is 16384 bytes, and the smallest allowable size is 4096 bytes.
+The optimal block:fragment ratio is 8:1.
+Other ratios are possible, but are not recommended,
+and may produce poor results.
+.It Fl c Ar blocks-per-cylinder-group
+The number of blocks per cylinder group in a file system.
+The default is to compute the maximum allowed by the other parameters.
+This value is
+dependent on a number of other parameters, in particular the block size
+and the number of bytes per inode.
+.It Fl d Ar max-extent-size
+The file system may choose to store large files using extents.
+This parameter specifies the largest extent size that may be used.
+It is presently limited to its default value which is 16 times
+the file system blocksize.
+.It Fl e Ar maxbpg
+Indicate the maximum number of blocks any single file can
+allocate out of a cylinder group before it is forced to begin
+allocating blocks from another cylinder group.
+The default is about one quarter of the total blocks in a cylinder group.
+See
+.Xr tunefs 8
+for more details on how to set this option.
+.It Fl f Ar frag-size
+The fragment size of the file system in bytes.
+It must be a power of two
+ranging in value between
+.Ar blocksize Ns /8
+and
+.Ar blocksize .
+The default is 2048 bytes.
+.It Fl g Ar avgfilesize
+The expected average file size for the file system.
+.It Fl h Ar avgfpdir
+The expected average number of files per directory on the file system.
+.It Fl i Ar bytes
+Specify the density of inodes in the file system.
+The default is to create an inode for every
+.Pq 4 * Ar frag-size
+bytes of data space.
+If fewer inodes are desired, a larger number should be used;
+to create more inodes a smaller number should be given.
+One inode is required for each distinct file, so this value effectively
+specifies the average file size on the file system.
+.It Fl l
+Enable multilabel MAC on the new file system.
+.It Fl m Ar free-space
+The percentage of space reserved from normal users; the minimum free
+space threshold.
+The default value used is
+defined by
+.Dv MINFREE
+from
+.In ufs/ffs/fs.h ,
+currently 8%.
+See
+.Xr tunefs 8
+for more details on how to set this option.
+.It Fl n
+Do not create a
+.Pa .snap
+directory on the new file system.
+The resulting file system will not support snapshot generation, so
+.Xr dump 8
+in live mode and background
+.Xr fsck 8
+will not function properly.
+The traditional
+.Xr fsck 8
+and offline
+.Xr dump 8
+will work on the file system.
+This option is intended primarily for memory or vnode-backed file systems that
+do not require
+.Xr dump 8
+or
+.Xr fsck 8
+support.
+.It Fl o Ar optimization
+.Cm ( space
+or
+.Cm time ) .
+The file system can either be instructed to try to minimize the time spent
+allocating blocks, or to try to minimize the space fragmentation on the disk.
+If the value of minfree (see above) is less than 8%,
+the default is to optimize for
+.Cm space ;
+if the value of minfree is greater than or equal to 8%,
+the default is to optimize for
+.Cm time .
+See
+.Xr tunefs 8
+for more details on how to set this option.
+.It Fl s Ar size
+The size of the file system in sectors.
+This value defaults to the size of the
+raw partition specified in
+.Ar special
+(in other words,
+.Nm
+will use the entire partition for the file system).
+.El
+.Pp
+The following options override the standard sizes for the disk geometry.
+Their default values are taken from the disk label.
+Changing these defaults is useful only when using
+.Nm
+to build a file system whose raw image will eventually be used on a
+different type of disk than the one on which it is initially created
+(for example on a write-once disk).
+Note that changing any of these values from their defaults will make
+it impossible for
+.Xr fsck 8
+to find the alternate superblocks if the standard superblock is lost.
+.Bl -tag -width indent
+.It Fl S Ar sector-size
+The size of a sector in bytes (almost never anything but 512).
+.El
+.Sh EXAMPLES
+.Dl newfs /dev/ad3s1a
+.Pp
+Creates a new ufs file system on
+.Pa ad3s1a .
+The
+.Nm
+utility will use a block size of 16384 bytes, a fragment size of 2048 bytes
+and the largest possible number of blocks per cylinders group.
+These values tend to produce better performance for most applications
+than the historical defaults
+(8192 byte block size and 1024 byte fragment size).
+This large fragment size may lead to much wasted space
+on file systems that contain many small files.
+.Sh SEE ALSO
+.Xr fdformat 1 ,
+.Xr disktab 5 ,
+.Xr fs 5 ,
+.Xr bsdlabel 8 ,
+.Xr camcontrol 8 ,
+.Xr dump 8 ,
+.Xr dumpfs 8 ,
+.Xr fsck 8 ,
+.Xr mount 8 ,
+.Xr tunefs 8 ,
+.Xr vinum 8
+.Rs
+.%A M. McKusick
+.%A W. Joy
+.%A S. Leffler
+.%A R. Fabry
+.%T A Fast File System for UNIX
+.%J ACM Transactions on Computer Systems 2
+.%V 3
+.%P pp 181-197
+.%D August 1984
+.%O (reprinted in the BSD System Manager's Manual)
+.Re
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
diff --git a/sbin/newfs/newfs.c b/sbin/newfs/newfs.c
new file mode 100644
index 0000000..289d055
--- /dev/null
+++ b/sbin/newfs/newfs.c
@@ -0,0 +1,443 @@
+/*
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and Network Associates Laboratories, the Security
+ * Research Division of Network Associates, Inc. under DARPA/SPAWAR
+ * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+ * research program.
+ *
+ * Copyright (c) 1983, 1989, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)newfs.c 8.13 (Berkeley) 5/1/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * newfs: friendly front end to mkfs
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/file.h>
+#include <sys/mount.h>
+
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/ufsmount.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <paths.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "newfs.h"
+
+/*
+ * The following two constants set the default block and fragment sizes.
+ * Both constants must be a power of 2 and meet the following constraints:
+ * MINBSIZE <= DESBLKSIZE <= MAXBSIZE
+ * sectorsize <= DESFRAGSIZE <= DESBLKSIZE
+ * DESBLKSIZE / DESFRAGSIZE <= 8
+ */
+#define DFL_FRAGSIZE 2048
+#define DFL_BLKSIZE 16384
+
+/*
+ * Cylinder groups may have up to MAXBLKSPERCG blocks. The actual
+ * number used depends upon how much information can be stored
+ * in a cylinder group map which must fit in a single file system
+ * block. The default is to use as many as possible blocks per group.
+ */
+#define MAXBLKSPERCG 0x7fffffff /* desired fs_fpg ("infinity") */
+
+/*
+ * MAXBLKPG determines the maximum number of data blocks which are
+ * placed in a single cylinder group. The default is one indirect
+ * block worth of data blocks.
+ */
+#define MAXBLKPG(bsize) ((bsize) / sizeof(ufs2_daddr_t))
+
+/*
+ * Each file system has a number of inodes statically allocated.
+ * We allocate one inode slot per NFPI fragments, expecting this
+ * to be far more than we will ever need.
+ */
+#define NFPI 4
+
+int Lflag; /* add a volume label */
+int Nflag; /* run without writing file system */
+int Oflag = 2; /* file system format (1 => UFS1, 2 => UFS2) */
+int Rflag; /* regression test */
+int Uflag; /* enable soft updates for file system */
+int Eflag = 0; /* exit in middle of newfs for testing */
+int lflag; /* enable multilabel for file system */
+int nflag; /* do not create .snap directory */
+quad_t fssize; /* file system size */
+int sectorsize; /* bytes/sector */
+int realsectorsize; /* bytes/sector in hardware */
+int fsize = 0; /* fragment size */
+int bsize = 0; /* block size */
+int maxbsize = 0; /* maximum clustering */
+int maxblkspercg = MAXBLKSPERCG; /* maximum blocks per cylinder group */
+int minfree = MINFREE; /* free space threshold */
+int opt = DEFAULTOPT; /* optimization preference (space or time) */
+int density; /* number of bytes per inode */
+int maxcontig = 0; /* max contiguous blocks to allocate */
+int maxbpg; /* maximum blocks per file in a cyl group */
+int avgfilesize = AVFILESIZ;/* expected average file size */
+int avgfilesperdir = AFPDIR;/* expected number of files per directory */
+u_char *volumelabel = NULL; /* volume label for filesystem */
+struct uufsd disk; /* libufs disk structure */
+
+static char device[MAXPATHLEN];
+static char *disktype;
+static int unlabeled;
+
+static struct disklabel *getdisklabel(char *s);
+static void rewritelabel(char *s, struct disklabel *lp);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ struct partition *pp;
+ struct disklabel *lp;
+ struct partition oldpartition;
+ struct stat st;
+ char *cp, *special;
+ int ch, i;
+ off_t mediasize;
+
+ while ((ch = getopt(argc, argv,
+ "EL:NO:RS:T:Ua:b:c:d:e:f:g:h:i:lm:no:s:")) != -1)
+ switch (ch) {
+ case 'E':
+ Eflag++;
+ break;
+ case 'L':
+ volumelabel = optarg;
+ i = -1;
+ while (isalnum(volumelabel[++i]));
+ if (volumelabel[i] != '\0') {
+ errx(1, "bad volume label. Valid characters are alphanumerics.");
+ }
+ if (strlen(volumelabel) >= MAXVOLLEN) {
+ errx(1, "bad volume label. Length is longer than %d.",
+ MAXVOLLEN);
+ }
+ Lflag = 1;
+ break;
+ case 'N':
+ Nflag = 1;
+ break;
+ case 'O':
+ if ((Oflag = atoi(optarg)) < 1 || Oflag > 2)
+ errx(1, "%s: bad file system format value",
+ optarg);
+ break;
+ case 'R':
+ Rflag = 1;
+ break;
+ case 'S':
+ if ((sectorsize = atoi(optarg)) <= 0)
+ errx(1, "%s: bad sector size", optarg);
+ break;
+ case 'T':
+ disktype = optarg;
+ break;
+ case 'U':
+ Uflag = 1;
+ break;
+ case 'a':
+ if ((maxcontig = atoi(optarg)) <= 0)
+ errx(1, "%s: bad maximum contiguous blocks",
+ optarg);
+ break;
+ case 'b':
+ if ((bsize = atoi(optarg)) < MINBSIZE)
+ errx(1, "%s: block size too small, min is %d",
+ optarg, MINBSIZE);
+ if (bsize > MAXBSIZE)
+ errx(1, "%s: block size too large, max is %d",
+ optarg, MAXBSIZE);
+ break;
+ case 'c':
+ if ((maxblkspercg = atoi(optarg)) <= 0)
+ errx(1, "%s: bad blocks per cylinder group",
+ optarg);
+ break;
+ case 'd':
+ if ((maxbsize = atoi(optarg)) < MINBSIZE)
+ errx(1, "%s: bad extent block size", optarg);
+ break;
+ case 'e':
+ if ((maxbpg = atoi(optarg)) <= 0)
+ errx(1, "%s: bad blocks per file in a cylinder group",
+ optarg);
+ break;
+ case 'f':
+ if ((fsize = atoi(optarg)) <= 0)
+ errx(1, "%s: bad fragment size", optarg);
+ break;
+ case 'g':
+ if ((avgfilesize = atoi(optarg)) <= 0)
+ errx(1, "%s: bad average file size", optarg);
+ break;
+ case 'h':
+ if ((avgfilesperdir = atoi(optarg)) <= 0)
+ errx(1, "%s: bad average files per dir", optarg);
+ break;
+ case 'i':
+ if ((density = atoi(optarg)) <= 0)
+ errx(1, "%s: bad bytes per inode", optarg);
+ break;
+ case 'l':
+ lflag = 1;
+ break;
+ case 'm':
+ if ((minfree = atoi(optarg)) < 0 || minfree > 99)
+ errx(1, "%s: bad free space %%", optarg);
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'o':
+ if (strcmp(optarg, "space") == 0)
+ opt = FS_OPTSPACE;
+ else if (strcmp(optarg, "time") == 0)
+ opt = FS_OPTTIME;
+ else
+ errx(1,
+ "%s: unknown optimization preference: use `space' or `time'",
+ optarg);
+ break;
+ case 's':
+ errno = 0;
+ fssize = strtoimax(optarg, NULL, 0);
+ if (errno != 0)
+ err(1, "%s: bad file system size", optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ special = argv[0];
+ cp = strrchr(special, '/');
+ if (cp == 0) {
+ /*
+ * No path prefix; try prefixing _PATH_DEV.
+ */
+ snprintf(device, sizeof(device), "%s%s", _PATH_DEV, special);
+ special = device;
+ }
+
+ if (ufs_disk_fillout_blank(&disk, special) == -1 ||
+ (!Nflag && ufs_disk_write(&disk) == -1)) {
+ if (disk.d_error != NULL)
+ errx(1, "%s: %s", special, disk.d_error);
+ else
+ err(1, "%s", special);
+ }
+ if (fstat(disk.d_fd, &st) < 0)
+ err(1, "%s", special);
+ if ((st.st_mode & S_IFMT) != S_IFCHR)
+ errx(1, "%s: not a character-special device", special);
+
+ if (sectorsize == 0)
+ ioctl(disk.d_fd, DIOCGSECTORSIZE, &sectorsize);
+ if (sectorsize && !ioctl(disk.d_fd, DIOCGMEDIASIZE, &mediasize)) {
+ if (fssize == 0)
+ fssize = mediasize / sectorsize;
+ else if (fssize > mediasize / sectorsize)
+ errx(1, "%s: maximum file system size is %jd",
+ special, (intmax_t)(mediasize / sectorsize));
+ }
+ pp = NULL;
+ lp = getdisklabel(special);
+ if (lp != NULL) {
+ cp = strchr(special, '\0');
+ cp--;
+ if ((*cp < 'a' || *cp > 'h') && !isdigit(*cp))
+ errx(1, "%s: can't figure out file system partition",
+ special);
+ if (isdigit(*cp))
+ pp = &lp->d_partitions[RAW_PART];
+ else
+ pp = &lp->d_partitions[*cp - 'a'];
+ oldpartition = *pp;
+ if (pp->p_size == 0)
+ errx(1, "%s: `%c' partition is unavailable",
+ special, *cp);
+ if (pp->p_fstype == FS_BOOT)
+ errx(1, "%s: `%c' partition overlaps boot program",
+ special, *cp);
+ if (fssize == 0)
+ fssize = pp->p_size;
+ if (fssize > pp->p_size)
+ errx(1,
+ "%s: maximum file system size %d", special, pp->p_size);
+ if (sectorsize == 0)
+ sectorsize = lp->d_secsize;
+ if (fsize == 0)
+ fsize = pp->p_fsize;
+ if (bsize == 0)
+ bsize = pp->p_frag * pp->p_fsize;
+ }
+ if (sectorsize <= 0)
+ errx(1, "%s: no default sector size", special);
+ if (fsize <= 0)
+ fsize = MAX(DFL_FRAGSIZE, sectorsize);
+ if (bsize <= 0)
+ bsize = MIN(DFL_BLKSIZE, 8 * fsize);
+ if (maxbsize == 0)
+ maxbsize = bsize;
+ /*
+ * Maxcontig sets the default for the maximum number of blocks
+ * that may be allocated sequentially. With file system clustering
+ * it is possible to allocate contiguous blocks up to the maximum
+ * transfer size permitted by the controller or buffering.
+ */
+ if (maxcontig == 0)
+ maxcontig = MAX(1, MAXPHYS / bsize);
+ if (density == 0)
+ density = NFPI * fsize;
+ if (minfree < MINFREE && opt != FS_OPTSPACE) {
+ fprintf(stderr, "Warning: changing optimization to space ");
+ fprintf(stderr, "because minfree is less than %d%%\n", MINFREE);
+ opt = FS_OPTSPACE;
+ }
+ if (maxbpg == 0)
+ maxbpg = MAXBLKPG(bsize);
+ realsectorsize = sectorsize;
+ if (sectorsize != DEV_BSIZE) { /* XXX */
+ int secperblk = sectorsize / DEV_BSIZE;
+
+ sectorsize = DEV_BSIZE;
+ fssize *= secperblk;
+ if (pp != NULL)
+ pp->p_size *= secperblk;
+ }
+ mkfs(pp, special);
+ if (!unlabeled) {
+ if (realsectorsize != DEV_BSIZE)
+ pp->p_size /= realsectorsize / DEV_BSIZE;
+ if (!Nflag && bcmp(pp, &oldpartition, sizeof(oldpartition)))
+ rewritelabel(special, lp);
+ }
+ ufs_disk_close(&disk);
+ exit(0);
+}
+
+struct disklabel *
+getdisklabel(char *s)
+{
+ static struct disklabel lab;
+ struct disklabel *lp;
+
+ if (!ioctl(disk.d_fd, DIOCGDINFO, (char *)&lab))
+ return (&lab);
+ unlabeled++;
+ if (disktype) {
+ lp = getdiskbyname(disktype);
+ if (lp != NULL)
+ return (lp);
+ }
+ return (NULL);
+}
+
+void
+rewritelabel(char *s, struct disklabel *lp)
+{
+ if (unlabeled)
+ return;
+ lp->d_checksum = 0;
+ lp->d_checksum = dkcksum(lp);
+ if (ioctl(disk.d_fd, DIOCWDINFO, (char *)lp) < 0)
+ warn("ioctl (WDINFO): %s: can't rewrite disk label", s);
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: %s [ -fsoptions ] special-device%s\n",
+ getprogname(),
+ " [device-type]");
+ fprintf(stderr, "where fsoptions are:\n");
+ fprintf(stderr, "\t-L volume label to add to superblock\n");
+ fprintf(stderr,
+ "\t-N do not create file system, just print out parameters\n");
+ fprintf(stderr, "\t-O file system format: 1 => UFS1, 2 => UFS2\n");
+ fprintf(stderr, "\t-R regression test, supress random factors\n");
+ fprintf(stderr, "\t-S sector size\n");
+ fprintf(stderr, "\t-T disktype\n");
+ fprintf(stderr, "\t-U enable soft updates\n");
+ fprintf(stderr, "\t-a maximum contiguous blocks\n");
+ fprintf(stderr, "\t-b block size\n");
+ fprintf(stderr, "\t-c blocks per cylinders group\n");
+ fprintf(stderr, "\t-d maximum extent size\n");
+ fprintf(stderr, "\t-e maximum blocks per file in a cylinder group\n");
+ fprintf(stderr, "\t-f frag size\n");
+ fprintf(stderr, "\t-g average file size\n");
+ fprintf(stderr, "\t-h average files per directory\n");
+ fprintf(stderr, "\t-i number of bytes per inode\n");
+ fprintf(stderr, "\t-l enable multilabel MAC\n");
+ fprintf(stderr, "\t-n do not create .snap directory\n");
+ fprintf(stderr, "\t-m minimum free space %%\n");
+ fprintf(stderr, "\t-o optimization preference (`space' or `time')\n");
+ fprintf(stderr, "\t-s file systemsize (sectors)\n");
+ exit(1);
+}
diff --git a/sbin/newfs/newfs.h b/sbin/newfs/newfs.h
new file mode 100644
index 0000000..dae9fd8
--- /dev/null
+++ b/sbin/newfs/newfs.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and Network Associates Laboratories, the Security
+ * Research Division of Network Associates, Inc. under DARPA/SPAWAR
+ * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+ * research program.
+ *
+ * Copyright (c) 1980, 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.
+ * 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <libufs.h>
+
+/*
+ * variables set up by front end.
+ */
+extern int Lflag; /* add a volume label */
+extern int Nflag; /* run mkfs without writing file system */
+extern int Oflag; /* build UFS1 format file system */
+extern int Rflag; /* regression test */
+extern int Uflag; /* enable soft updates for file system */
+extern int Eflag; /* exit as if error, for testing */
+extern int lflag; /* enable multilabel MAC for file system */
+extern int nflag; /* do not create .snap directory */
+extern quad_t fssize; /* file system size */
+extern int sectorsize; /* bytes/sector */
+extern int realsectorsize; /* bytes/sector in hardware*/
+extern int fsize; /* fragment size */
+extern int bsize; /* block size */
+extern int maxbsize; /* maximum clustering */
+extern int maxblkspercg; /* maximum blocks per cylinder group */
+extern int minfree; /* free space threshold */
+extern int opt; /* optimization preference (space or time) */
+extern int density; /* number of bytes per inode */
+extern int maxcontig; /* max contiguous blocks to allocate */
+extern int maxbpg; /* maximum blocks per file in a cyl group */
+extern int avgfilesize; /* expected average file size */
+extern int avgfilesperdir; /* expected number of files per directory */
+extern u_char *volumelabel; /* volume label for filesystem */
+extern struct uufsd disk; /* libufs disk structure */
+
+void mkfs (struct partition *, char *);
diff --git a/sbin/newfs/ref.test b/sbin/newfs/ref.test
new file mode 100644
index 0000000..1967e24
--- /dev/null
+++ b/sbin/newfs/ref.test
@@ -0,0 +1,7 @@
+# $FreeBSD$
+00c08266df6b0c79d2673515c182216a
+c00458f223a9119190591e8b8679bf97
+7d5b3c75244898dbb07a4cd20860c8a1
+a69179c925b67edc20c289c3321ae87a
+4d1c6cf3c563044a59c3d426bb890ece
+841ed8884da029d4590b56b2f033f404
diff --git a/sbin/newfs/runtest00.sh b/sbin/newfs/runtest00.sh
new file mode 100644
index 0000000..a95dbcc
--- /dev/null
+++ b/sbin/newfs/runtest00.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+# $FreeBSD$
+
+set -e
+
+MD=99
+(
+for s in 1m 4m 60m 120m 240m 1g
+do
+ (
+ mdconfig -d -u $MD || true
+ mdconfig -a -t malloc -s $s -u $MD
+ disklabel -r -w md$MD auto
+ ./newfs -R /dev/md${MD}c
+ ) 1>&2
+ md5 < /dev/md${MD}c
+done
+mdconfig -d -u $MD 1>&2 || true
+)
diff --git a/sbin/newfs/runtest01.sh b/sbin/newfs/runtest01.sh
new file mode 100644
index 0000000..4712832
--- /dev/null
+++ b/sbin/newfs/runtest01.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+# $FreeBSD$
+
+set -e
+
+MD=99
+ME=98
+s=1m
+mdconfig -d -u $MD || true
+mdconfig -d -u $ME || true
+mdconfig -a -t malloc -s $s -u $MD
+mdconfig -a -t malloc -s $s -u $ME
+disklabel -r -w md$MD auto
+disklabel -r -w md$ME auto
+./newfs -R /dev/md${MD}c
+./newfs -R /dev/md${ME}c
+if cmp /dev/md${MD}c /dev/md${ME}c ; then
+ echo "Test passed"
+ e=0
+else
+ echo "Test failed"
+ e=1
+fi
+mdconfig -d -u $MD || true
+mdconfig -d -u $ME || true
+exit $e
+
diff --git a/sbin/newfs_msdos/Makefile b/sbin/newfs_msdos/Makefile
new file mode 100644
index 0000000..5ee8277
--- /dev/null
+++ b/sbin/newfs_msdos/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= newfs_msdos
+MAN= newfs_msdos.8
+
+.if ${MACHINE_ARCH} == "arm"
+WARNS?= 3
+.else
+WARNS?= 6
+.endif
+
+.include <bsd.prog.mk>
diff --git a/sbin/newfs_msdos/newfs_msdos.8 b/sbin/newfs_msdos/newfs_msdos.8
new file mode 100644
index 0000000..024ed1c
--- /dev/null
+++ b/sbin/newfs_msdos/newfs_msdos.8
@@ -0,0 +1,193 @@
+.\" Copyright (c) 1998 Robert Nordier
+.\" 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(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, 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 6, 1998
+.Dt NEWFS_MSDOS 8
+.Os
+.Sh NAME
+.Nm newfs_msdos
+.Nd construct a new MS-DOS (FAT) file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl N
+.Op Fl B Ar boot
+.Op Fl F Ar FAT-type
+.Op Fl I Ar volid
+.Op Fl L Ar label
+.Op Fl O Ar OEM
+.Op Fl S Ar sector-size
+.Op Fl a Ar FAT-size
+.Op Fl b Ar block-size
+.Op Fl c Ar cluster-size
+.Op Fl e Ar dirents
+.Op Fl f Ar format
+.Op Fl h Ar heads
+.Op Fl i Ar info
+.Op Fl k Ar backup
+.Op Fl m Ar media
+.Op Fl n Ar FATs
+.Op Fl o Ar hidden
+.Op Fl r Ar reserved
+.Op Fl s Ar total
+.Op Fl u Ar track-size
+.Ar special
+.Op Ar disktype
+.Sh DESCRIPTION
+The
+.Nm
+utility creates a FAT12, FAT16, or FAT32 file system on device
+.Ar special ,
+using
+.Xr disktab 5
+entry
+.Ar disktype
+to determine geometry, if required.
+.Pp
+The options are as follow:
+.Bl -tag -width indent
+.It Fl N
+Do not create a file system: just print out parameters.
+.It Fl B Ar boot
+Get bootstrap from file.
+.It Fl F Ar FAT-type
+FAT type (one of 12, 16, or 32).
+.It Fl I Ar volid
+Volume ID.
+.It Fl L Ar label
+Volume label (up to 11 characters).
+The label should consist of
+only those characters permitted in regular DOS (8+3) filenames.
+.It Fl O Ar OEM
+OEM string (up to 8 characters).
+The default is
+.Qq Li "BSD 4.4" .
+.It Fl S Ar sector-size
+Number of bytes per sector.
+Acceptable values are powers of 2
+in the range 128 through 32768.
+.It Fl a Ar FAT-size
+Number of sectors per FAT.
+.It Fl b Ar block-size
+File system block size (bytes per cluster).
+This should resolve to an
+acceptable number of sectors per cluster (see below).
+.It Fl c Ar cluster-size
+Sectors per cluster.
+Acceptable values are powers of 2 in the range
+1 through 128.
+.It Fl e Ar dirents
+Number of root directory entries (FAT12 and FAT16 only).
+.It Fl f Ar format
+Specify a standard (floppy disk) format.
+The standard formats
+are (capacities in kilobytes): 160, 180, 320, 360, 640, 720, 1200,
+1232, 1440, 2880.
+.It Fl h Ar heads
+Number of drive heads.
+.It Fl i Ar info
+Location of the file system info sector (FAT32 only).
+A value of 0xffff signifies no info sector.
+.It Fl k Ar backup
+Location of the backup boot sector (FAT32 only).
+A value
+of 0xffff signifies no backup sector.
+.It Fl m Ar media
+Media descriptor (acceptable range 0xf0 to 0xff).
+.It Fl n Ar FATs
+Number of FATs.
+Acceptable values are 1 to 16 inclusive.
+The default
+is 2.
+.It Fl o Ar hidden
+Number of hidden sectors.
+.It Fl r Ar reserved
+Number of reserved sectors.
+.It Fl s Ar total
+File system size.
+.It Fl u Ar track-size
+Number of sectors per track.
+.El
+.Sh NOTES
+FAT file system parameters occupy a "Boot Sector BPB (BIOS Parameter
+Block)" in the first of the "reserved" sectors which precede the actual
+file system.
+For reference purposes, this structure is presented
+below.
+.Bd -literal
+struct bsbpb {
+ u_int16_t bps; /* [-S] bytes per sector */
+ u_int8_t spc; /* [-c] sectors per cluster */
+ u_int16_t res; /* [-r] reserved sectors */
+ u_int8_t nft; /* [-n] number of FATs */
+ u_int16_t rde; /* [-e] root directory entries */
+ u_int16_t sec; /* [-s] total sectors */
+ u_int8_t mid; /* [-m] media descriptor */
+ u_int16_t spf; /* [-a] sectors per FAT */
+ u_int16_t spt; /* [-u] sectors per track */
+ u_int16_t hds; /* [-h] drive heads */
+ u_int32_t hid; /* [-o] hidden sectors */
+ u_int32_t bsec; /* [-s] big total sectors */
+};
+/* FAT32 extensions */
+struct bsxbpb {
+ u_int32_t bspf; /* [-a] big sectors per FAT */
+ u_int16_t xflg; /* control flags */
+ u_int16_t vers; /* file system version */
+ u_int32_t rdcl; /* root directory start cluster */
+ u_int16_t infs; /* [-i] file system info sector */
+ u_int16_t bkbs; /* [-k] backup boot sector */
+};
+.Ed
+.Sh EXIT STATUS
+Exit status is 0 on success and 1 on error.
+.Sh EXAMPLES
+.Bd -literal -offset indent
+newfs_msdos /dev/ad0s1
+.Ed
+.Pp
+Create a file system, using default parameters, on
+.Pa /dev/ad0s1 .
+.Bd -literal -offset indent
+newfs_msdos -f 1440 -L foo fd0
+.Ed
+.Pp
+Create a standard 1.44M file system, with volume label
+.Ar foo ,
+on
+.Pa /dev/fd0 .
+.Sh SEE ALSO
+.Xr disktab 5 ,
+.Xr disklabel 8 ,
+.Xr fdisk 8 ,
+.Xr newfs 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+.An Robert Nordier Aq rnordier@FreeBSD.org .
diff --git a/sbin/newfs_msdos/newfs_msdos.c b/sbin/newfs_msdos/newfs_msdos.c
new file mode 100644
index 0000000..b645fb7
--- /dev/null
+++ b/sbin/newfs_msdos/newfs_msdos.c
@@ -0,0 +1,896 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * 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(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, 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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/fdcio.h>
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define MAXU16 0xffff /* maximum unsigned 16-bit quantity */
+#define BPN 4 /* bits per nibble */
+#define NPB 2 /* nibbles per byte */
+
+#define DOSMAGIC 0xaa55 /* DOS magic number */
+#define MINBPS 128 /* minimum bytes per sector */
+#define MAXSPC 128 /* maximum sectors per cluster */
+#define MAXNFT 16 /* maximum number of FATs */
+#define DEFBLK 4096 /* default block size */
+#define DEFBLK16 2048 /* default block size FAT16 */
+#define DEFRDE 512 /* default root directory entries */
+#define RESFTE 2 /* reserved FAT entries */
+#define MINCLS12 1 /* minimum FAT12 clusters */
+#define MINCLS16 0x1000 /* minimum FAT16 clusters */
+#define MINCLS32 2 /* minimum FAT32 clusters */
+#define MAXCLS12 0xfed /* maximum FAT12 clusters */
+#define MAXCLS16 0xfff5 /* maximum FAT16 clusters */
+#define MAXCLS32 0xffffff5 /* maximum FAT32 clusters */
+
+#define mincls(fat) ((fat) == 12 ? MINCLS12 : \
+ (fat) == 16 ? MINCLS16 : \
+ MINCLS32)
+
+#define maxcls(fat) ((fat) == 12 ? MAXCLS12 : \
+ (fat) == 16 ? MAXCLS16 : \
+ MAXCLS32)
+
+#define mk1(p, x) \
+ (p) = (u_int8_t)(x)
+
+#define mk2(p, x) \
+ (p)[0] = (u_int8_t)(x), \
+ (p)[1] = (u_int8_t)((x) >> 010)
+
+#define mk4(p, x) \
+ (p)[0] = (u_int8_t)(x), \
+ (p)[1] = (u_int8_t)((x) >> 010), \
+ (p)[2] = (u_int8_t)((x) >> 020), \
+ (p)[3] = (u_int8_t)((x) >> 030)
+
+#define argto1(arg, lo, msg) argtou(arg, lo, 0xff, msg)
+#define argto2(arg, lo, msg) argtou(arg, lo, 0xffff, msg)
+#define argto4(arg, lo, msg) argtou(arg, lo, 0xffffffff, msg)
+#define argtox(arg, lo, msg) argtou(arg, lo, UINT_MAX, msg)
+
+struct bs {
+ u_int8_t jmp[3]; /* bootstrap entry point */
+ u_int8_t oem[8]; /* OEM name and version */
+};
+
+struct bsbpb {
+ u_int8_t bps[2]; /* bytes per sector */
+ u_int8_t spc; /* sectors per cluster */
+ u_int8_t res[2]; /* reserved sectors */
+ u_int8_t nft; /* number of FATs */
+ u_int8_t rde[2]; /* root directory entries */
+ u_int8_t sec[2]; /* total sectors */
+ u_int8_t mid; /* media descriptor */
+ u_int8_t spf[2]; /* sectors per FAT */
+ u_int8_t spt[2]; /* sectors per track */
+ u_int8_t hds[2]; /* drive heads */
+ u_int8_t hid[4]; /* hidden sectors */
+ u_int8_t bsec[4]; /* big total sectors */
+};
+
+struct bsxbpb {
+ u_int8_t bspf[4]; /* big sectors per FAT */
+ u_int8_t xflg[2]; /* FAT control flags */
+ u_int8_t vers[2]; /* file system version */
+ u_int8_t rdcl[4]; /* root directory start cluster */
+ u_int8_t infs[2]; /* file system info sector */
+ u_int8_t bkbs[2]; /* backup boot sector */
+ u_int8_t rsvd[12]; /* reserved */
+};
+
+struct bsx {
+ u_int8_t drv; /* drive number */
+ u_int8_t rsvd; /* reserved */
+ u_int8_t sig; /* extended boot signature */
+ u_int8_t volid[4]; /* volume ID number */
+ u_int8_t label[11]; /* volume label */
+ u_int8_t type[8]; /* file system type */
+};
+
+struct de {
+ u_int8_t namext[11]; /* name and extension */
+ u_int8_t attr; /* attributes */
+ u_int8_t rsvd[10]; /* reserved */
+ u_int8_t time[2]; /* creation time */
+ u_int8_t date[2]; /* creation date */
+ u_int8_t clus[2]; /* starting cluster */
+ u_int8_t size[4]; /* size */
+};
+
+struct bpb {
+ u_int bps; /* bytes per sector */
+ u_int spc; /* sectors per cluster */
+ u_int res; /* reserved sectors */
+ u_int nft; /* number of FATs */
+ u_int rde; /* root directory entries */
+ u_int sec; /* total sectors */
+ u_int mid; /* media descriptor */
+ u_int spf; /* sectors per FAT */
+ u_int spt; /* sectors per track */
+ u_int hds; /* drive heads */
+ u_int hid; /* hidden sectors */
+ u_int bsec; /* big total sectors */
+ u_int bspf; /* big sectors per FAT */
+ u_int rdcl; /* root directory start cluster */
+ u_int infs; /* file system info sector */
+ u_int bkbs; /* backup boot sector */
+};
+
+#define BPBGAP 0, 0, 0, 0, 0, 0
+
+static struct {
+ const char *name;
+ struct bpb bpb;
+} stdfmt[] = {
+ {"160", {512, 1, 1, 2, 64, 320, 0xfe, 1, 8, 1, BPBGAP}},
+ {"180", {512, 1, 1, 2, 64, 360, 0xfc, 2, 9, 1, BPBGAP}},
+ {"320", {512, 2, 1, 2, 112, 640, 0xff, 1, 8, 2, BPBGAP}},
+ {"360", {512, 2, 1, 2, 112, 720, 0xfd, 2, 9, 2, BPBGAP}},
+ {"640", {512, 2, 1, 2, 112, 1280, 0xfb, 2, 8, 2, BPBGAP}},
+ {"720", {512, 2, 1, 2, 112, 1440, 0xf9, 3, 9, 2, BPBGAP}},
+ {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}},
+ {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2, 8, 2, BPBGAP}},
+ {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}},
+ {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}}
+};
+
+static u_int8_t bootcode[] = {
+ 0xfa, /* cli */
+ 0x31, 0xc0, /* xor ax,ax */
+ 0x8e, 0xd0, /* mov ss,ax */
+ 0xbc, 0x00, 0x7c, /* mov sp,7c00h */
+ 0xfb, /* sti */
+ 0x8e, 0xd8, /* mov ds,ax */
+ 0xe8, 0x00, 0x00, /* call $ + 3 */
+ 0x5e, /* pop si */
+ 0x83, 0xc6, 0x19, /* add si,+19h */
+ 0xbb, 0x07, 0x00, /* mov bx,0007h */
+ 0xfc, /* cld */
+ 0xac, /* lodsb */
+ 0x84, 0xc0, /* test al,al */
+ 0x74, 0x06, /* jz $ + 8 */
+ 0xb4, 0x0e, /* mov ah,0eh */
+ 0xcd, 0x10, /* int 10h */
+ 0xeb, 0xf5, /* jmp $ - 9 */
+ 0x30, 0xe4, /* xor ah,ah */
+ 0xcd, 0x16, /* int 16h */
+ 0xcd, 0x19, /* int 19h */
+ 0x0d, 0x0a,
+ 'N', 'o', 'n', '-', 's', 'y', 's', 't',
+ 'e', 'm', ' ', 'd', 'i', 's', 'k',
+ 0x0d, 0x0a,
+ 'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
+ 'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
+ ' ', 'r', 'e', 'b', 'o', 'o', 't',
+ 0x0d, 0x0a,
+ 0
+};
+
+static void check_mounted(const char *, mode_t);
+static void getstdfmt(const char *, struct bpb *);
+static void getdiskinfo(int, const char *, const char *, int,
+ struct bpb *);
+static void print_bpb(struct bpb *);
+static u_int ckgeom(const char *, u_int, const char *);
+static u_int argtou(const char *, u_int, u_int, const char *);
+static int oklabel(const char *);
+static void mklabel(u_int8_t *, const char *);
+static void setstr(u_int8_t *, const char *, size_t);
+static void usage(void);
+
+/*
+ * Construct a FAT12, FAT16, or FAT32 file system.
+ */
+int
+main(int argc, char *argv[])
+{
+ static char opts[] = "NB:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:";
+ static const char *opt_B, *opt_L, *opt_O, *opt_f;
+ static u_int opt_F, opt_I, opt_S, opt_a, opt_b, opt_c, opt_e;
+ static u_int opt_h, opt_i, opt_k, opt_m, opt_n, opt_o, opt_r;
+ static u_int opt_s, opt_u;
+ static int opt_N;
+ static int Iflag, mflag, oflag;
+ char buf[MAXPATHLEN];
+ struct stat sb;
+ struct timeval tv;
+ struct bpb bpb;
+ struct tm *tm;
+ struct bs *bs;
+ struct bsbpb *bsbpb;
+ struct bsxbpb *bsxbpb;
+ struct bsx *bsx;
+ struct de *de;
+ u_int8_t *img;
+ const char *fname, *dtype, *bname;
+ ssize_t n;
+ time_t now;
+ u_int fat, bss, rds, cls, dir, lsn, x, x1, x2;
+ int ch, fd, fd1;
+
+ while ((ch = getopt(argc, argv, opts)) != -1)
+ switch (ch) {
+ case 'N':
+ opt_N = 1;
+ break;
+ case 'B':
+ opt_B = optarg;
+ break;
+ case 'F':
+ if (strcmp(optarg, "12") &&
+ strcmp(optarg, "16") &&
+ strcmp(optarg, "32"))
+ errx(1, "%s: bad FAT type", optarg);
+ opt_F = atoi(optarg);
+ break;
+ case 'I':
+ opt_I = argto4(optarg, 0, "volume ID");
+ Iflag = 1;
+ break;
+ case 'L':
+ if (!oklabel(optarg))
+ errx(1, "%s: bad volume label", optarg);
+ opt_L = optarg;
+ break;
+ case 'O':
+ if (strlen(optarg) > 8)
+ errx(1, "%s: bad OEM string", optarg);
+ opt_O = optarg;
+ break;
+ case 'S':
+ opt_S = argto2(optarg, 1, "bytes/sector");
+ break;
+ case 'a':
+ opt_a = argto4(optarg, 1, "sectors/FAT");
+ break;
+ case 'b':
+ opt_b = argtox(optarg, 1, "block size");
+ opt_c = 0;
+ break;
+ case 'c':
+ opt_c = argto1(optarg, 1, "sectors/cluster");
+ opt_b = 0;
+ break;
+ case 'e':
+ opt_e = argto2(optarg, 1, "directory entries");
+ break;
+ case 'f':
+ opt_f = optarg;
+ break;
+ case 'h':
+ opt_h = argto2(optarg, 1, "drive heads");
+ break;
+ case 'i':
+ opt_i = argto2(optarg, 1, "info sector");
+ break;
+ case 'k':
+ opt_k = argto2(optarg, 1, "backup sector");
+ break;
+ case 'm':
+ opt_m = argto1(optarg, 0, "media descriptor");
+ mflag = 1;
+ break;
+ case 'n':
+ opt_n = argto1(optarg, 1, "number of FATs");
+ break;
+ case 'o':
+ opt_o = argto4(optarg, 0, "hidden sectors");
+ oflag = 1;
+ break;
+ case 'r':
+ opt_r = argto2(optarg, 1, "reserved sectors");
+ break;
+ case 's':
+ opt_s = argto4(optarg, 1, "file system size");
+ break;
+ case 'u':
+ opt_u = argto2(optarg, 1, "sectors/track");
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 1 || argc > 2)
+ usage();
+ fname = *argv++;
+ if (!strchr(fname, '/')) {
+ snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
+ if (!(fname = strdup(buf)))
+ err(1, NULL);
+ }
+ dtype = *argv;
+ if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1 ||
+ fstat(fd, &sb))
+ err(1, "%s", fname);
+ if (!opt_N)
+ check_mounted(fname, sb.st_mode);
+ if (!S_ISCHR(sb.st_mode))
+ warnx("warning: %s is not a character device", fname);
+ memset(&bpb, 0, sizeof(bpb));
+ if (opt_f) {
+ getstdfmt(opt_f, &bpb);
+ bpb.bsec = bpb.sec;
+ bpb.sec = 0;
+ bpb.bspf = bpb.spf;
+ bpb.spf = 0;
+ }
+ if (opt_h)
+ bpb.hds = opt_h;
+ if (opt_u)
+ bpb.spt = opt_u;
+ if (opt_S)
+ bpb.bps = opt_S;
+ if (opt_s)
+ bpb.bsec = opt_s;
+ if (oflag)
+ bpb.hid = opt_o;
+ if (!(opt_f || (opt_h && opt_u && opt_S && opt_s && oflag)))
+ getdiskinfo(fd, fname, dtype, oflag, &bpb);
+ if (!powerof2(bpb.bps))
+ errx(1, "bytes/sector (%u) is not a power of 2", bpb.bps);
+ if (bpb.bps < MINBPS)
+ errx(1, "bytes/sector (%u) is too small; minimum is %u",
+ bpb.bps, MINBPS);
+ if (!(fat = opt_F)) {
+ if (opt_f)
+ fat = 12;
+ else if (!opt_e && (opt_i || opt_k))
+ fat = 32;
+ }
+ if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k)))
+ errx(1, "-%c is not a legal FAT%s option",
+ fat == 32 ? 'e' : opt_i ? 'i' : 'k',
+ fat == 32 ? "32" : "12/16");
+ if (opt_f && fat == 32)
+ bpb.rde = 0;
+ if (opt_b) {
+ if (!powerof2(opt_b))
+ errx(1, "block size (%u) is not a power of 2", opt_b);
+ if (opt_b < bpb.bps)
+ errx(1, "block size (%u) is too small; minimum is %u",
+ opt_b, bpb.bps);
+ if (opt_b > bpb.bps * MAXSPC)
+ errx(1, "block size (%u) is too large; maximum is %u",
+ opt_b, bpb.bps * MAXSPC);
+ bpb.spc = opt_b / bpb.bps;
+ }
+ if (opt_c) {
+ if (!powerof2(opt_c))
+ errx(1, "sectors/cluster (%u) is not a power of 2", opt_c);
+ bpb.spc = opt_c;
+ }
+ if (opt_r)
+ bpb.res = opt_r;
+ if (opt_n) {
+ if (opt_n > MAXNFT)
+ errx(1, "number of FATs (%u) is too large; maximum is %u",
+ opt_n, MAXNFT);
+ bpb.nft = opt_n;
+ }
+ if (opt_e)
+ bpb.rde = opt_e;
+ if (mflag) {
+ if (opt_m < 0xf0)
+ errx(1, "illegal media descriptor (%#x)", opt_m);
+ bpb.mid = opt_m;
+ }
+ if (opt_a)
+ bpb.bspf = opt_a;
+ if (opt_i)
+ bpb.infs = opt_i;
+ if (opt_k)
+ bpb.bkbs = opt_k;
+ bss = 1;
+ bname = NULL;
+ fd1 = -1;
+ if (opt_B) {
+ bname = opt_B;
+ if (!strchr(bname, '/')) {
+ snprintf(buf, sizeof(buf), "/boot/%s", bname);
+ if (!(bname = strdup(buf)))
+ err(1, NULL);
+ }
+ if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb))
+ err(1, "%s", bname);
+ if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps ||
+ sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16)
+ errx(1, "%s: inappropriate file type or format", bname);
+ bss = sb.st_size / bpb.bps;
+ }
+ if (!bpb.nft)
+ bpb.nft = 2;
+ if (!fat) {
+ if (bpb.bsec < (bpb.res ? bpb.res : bss) +
+ howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) *
+ ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) *
+ bpb.nft +
+ howmany(bpb.rde ? bpb.rde : DEFRDE,
+ bpb.bps / sizeof(struct de)) +
+ (bpb.spc ? MINCLS16 : MAXCLS12 + 1) *
+ (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps)))
+ fat = 12;
+ else if (bpb.rde || bpb.bsec <
+ (bpb.res ? bpb.res : bss) +
+ howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft +
+ howmany(DEFRDE, bpb.bps / sizeof(struct de)) +
+ (MAXCLS16 + 1) *
+ (bpb.spc ? bpb.spc : howmany(8192, bpb.bps)))
+ fat = 16;
+ else
+ fat = 32;
+ }
+ x = bss;
+ if (fat == 32) {
+ if (!bpb.infs) {
+ if (x == MAXU16 || x == bpb.bkbs)
+ errx(1, "no room for info sector");
+ bpb.infs = x;
+ }
+ if (bpb.infs != MAXU16 && x <= bpb.infs)
+ x = bpb.infs + 1;
+ if (!bpb.bkbs) {
+ if (x == MAXU16)
+ errx(1, "no room for backup sector");
+ bpb.bkbs = x;
+ } else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs)
+ errx(1, "backup sector would overwrite info sector");
+ if (bpb.bkbs != MAXU16 && x <= bpb.bkbs)
+ x = bpb.bkbs + 1;
+ }
+ if (!bpb.res)
+ bpb.res = fat == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x;
+ else if (bpb.res < x)
+ errx(1, "too few reserved sectors");
+ if (fat != 32 && !bpb.rde)
+ bpb.rde = DEFRDE;
+ rds = howmany(bpb.rde, bpb.bps / sizeof(struct de));
+ if (!bpb.spc)
+ for (bpb.spc = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bps);
+ bpb.spc < MAXSPC &&
+ bpb.res +
+ howmany((RESFTE + maxcls(fat)) * (fat / BPN),
+ bpb.bps * NPB) * bpb.nft +
+ rds +
+ (u_int64_t)(maxcls(fat) + 1) * bpb.spc <= bpb.bsec;
+ bpb.spc <<= 1);
+ if (fat != 32 && bpb.bspf > MAXU16)
+ errx(1, "too many sectors/FAT for FAT12/16");
+ x1 = bpb.res + rds;
+ x = bpb.bspf ? bpb.bspf : 1;
+ if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec)
+ errx(1, "meta data exceeds file system size");
+ x1 += x * bpb.nft;
+ x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB /
+ (bpb.spc * bpb.bps * NPB + fat / BPN * bpb.nft);
+ x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN),
+ bpb.bps * NPB);
+ if (!bpb.bspf) {
+ bpb.bspf = x2;
+ x1 += (bpb.bspf - 1) * bpb.nft;
+ }
+ cls = (bpb.bsec - x1) / bpb.spc;
+ x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (fat / BPN) - RESFTE;
+ if (cls > x)
+ cls = x;
+ if (bpb.bspf < x2)
+ warnx("warning: sectors/FAT limits file system to %u clusters",
+ cls);
+ if (cls < mincls(fat))
+ errx(1, "%u clusters too few clusters for FAT%u, need %u", cls, fat,
+ mincls(fat));
+ if (cls > maxcls(fat)) {
+ cls = maxcls(fat);
+ bpb.bsec = x1 + (cls + 1) * bpb.spc - 1;
+ warnx("warning: FAT type limits file system to %u sectors",
+ bpb.bsec);
+ }
+ printf("%s: %u sector%s in %u FAT%u cluster%s "
+ "(%u bytes/cluster)\n", fname, cls * bpb.spc,
+ cls * bpb.spc == 1 ? "" : "s", cls, fat,
+ cls == 1 ? "" : "s", bpb.bps * bpb.spc);
+ if (!bpb.mid)
+ bpb.mid = !bpb.hid ? 0xf0 : 0xf8;
+ if (fat == 32)
+ bpb.rdcl = RESFTE;
+ if (bpb.hid + bpb.bsec <= MAXU16) {
+ bpb.sec = bpb.bsec;
+ bpb.bsec = 0;
+ }
+ if (fat != 32) {
+ bpb.spf = bpb.bspf;
+ bpb.bspf = 0;
+ }
+ print_bpb(&bpb);
+ if (!opt_N) {
+ gettimeofday(&tv, NULL);
+ now = tv.tv_sec;
+ tm = localtime(&now);
+ if (!(img = malloc(bpb.bps)))
+ err(1, NULL);
+ dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft;
+ for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) {
+ x = lsn;
+ if (opt_B &&
+ fat == 32 && bpb.bkbs != MAXU16 &&
+ bss <= bpb.bkbs && x >= bpb.bkbs) {
+ x -= bpb.bkbs;
+ if (!x && lseek(fd1, 0, SEEK_SET))
+ err(1, "%s", bname);
+ }
+ if (opt_B && x < bss) {
+ if ((n = read(fd1, img, bpb.bps)) == -1)
+ err(1, "%s", bname);
+ if ((unsigned)n != bpb.bps)
+ errx(1, "%s: can't read sector %u", bname, x);
+ } else
+ memset(img, 0, bpb.bps);
+ if (!lsn ||
+ (fat == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) {
+ x1 = sizeof(struct bs);
+ bsbpb = (struct bsbpb *)(img + x1);
+ mk2(bsbpb->bps, bpb.bps);
+ mk1(bsbpb->spc, bpb.spc);
+ mk2(bsbpb->res, bpb.res);
+ mk1(bsbpb->nft, bpb.nft);
+ mk2(bsbpb->rde, bpb.rde);
+ mk2(bsbpb->sec, bpb.sec);
+ mk1(bsbpb->mid, bpb.mid);
+ mk2(bsbpb->spf, bpb.spf);
+ mk2(bsbpb->spt, bpb.spt);
+ mk2(bsbpb->hds, bpb.hds);
+ mk4(bsbpb->hid, bpb.hid);
+ mk4(bsbpb->bsec, bpb.bsec);
+ x1 += sizeof(struct bsbpb);
+ if (fat == 32) {
+ bsxbpb = (struct bsxbpb *)(img + x1);
+ mk4(bsxbpb->bspf, bpb.bspf);
+ mk2(bsxbpb->xflg, 0);
+ mk2(bsxbpb->vers, 0);
+ mk4(bsxbpb->rdcl, bpb.rdcl);
+ mk2(bsxbpb->infs, bpb.infs);
+ mk2(bsxbpb->bkbs, bpb.bkbs);
+ x1 += sizeof(struct bsxbpb);
+ }
+ bsx = (struct bsx *)(img + x1);
+ mk1(bsx->sig, 0x29);
+ if (Iflag)
+ x = opt_I;
+ else
+ x = (((u_int)(1 + tm->tm_mon) << 8 |
+ (u_int)tm->tm_mday) +
+ ((u_int)tm->tm_sec << 8 |
+ (u_int)(tv.tv_usec / 10))) << 16 |
+ ((u_int)(1900 + tm->tm_year) +
+ ((u_int)tm->tm_hour << 8 |
+ (u_int)tm->tm_min));
+ mk4(bsx->volid, x);
+ mklabel(bsx->label, opt_L ? opt_L : "NO NAME");
+ sprintf(buf, "FAT%u", fat);
+ setstr(bsx->type, buf, sizeof(bsx->type));
+ if (!opt_B) {
+ x1 += sizeof(struct bsx);
+ bs = (struct bs *)img;
+ mk1(bs->jmp[0], 0xeb);
+ mk1(bs->jmp[1], x1 - 2);
+ mk1(bs->jmp[2], 0x90);
+ setstr(bs->oem, opt_O ? opt_O : "BSD 4.4",
+ sizeof(bs->oem));
+ memcpy(img + x1, bootcode, sizeof(bootcode));
+ mk2(img + bpb.bps - 2, DOSMAGIC);
+ }
+ } else if (fat == 32 && bpb.infs != MAXU16 &&
+ (lsn == bpb.infs ||
+ (bpb.bkbs != MAXU16 &&
+ lsn == bpb.bkbs + bpb.infs))) {
+ mk4(img, 0x41615252);
+ mk4(img + bpb.bps - 28, 0x61417272);
+ mk4(img + bpb.bps - 24, 0xffffffff);
+ mk4(img + bpb.bps - 20, bpb.rdcl);
+ mk2(img + bpb.bps - 2, DOSMAGIC);
+ } else if (lsn >= bpb.res && lsn < dir &&
+ !((lsn - bpb.res) %
+ (bpb.spf ? bpb.spf : bpb.bspf))) {
+ mk1(img[0], bpb.mid);
+ for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++)
+ mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff);
+ } else if (lsn == dir && opt_L) {
+ de = (struct de *)img;
+ mklabel(de->namext, opt_L);
+ mk1(de->attr, 050);
+ x = (u_int)tm->tm_hour << 11 |
+ (u_int)tm->tm_min << 5 |
+ (u_int)tm->tm_sec >> 1;
+ mk2(de->time, x);
+ x = (u_int)(tm->tm_year - 80) << 9 |
+ (u_int)(tm->tm_mon + 1) << 5 |
+ (u_int)tm->tm_mday;
+ mk2(de->date, x);
+ }
+ if ((n = write(fd, img, bpb.bps)) == -1)
+ err(1, "%s", fname);
+ if ((unsigned)n != bpb.bps)
+ errx(1, "%s: can't write sector %u", fname, lsn);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Exit with error if file system is mounted.
+ */
+static void
+check_mounted(const char *fname, mode_t mode)
+{
+ struct statfs *mp;
+ const char *s1, *s2;
+ size_t len;
+ int n, r;
+
+ if (!(n = getmntinfo(&mp, MNT_NOWAIT)))
+ err(1, "getmntinfo");
+ len = strlen(_PATH_DEV);
+ s1 = fname;
+ if (!strncmp(s1, _PATH_DEV, len))
+ s1 += len;
+ r = S_ISCHR(mode) && s1 != fname && *s1 == 'r';
+ for (; n--; mp++) {
+ s2 = mp->f_mntfromname;
+ if (!strncmp(s2, _PATH_DEV, len))
+ s2 += len;
+ if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) ||
+ !strcmp(s1, s2))
+ errx(1, "%s is mounted on %s", fname, mp->f_mntonname);
+ }
+}
+
+/*
+ * Get a standard format.
+ */
+static void
+getstdfmt(const char *fmt, struct bpb *bpb)
+{
+ u_int x, i;
+
+ x = sizeof(stdfmt) / sizeof(stdfmt[0]);
+ for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++);
+ if (i == x)
+ errx(1, "%s: unknown standard format", fmt);
+ *bpb = stdfmt[i].bpb;
+}
+
+/*
+ * Get disk slice, partition, and geometry information.
+ */
+static void
+getdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag,
+ struct bpb *bpb)
+{
+ struct disklabel *lp, dlp;
+ struct fd_type type;
+ off_t ms, hs = 0;
+
+ lp = NULL;
+
+ /* If the user specified a disk type, try to use that */
+ if (dtype != NULL) {
+ lp = getdiskbyname(dtype);
+ }
+
+ /* Maybe it's a floppy drive */
+ if (lp == NULL) {
+ if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1)
+ errx(1, "Cannot get disk size, %s", strerror(errno));
+ if (ioctl(fd, FD_GTYPE, &type) != -1) {
+ dlp.d_secsize = 128 << type.secsize;
+ dlp.d_nsectors = type.sectrac;
+ dlp.d_ntracks = type.heads;
+ dlp.d_secperunit = ms / dlp.d_secsize;
+ lp = &dlp;
+ }
+ }
+
+ /* Maybe it's a fixed drive */
+ if (lp == NULL) {
+ if (ioctl(fd, DIOCGDINFO, &dlp) == -1) {
+ if (ioctl(fd, DIOCGSECTORSIZE, &dlp.d_secsize) == -1)
+ errx(1, "Cannot get sector size, %s", strerror(errno));
+ if (ioctl(fd, DIOCGFWSECTORS, &dlp.d_nsectors) == -1)
+ errx(1, "Cannot get number of sectors, %s", strerror(errno));
+ if (ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks)== -1)
+ errx(1, "Cannot get number of heads, %s", strerror(errno));
+ dlp.d_secperunit = ms / dlp.d_secsize;
+ }
+
+ hs = (ms / dlp.d_secsize) - dlp.d_secperunit;
+ lp = &dlp;
+ }
+
+ if (bpb->bps == 0)
+ bpb->bps = ckgeom(fname, lp->d_secsize, "bytes/sector");
+ if (bpb->spt == 0)
+ bpb->spt = ckgeom(fname, lp->d_nsectors, "sectors/track");
+ if (bpb->hds == 0)
+ bpb->hds = ckgeom(fname, lp->d_ntracks, "drive heads");
+ if (bpb->bsec == 0)
+ bpb->bsec = lp->d_secperunit;
+ if (bpb->hid == 0)
+ bpb->hid = hs;
+}
+
+/*
+ * Print out BPB values.
+ */
+static void
+print_bpb(struct bpb *bpb)
+{
+ printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res,
+ bpb->nft);
+ if (bpb->rde)
+ printf(" rde=%u", bpb->rde);
+ if (bpb->sec)
+ printf(" sec=%u", bpb->sec);
+ printf(" mid=%#x", bpb->mid);
+ if (bpb->spf)
+ printf(" spf=%u", bpb->spf);
+ printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid);
+ if (bpb->bsec)
+ printf(" bsec=%u", bpb->bsec);
+ if (!bpb->spf) {
+ printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl);
+ printf(" infs=");
+ printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs);
+ printf(" bkbs=");
+ printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs);
+ }
+ printf("\n");
+}
+
+/*
+ * Check a disk geometry value.
+ */
+static u_int
+ckgeom(const char *fname, u_int val, const char *msg)
+{
+ if (!val)
+ errx(1, "%s: no default %s", fname, msg);
+ if (val > MAXU16)
+ errx(1, "%s: illegal %s %d", fname, msg, val);
+ return val;
+}
+
+/*
+ * Convert and check a numeric option argument.
+ */
+static u_int
+argtou(const char *arg, u_int lo, u_int hi, const char *msg)
+{
+ char *s;
+ u_long x;
+
+ errno = 0;
+ x = strtoul(arg, &s, 0);
+ if (errno || !*arg || *s || x < lo || x > hi)
+ errx(1, "%s: bad %s", arg, msg);
+ return x;
+}
+
+/*
+ * Check a volume label.
+ */
+static int
+oklabel(const char *src)
+{
+ int c, i;
+
+ for (i = 0; i <= 11; i++) {
+ c = (u_char)*src++;
+ if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
+ break;
+ }
+ return i && !c;
+}
+
+/*
+ * Make a volume label.
+ */
+static void
+mklabel(u_int8_t *dest, const char *src)
+{
+ int c, i;
+
+ for (i = 0; i < 11; i++) {
+ c = *src ? toupper(*src++) : ' ';
+ *dest++ = !i && c == '\xe5' ? 5 : c;
+ }
+}
+
+/*
+ * Copy string, padding with spaces.
+ */
+static void
+setstr(u_int8_t *dest, const char *src, size_t len)
+{
+ while (len--)
+ *dest++ = *src ? *src++ : ' ';
+}
+
+/*
+ * Print usage message.
+ */
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: newfs_msdos [ -options ] special [disktype]\n");
+ fprintf(stderr, "where the options are:\n");
+ fprintf(stderr, "\t-N don't create file system: "
+ "just print out parameters\n");
+ fprintf(stderr, "\t-B get bootstrap from file\n");
+ fprintf(stderr, "\t-F FAT type (12, 16, or 32)\n");
+ fprintf(stderr, "\t-I volume ID\n");
+ fprintf(stderr, "\t-L volume label\n");
+ fprintf(stderr, "\t-O OEM string\n");
+ fprintf(stderr, "\t-S bytes/sector\n");
+ fprintf(stderr, "\t-a sectors/FAT\n");
+ fprintf(stderr, "\t-b block size\n");
+ fprintf(stderr, "\t-c sectors/cluster\n");
+ fprintf(stderr, "\t-e root directory entries\n");
+ fprintf(stderr, "\t-f standard format\n");
+ fprintf(stderr, "\t-h drive heads\n");
+ fprintf(stderr, "\t-i file system info sector\n");
+ fprintf(stderr, "\t-k backup boot sector\n");
+ fprintf(stderr, "\t-m media descriptor\n");
+ fprintf(stderr, "\t-n number of FATs\n");
+ fprintf(stderr, "\t-o hidden sectors\n");
+ fprintf(stderr, "\t-r reserved sectors\n");
+ fprintf(stderr, "\t-s file system size (sectors)\n");
+ fprintf(stderr, "\t-u sectors/track\n");
+ exit(1);
+}
diff --git a/sbin/nfsiod/Makefile b/sbin/nfsiod/Makefile
new file mode 100644
index 0000000..22717c5
--- /dev/null
+++ b/sbin/nfsiod/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= nfsiod
+WARNS?= 6
+MAN= nfsiod.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/nfsiod/nfsiod.8 b/sbin/nfsiod/nfsiod.8
new file mode 100644
index 0000000..44bf1af
--- /dev/null
+++ b/sbin/nfsiod/nfsiod.8
@@ -0,0 +1,87 @@
+.\" Copyright (c) 1989, 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.
+.\" 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 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: @(#)nfsiod.8 8.2 (Berkeley) 2/22/94
+.\" $FreeBSD$
+.\"
+.Dd September 22, 1994
+.Dt NFSIOD 8
+.Os
+.Sh NAME
+.Nm nfsiod
+.Nd local
+.Tn NFS
+asynchronous I/O server
+.Sh SYNOPSIS
+.Nm
+.Op Fl n Ar num_servers
+.Sh DESCRIPTION
+The
+.Nm
+utility is a kernel process which runs on an
+.Tn NFS
+client machine to service asynchronous I/O requests to its server.
+It improves performance but is not required for correct operation.
+.Pp
+This program controls the maximum number of processes that the kernel runs.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl n
+Specify how many servers are permitted to be started.
+.El
+.Pp
+A client should run enough daemons to handle its maximum
+level of concurrency, typically four to six.
+.Pp
+If
+.Nm
+detects that the running kernel does not include
+.Tn NFS
+support, it will attempt to load a loadable kernel module containing
+.Tn NFS
+code, using
+.Xr kldload 2 .
+If this fails, or no
+.Tn NFS
+KLD was available,
+.Nm
+exits with an error.
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr nfsstat 1 ,
+.Xr kldload 2 ,
+.Xr nfssvc 2 ,
+.Xr mountd 8 ,
+.Xr nfsd 8 ,
+.Xr rpcbind 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
diff --git a/sbin/nfsiod/nfsiod.c b/sbin/nfsiod/nfsiod.c
new file mode 100644
index 0000000..31559cc
--- /dev/null
+++ b/sbin/nfsiod/nfsiod.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1989, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#ifndef lint
+static char sccsid[] = "@(#)nfsiod.c 8.4 (Berkeley) 5/3/95";
+#endif
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <sys/wait.h>
+#include <sys/linker.h>
+#include <sys/mount.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define MAXNFSDCNT 20
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: nfsiod [-n num_servers]\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ struct xvfsconf vfc;
+ int error;
+ unsigned int iodmin, iodmax, num_servers;
+ size_t len;
+
+ error = getvfsbyname("nfs", &vfc);
+ if (error) {
+ if (kldload("nfs") == -1)
+ err(1, "kldload(nfs)");
+ error = getvfsbyname("nfs", &vfc);
+ }
+ if (error)
+ errx(1, "NFS support is not available in the running kernel");
+
+ num_servers = 0;
+ while ((ch = getopt(argc, argv, "n:")) != -1)
+ switch (ch) {
+ case 'n':
+ num_servers = atoi(optarg);
+ if (num_servers < 1) {
+ warnx("nfsiod count %d; reset to %d",
+ num_servers, 1);
+ num_servers = 1;
+ }
+ if (num_servers > MAXNFSDCNT) {
+ warnx("nfsiod count %d; reset to %d",
+ num_servers, MAXNFSDCNT);
+ num_servers = MAXNFSDCNT;
+ }
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ usage();
+
+ if (num_servers == 0)
+ exit(0); /* no change */
+
+ len = sizeof iodmin;
+ error = sysctlbyname("vfs.nfs.iodmin", &iodmin, &len, NULL, 0);
+ if (error < 0)
+ err(1, "sysctlbyname(\"vfs.nfs.iodmin\")");
+ len = sizeof iodmax;
+ error = sysctlbyname("vfs.nfs.iodmax", &iodmax, &len, NULL, 0);
+ if (error < 0)
+ err(1, "sysctlbyname(\"vfs.nfs.iodmax\")");
+ /* Catch the case where we're lowering num_servers below iodmin */
+ if (iodmin > num_servers) {
+ iodmin = num_servers;
+ error = sysctlbyname("vfs.nfs.iodmin", NULL, 0, &iodmin,
+ sizeof iodmin);
+ if (error < 0)
+ err(1, "sysctlbyname(\"vfs.nfs.iodmin\")");
+ }
+ iodmax = num_servers;
+ error = sysctlbyname("vfs.nfs.iodmax", NULL, 0, &iodmax, sizeof iodmax);
+ if (error < 0)
+ err(1, "sysctlbyname(\"vfs.nfs.iodmax\")");
+ exit (0);
+}
+
diff --git a/sbin/nos-tun/Makefile b/sbin/nos-tun/Makefile
new file mode 100644
index 0000000..e128b62
--- /dev/null
+++ b/sbin/nos-tun/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= nos-tun
+WARNS?= 0
+MAN= nos-tun.8
+
+.include <bsd.prog.mk>
+
diff --git a/sbin/nos-tun/nos-tun.8 b/sbin/nos-tun/nos-tun.8
new file mode 100644
index 0000000..405d430
--- /dev/null
+++ b/sbin/nos-tun/nos-tun.8
@@ -0,0 +1,92 @@
+.\"
+.\" ----------------------------------------------------------------------------
+.\" "THE BEER-WARE LICENSE" (Revision 42):
+.\" <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+.\" can do whatever you want with this stuff. If we meet some day, and you think
+.\" this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+.\" ----------------------------------------------------------------------------
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 11, 1998
+.Dt NOS-TUN 8
+.Os
+.Sh NAME
+.Nm nos-tun
+.Nd implement ``nos'' or ``ka9q'' style IP over IP tunnel
+.Sh SYNOPSIS
+.Nm
+.Fl t
+.Ar tunnel
+.Fl s
+.Ar source
+.Fl d
+.Ar destination
+.Fl p
+.Ar protocol_number
+.Op Ar source
+.Ar target
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to establish an
+.Em nos
+style tunnel, (also known as
+.Em ka9q
+or
+.Em IP-IP
+tunnel) using a
+.Xr tun 4
+kernel interface.
+.Pp
+.Ar Tunnel
+is the name of the tunnel device
+.Pa /dev/tun0
+for example.
+.Pp
+.Ar Source
+and
+.Ar destination
+are the addresses used on the tunnel device.
+If you configure the tunnel against a cisco router, use a netmask of
+.Dq 255.255.255.252
+on the cisco.
+This is because the tunnel is a point-to-point interface
+in the
+.Fx
+end, a concept cisco does not really implement.
+.Pp
+.Ar Protocol number
+sets tunnel mode.
+Original KA9Q NOS uses 94 but many people use 4
+on the worldwide backbone of ampr.org.
+.Pp
+.Ar Target
+is the address of the remote tunnel device, this must match the source
+address set on the remote end.
+.Sh EXAMPLES
+This end, a
+.Fx
+box on address 192.168.59.34:
+.Bd -literal -offset indent
+nos-tun -t /dev/tun0 -s 192.168.61.1 -d 192.168.61.2 192.168.56.45
+.Ed
+.Pp
+Remote cisco on address 192.168.56.45:
+.Bd -literal -offset indent
+interface tunnel 0
+ip address 192.168.61.2 255.255.255.252
+tunnel mode nos
+tunnel destination 192.168.59.34
+tunnel source 192.168.56.45
+.Ed
+.Sh AUTHORS
+.An -nosplit
+.An Nickolay N. Dudorov Aq nnd@itfs.nsk.su
+wrote the program,
+.An Poul-Henning Kamp Aq phk@FreeBSD.org
+wrote the man-page.
+.An Isao SEKI Aq iseki@gongon.com
+added a new flag, IP protocol number.
+.Sh BUGS
+We do not allow for setting our source address for multihomed machines.
diff --git a/sbin/nos-tun/nos-tun.c b/sbin/nos-tun/nos-tun.c
new file mode 100644
index 0000000..9966840
--- /dev/null
+++ b/sbin/nos-tun/nos-tun.c
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 1996, Nickolay Dudorov
+ * 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 unmodified, 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.
+ *
+ */
+
+/*
+ * 'nos-tun' program configure tunN interface as a point-to-point
+ * connection with two "pseudo"-addresses between this host and
+ * 'target'.
+ *
+ * It uses Ip-over-Ip incapsulation ( protocol number 94 - IPIP)
+ * (known as NOS-incapsulation in CISCO-routers' terminology).
+ *
+ * 'nos-tun' can works with itself and CISCO-routers.
+ * (It may also work with Linux 'nos-tun's, but
+ * I have no Linux system here to test with).
+ *
+ * BUGS (or features ?):
+ * - you must specify ONE of the target host's addresses
+ * ( nos-tun sends and accepts packets only to/from this
+ * address )
+ * - there can be only ONE tunnel between two hosts,
+ * more precisely - between given host and (one of)
+ * target hosts' address(es)
+ * (and why do you want more ?)
+ */
+
+/*
+ * Mar. 23 1999 by Isao SEKI <iseki@gongon.com>
+ * I added a new flag for ip protocol number.
+ * We are using 4 as protocol number in ampr.org.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+/* Tunnel interface configuration stuff */
+static struct ifaliasreq ifra;
+static struct ifreq ifrq;
+
+/* Global descriptors */
+int net; /* socket descriptor */
+int tun; /* tunnel descriptor */
+
+static void usage(void);
+
+int Set_address(char *addr, struct sockaddr_in *sin)
+{
+ struct hostent *hp;
+
+ bzero((char *)sin, sizeof(struct sockaddr));
+ sin->sin_family = AF_INET;
+ if((sin->sin_addr.s_addr = inet_addr(addr)) == (in_addr_t)-1) {
+ hp = gethostbyname(addr);
+ if (!hp) {
+ syslog(LOG_ERR,"unknown host %s", addr);
+ return 1;
+ }
+ sin->sin_family = hp->h_addrtype;
+ bcopy(hp->h_addr, (caddr_t)&sin->sin_addr, hp->h_length);
+ }
+ return 0;
+}
+
+int tun_open(char *devname, struct sockaddr *ouraddr, char *theiraddr)
+{
+ int s;
+ struct sockaddr_in *sin;
+
+ /* Open tun device */
+ tun = open (devname, O_RDWR);
+ if (tun < 0) {
+ syslog(LOG_ERR,"can't open %s - %m",devname);
+ return(1);
+ }
+
+ /*
+ * At first, name the interface.
+ */
+ bzero((char *)&ifra, sizeof(ifra));
+ bzero((char *)&ifrq, sizeof(ifrq));
+
+ strncpy(ifrq.ifr_name, devname+5, IFNAMSIZ);
+ strncpy(ifra.ifra_name, devname+5, IFNAMSIZ);
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ syslog(LOG_ERR,"can't open socket - %m");
+ goto tunc_return;
+ }
+
+ /*
+ * Delete (previous) addresses for interface
+ *
+ * !!!!
+ * On FreeBSD this ioctl returns error
+ * when tunN have no addresses, so - log and ignore it.
+ *
+ */
+ if (ioctl(s, SIOCDIFADDR, &ifra) < 0) {
+ syslog(LOG_ERR,"SIOCDIFADDR - %m");
+ }
+
+ /*
+ * Set interface address
+ */
+ sin = (struct sockaddr_in *)&(ifra.ifra_addr);
+ bcopy(ouraddr, sin, sizeof(struct sockaddr_in));
+ sin->sin_len = sizeof(*sin);
+
+ /*
+ * Set destination address
+ */
+ sin = (struct sockaddr_in *)&(ifra.ifra_broadaddr);
+ if(Set_address(theiraddr,sin)) {
+ syslog(LOG_ERR,"bad destination address: %s",theiraddr);
+ goto stunc_return;
+ }
+ sin->sin_len = sizeof(*sin);
+
+ if (ioctl(s, SIOCAIFADDR, &ifra) < 0) {
+ syslog(LOG_ERR,"can't set interface address - %m");
+ goto stunc_return;
+ }
+
+ /*
+ * Now, bring up the interface.
+ */
+ if (ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) {
+ syslog(LOG_ERR,"can't get interface flags - %m");
+ goto stunc_return;
+ }
+
+ ifrq.ifr_flags |= IFF_UP;
+ if (!(ioctl(s, SIOCSIFFLAGS, &ifrq) < 0)) {
+ close(s);
+ return(0);
+ }
+ syslog(LOG_ERR,"can't set interface UP - %m");
+stunc_return:
+ close(s);
+tunc_return:
+ close(tun);
+ return(1);
+}
+
+void Finish(int signum)
+{
+ int s;
+
+ syslog(LOG_INFO,"exiting");
+
+ close(net);
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ syslog(LOG_ERR,"can't open socket - %m");
+ goto closing_tun;
+ }
+
+ /*
+ * Shut down interface.
+ */
+ if (ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) {
+ syslog(LOG_ERR,"can't get interface flags - %m");
+ goto closing_fds;
+ }
+
+ ifrq.ifr_flags &= ~(IFF_UP|IFF_RUNNING);
+ if (ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) {
+ syslog(LOG_ERR,"can't set interface DOWN - %m");
+ goto closing_fds;
+ }
+
+ /*
+ * Delete addresses for interface
+ */
+ bzero(&ifra.ifra_addr, sizeof(ifra.ifra_addr));
+ bzero(&ifra.ifra_broadaddr, sizeof(ifra.ifra_addr));
+ bzero(&ifra.ifra_mask, sizeof(ifra.ifra_addr));
+ if (ioctl(s, SIOCDIFADDR, &ifra) < 0) {
+ syslog(LOG_ERR,"can't delete interface's addresses - %m");
+ }
+closing_fds:
+ close(s);
+closing_tun:
+ close(tun);
+ closelog();
+ exit(signum);
+}
+
+int main (int argc, char **argv)
+{
+ int c, len, ipoff;
+
+ char *devname = NULL;
+ char *point_to = NULL;
+ char *to_point = NULL;
+ char *target;
+ char *source = NULL;
+ char *protocol = NULL;
+ int protnum;
+
+ struct sockaddr t_laddr; /* Source address of tunnel */
+ struct sockaddr whereto; /* Destination of tunnel */
+ struct sockaddr wherefrom; /* Source of tunnel */
+ struct sockaddr_in *to;
+
+ char buf[0x2000]; /* Packets buffer */
+ struct ip *ip = (struct ip *)buf;
+
+ fd_set rfds; /* File descriptors for select() */
+ int nfds; /* Return from select() */
+ int lastfd; /* highest fd we care about */
+
+
+ while ((c = getopt(argc, argv, "d:s:t:p:")) != -1) {
+ switch (c) {
+ case 'd':
+ to_point = optarg;
+ break;
+ case 's':
+ point_to = optarg;
+ break;
+ case 't':
+ devname = optarg;
+ break;
+ case 'p':
+ protocol = optarg;
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if ((argc != 1 && argc != 2) || (devname == NULL) ||
+ (point_to == NULL) || (to_point == NULL)) {
+ usage();
+ }
+
+ if(protocol == NULL)
+ protnum = 94;
+ else
+ protnum = atoi(protocol);
+
+ if (argc == 1) {
+ target = *argv;
+ } else {
+ source = *argv++; target = *argv;
+ }
+
+ /* Establish logging through 'syslog' */
+ openlog("nos-tun", LOG_PID, LOG_DAEMON);
+
+ if(Set_address(point_to, (struct sockaddr_in *)&t_laddr)) {
+ closelog();
+ exit(2);
+ }
+
+ if(tun_open(devname, &t_laddr, to_point)) {
+ closelog();
+ exit(3);
+ }
+
+ to = (struct sockaddr_in *)&whereto;
+ if(Set_address(target, to))
+ Finish(4);
+
+ if ((net = socket(AF_INET, SOCK_RAW, protnum)) < 0) {
+ syslog(LOG_ERR,"can't open socket - %m");
+ Finish(5);
+ }
+
+ if (source) {
+ if (Set_address(source, (struct sockaddr_in *)&wherefrom))
+ Finish(9);
+ if (bind(net, &wherefrom, sizeof(wherefrom)) < 0) {
+ syslog(LOG_ERR, "can't bind source address - %m");
+ Finish(10);
+ }
+ }
+
+ if (connect(net,&whereto,sizeof(struct sockaddr_in)) < 0 ) {
+ syslog(LOG_ERR,"can't connect to target - %m");
+ close(net);
+ Finish(6);
+ }
+
+ /* Demonize it */
+ daemon(0,0);
+
+ /* Install signal handlers */
+ (void)signal(SIGHUP,Finish);
+ (void)signal(SIGINT,Finish);
+ (void)signal(SIGTERM,Finish);
+
+ if (tun > net)
+ lastfd = tun;
+ else
+ lastfd = net;
+
+ for (;;) {
+ /* Set file descriptors for select() */
+ FD_ZERO(&rfds);
+ FD_SET(tun,&rfds); FD_SET(net,&rfds);
+
+ nfds = select(lastfd+1,&rfds,NULL,NULL,NULL);
+ if(nfds < 0) {
+ syslog(LOG_ERR,"interrupted select");
+ close(net);
+ Finish(7);
+ }
+ if(nfds == 0) { /* Impossible ? */
+ syslog(LOG_ERR,"timeout in select");
+ close(net);
+ Finish(8);
+ }
+
+
+ if(FD_ISSET(net,&rfds)) {
+ /* Read from socket ... */
+ len = read(net, buf, sizeof(buf));
+ /* Check if this is "our" packet */
+ if((ip->ip_src).s_addr == (to->sin_addr).s_addr) {
+ /* ... skip encapsulation headers ... */
+ ipoff = (ip->ip_hl << 2);
+ /* ... and write to tun-device */
+ write(tun,buf+ipoff,len-ipoff);
+ }
+ }
+
+ if(FD_ISSET(tun,&rfds)) {
+ /* Read from tun ... */
+ len = read(tun, buf, sizeof(buf));
+ /* ... and send to network */
+ if(send(net, buf, len,0) <= 0) {
+ syslog(LOG_ERR,"can't send - %m");
+ }
+ }
+ }
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+"usage: nos-tun -t tunnel -s source -d destination -p protocol_number [source] target\n");
+ exit(1);
+}
+
diff --git a/sbin/pfctl/Makefile b/sbin/pfctl/Makefile
new file mode 100644
index 0000000..4ffa6a7
--- /dev/null
+++ b/sbin/pfctl/Makefile
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/pf/pfctl
+.PATH: ${.CURDIR}/../../contrib/pf/man
+
+PROG= pfctl
+MAN= pfctl.8 pf.4 pflog.4 pfsync.4 pf.conf.5 pf.os.5
+
+SRCS = pfctl.c parse.y pfctl_parser.c pf_print_state.c pfctl_altq.c
+SRCS+= pfctl_osfp.c pfctl_radix.c pfctl_table.c pfctl_qstats.c
+SRCS+= pfctl_optimize.c
+
+CFLAGS+= -Wall -Wmissing-prototypes -Wno-uninitialized
+CFLAGS+= -Wstrict-prototypes -I${.CURDIR}/../../contrib/pf/pfctl
+
+# XXX ALTQ
+CFLAGS+= -DENABLE_ALTQ
+#CFLAGS+= -I${.CURDIR}/missing
+
+YFLAGS=
+
+LDADD+= -lm -lmd
+DPADD+= ${LIBM}
+
+.include <bsd.prog.mk>
diff --git a/sbin/pfctl/missing/altq/altq.h b/sbin/pfctl/missing/altq/altq.h
new file mode 100644
index 0000000..c740ed3
--- /dev/null
+++ b/sbin/pfctl/missing/altq/altq.h
@@ -0,0 +1,204 @@
+/* $FreeBSD$ */
+/* $KAME: altq.h,v 1.10 2003/07/10 12:07:47 kjc Exp $ */
+
+/*
+ * Copyright (C) 1998-2003
+ * Sony Computer Science Laboratories 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 SONY CSL 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 SONY CSL 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 _ALTQ_ALTQ_H_
+#define _ALTQ_ALTQ_H_
+
+#if 0
+/*
+ * allow altq-3 (altqd(8) and /dev/altq) to coexist with the new pf-based altq.
+ * altq3 is mainly for research experiments. pf-based altq is for daily use.
+ */
+#define ALTQ3_COMPAT /* for compatibility with altq-3 */
+#define ALTQ3_CLFIER_COMPAT /* for compatibility with altq-3 classifier */
+#endif
+
+#ifdef ALTQ3_COMPAT
+#include <sys/param.h>
+#include <sys/ioccom.h>
+#include <sys/queue.h>
+#include <netinet/in.h>
+
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif
+#endif /* ALTQ3_COMPAT */
+
+/* altq discipline type */
+#define ALTQT_NONE 0 /* reserved */
+#define ALTQT_CBQ 1 /* cbq */
+#define ALTQT_WFQ 2 /* wfq */
+#define ALTQT_AFMAP 3 /* afmap */
+#define ALTQT_FIFOQ 4 /* fifoq */
+#define ALTQT_RED 5 /* red */
+#define ALTQT_RIO 6 /* rio */
+#define ALTQT_LOCALQ 7 /* local use */
+#define ALTQT_HFSC 8 /* hfsc */
+#define ALTQT_CDNR 9 /* traffic conditioner */
+#define ALTQT_BLUE 10 /* blue */
+#define ALTQT_PRIQ 11 /* priority queue */
+#define ALTQT_JOBS 12 /* JoBS */
+#define ALTQT_MAX 13 /* should be max discipline type + 1 */
+
+#ifdef ALTQ3_COMPAT
+struct altqreq {
+ char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */
+ u_long arg; /* request-specific argument */
+};
+#endif
+
+/* simple token backet meter profile */
+struct tb_profile {
+ u_int rate; /* rate in bit-per-sec */
+ u_int depth; /* depth in bytes */
+};
+
+#ifdef ALTQ3_COMPAT
+struct tbrreq {
+ char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */
+ struct tb_profile tb_prof; /* token bucket profile */
+};
+
+#ifdef ALTQ3_CLFIER_COMPAT
+/*
+ * common network flow info structure
+ */
+struct flowinfo {
+ u_char fi_len; /* total length */
+ u_char fi_family; /* address family */
+ u_int8_t fi_data[46]; /* actually longer; address family
+ specific flow info. */
+};
+
+/*
+ * flow info structure for internet protocol family.
+ * (currently this is the only protocol family supported)
+ */
+struct flowinfo_in {
+ u_char fi_len; /* sizeof(struct flowinfo_in) */
+ u_char fi_family; /* AF_INET */
+ u_int8_t fi_proto; /* IPPROTO_XXX */
+ u_int8_t fi_tos; /* type-of-service */
+ struct in_addr fi_dst; /* dest address */
+ struct in_addr fi_src; /* src address */
+ u_int16_t fi_dport; /* dest port */
+ u_int16_t fi_sport; /* src port */
+ u_int32_t fi_gpi; /* generalized port id for ipsec */
+ u_int8_t _pad[28]; /* make the size equal to
+ flowinfo_in6 */
+};
+
+#ifdef SIN6_LEN
+struct flowinfo_in6 {
+ u_char fi6_len; /* sizeof(struct flowinfo_in6) */
+ u_char fi6_family; /* AF_INET6 */
+ u_int8_t fi6_proto; /* IPPROTO_XXX */
+ u_int8_t fi6_tclass; /* traffic class */
+ u_int32_t fi6_flowlabel; /* ipv6 flowlabel */
+ u_int16_t fi6_dport; /* dest port */
+ u_int16_t fi6_sport; /* src port */
+ u_int32_t fi6_gpi; /* generalized port id */
+ struct in6_addr fi6_dst; /* dest address */
+ struct in6_addr fi6_src; /* src address */
+};
+#endif /* INET6 */
+
+/*
+ * flow filters for AF_INET and AF_INET6
+ */
+struct flow_filter {
+ int ff_ruleno;
+ struct flowinfo_in ff_flow;
+ struct {
+ struct in_addr mask_dst;
+ struct in_addr mask_src;
+ u_int8_t mask_tos;
+ u_int8_t _pad[3];
+ } ff_mask;
+ u_int8_t _pad2[24]; /* make the size equal to flow_filter6 */
+};
+
+#ifdef SIN6_LEN
+struct flow_filter6 {
+ int ff_ruleno;
+ struct flowinfo_in6 ff_flow6;
+ struct {
+ struct in6_addr mask6_dst;
+ struct in6_addr mask6_src;
+ u_int8_t mask6_tclass;
+ u_int8_t _pad[3];
+ } ff_mask6;
+};
+#endif /* INET6 */
+#endif /* ALTQ3_CLFIER_COMPAT */
+#endif /* ALTQ3_COMPAT */
+
+/*
+ * generic packet counter
+ */
+struct pktcntr {
+ u_int64_t packets;
+ u_int64_t bytes;
+};
+
+#define PKTCNTR_ADD(cntr, len) \
+ do { (cntr)->packets++; (cntr)->bytes += len; } while (/*CONSTCOND*/ 0)
+
+#ifdef ALTQ3_COMPAT
+/*
+ * altq related ioctls
+ */
+#define ALTQGTYPE _IOWR('q', 0, struct altqreq) /* get queue type */
+#if 0
+/*
+ * these ioctls are currently discipline-specific but could be shared
+ * in the future.
+ */
+#define ALTQATTACH _IOW('q', 1, struct altqreq) /* attach discipline */
+#define ALTQDETACH _IOW('q', 2, struct altqreq) /* detach discipline */
+#define ALTQENABLE _IOW('q', 3, struct altqreq) /* enable discipline */
+#define ALTQDISABLE _IOW('q', 4, struct altqreq) /* disable discipline*/
+#define ALTQCLEAR _IOW('q', 5, struct altqreq) /* (re)initialize */
+#define ALTQCONFIG _IOWR('q', 6, struct altqreq) /* set config params */
+#define ALTQADDCLASS _IOWR('q', 7, struct altqreq) /* add a class */
+#define ALTQMODCLASS _IOWR('q', 8, struct altqreq) /* modify a class */
+#define ALTQDELCLASS _IOWR('q', 9, struct altqreq) /* delete a class */
+#define ALTQADDFILTER _IOWR('q', 10, struct altqreq) /* add a filter */
+#define ALTQDELFILTER _IOWR('q', 11, struct altqreq) /* delete a filter */
+#define ALTQGETSTATS _IOWR('q', 12, struct altqreq) /* get statistics */
+#define ALTQGETCNTR _IOWR('q', 13, struct altqreq) /* get a pkt counter */
+#endif /* 0 */
+#define ALTQTBRSET _IOW('q', 14, struct tbrreq) /* set tb regulator */
+#define ALTQTBRGET _IOWR('q', 15, struct tbrreq) /* get tb regulator */
+#endif /* ALTQ3_COMPAT */
+
+#ifdef _KERNEL
+#include <altq/altq_var.h>
+#endif
+
+#endif /* _ALTQ_ALTQ_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_cbq.h b/sbin/pfctl/missing/altq/altq_cbq.h
new file mode 100644
index 0000000..3c41c6b
--- /dev/null
+++ b/sbin/pfctl/missing/altq/altq_cbq.h
@@ -0,0 +1,232 @@
+/* $FreeBSD$ */
+/* $KAME: altq_cbq.h,v 1.10 2003/08/20 23:30:23 itojun Exp $ */
+
+/*
+ * Copyright (c) Sun Microsystems, Inc. 1993-1998 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 the SMCC Technology
+ * Development Group at Sun Microsystems, Inc.
+ *
+ * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE
+ * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is
+ * provided "as is" without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this software.
+ */
+
+#ifndef _ALTQ_ALTQ_CBQ_H_
+#define _ALTQ_ALTQ_CBQ_H_
+
+#include <altq/altq.h>
+#include <altq/altq_rmclass.h>
+#include <altq/altq_red.h>
+#include <altq/altq_rio.h>
+
+/* #pragma ident "@(#)cbq.h 1.18 98/05/13 SMI" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Define a well known class handles
+ */
+#define NULL_CLASS_HANDLE 0xffffffff
+#define ROOT_CLASS_HANDLE 0xfffffffe
+#define DEFAULT_CLASS_HANDLE 0xfffffffd
+#ifdef ALTQ3_COMPAT
+#define CTL_CLASS_HANDLE 0xfffffffc
+#endif
+
+/* class flags shoud be same as class flags in rm_class.h */
+#define CBQCLF_RED 0x0001 /* use RED */
+#define CBQCLF_ECN 0x0002 /* use RED/ECN */
+#define CBQCLF_RIO 0x0004 /* use RIO */
+#define CBQCLF_FLOWVALVE 0x0008 /* use flowvalve (aka penalty-box) */
+#define CBQCLF_CLEARDSCP 0x0010 /* clear diffserv codepoint */
+#define CBQCLF_BORROW 0x0020 /* borrow from parent */
+
+/* class flags only for root class */
+#define CBQCLF_WRR 0x0100 /* weighted-round robin */
+#define CBQCLF_EFFICIENT 0x0200 /* work-conserving */
+
+/* class flags for special classes */
+#define CBQCLF_ROOTCLASS 0x1000 /* root class */
+#define CBQCLF_DEFCLASS 0x2000 /* default class */
+#ifdef ALTQ3_COMPAT
+#define CBQCLF_CTLCLASS 0x4000 /* control class */
+#endif
+#define CBQCLF_CLASSMASK 0xf000 /* class mask */
+
+#define CBQ_MAXQSIZE 200
+#define CBQ_MAXPRI RM_MAXPRIO
+
+typedef struct _cbq_class_stats_ {
+ u_int32_t handle;
+ u_int depth;
+
+ struct pktcntr xmit_cnt; /* packets sent in this class */
+ struct pktcntr drop_cnt; /* dropped packets */
+ u_int over; /* # times went over limit */
+ u_int borrows; /* # times tried to borrow */
+ u_int overactions; /* # times invoked overlimit action */
+ u_int delays; /* # times invoked delay actions */
+
+ /* other static class parameters useful for debugging */
+ int priority;
+ int maxidle;
+ int minidle;
+ int offtime;
+ int qmax;
+ int ns_per_byte;
+ int wrr_allot;
+
+ int qcnt; /* # packets in queue */
+ int avgidle;
+
+ /* red and rio related info */
+ int qtype;
+ struct redstats red[3];
+} class_stats_t;
+
+#ifdef ALTQ3_COMPAT
+/*
+ * Define structures associated with IOCTLS for cbq.
+ */
+
+/*
+ * Define the CBQ interface structure. This must be included in all
+ * IOCTL's such that the CBQ driver may find the appropriate CBQ module
+ * associated with the network interface to be affected.
+ */
+struct cbq_interface {
+ char cbq_ifacename[IFNAMSIZ];
+};
+
+typedef struct cbq_class_spec {
+ u_int priority;
+ u_int nano_sec_per_byte;
+ u_int maxq;
+ u_int maxidle;
+ int minidle;
+ u_int offtime;
+ u_int32_t parent_class_handle;
+ u_int32_t borrow_class_handle;
+
+ u_int pktsize;
+ int flags;
+} cbq_class_spec_t;
+
+struct cbq_add_class {
+ struct cbq_interface cbq_iface;
+
+ cbq_class_spec_t cbq_class;
+ u_int32_t cbq_class_handle;
+};
+
+struct cbq_delete_class {
+ struct cbq_interface cbq_iface;
+ u_int32_t cbq_class_handle;
+};
+
+struct cbq_modify_class {
+ struct cbq_interface cbq_iface;
+
+ cbq_class_spec_t cbq_class;
+ u_int32_t cbq_class_handle;
+};
+
+struct cbq_add_filter {
+ struct cbq_interface cbq_iface;
+ u_int32_t cbq_class_handle;
+ struct flow_filter cbq_filter;
+
+ u_long cbq_filter_handle;
+};
+
+struct cbq_delete_filter {
+ struct cbq_interface cbq_iface;
+ u_long cbq_filter_handle;
+};
+
+/* number of classes are returned in nclasses field */
+struct cbq_getstats {
+ struct cbq_interface iface;
+ int nclasses;
+ class_stats_t *stats;
+};
+
+/*
+ * Define IOCTLs for CBQ.
+ */
+#define CBQ_IF_ATTACH _IOW('Q', 1, struct cbq_interface)
+#define CBQ_IF_DETACH _IOW('Q', 2, struct cbq_interface)
+#define CBQ_ENABLE _IOW('Q', 3, struct cbq_interface)
+#define CBQ_DISABLE _IOW('Q', 4, struct cbq_interface)
+#define CBQ_CLEAR_HIERARCHY _IOW('Q', 5, struct cbq_interface)
+#define CBQ_ADD_CLASS _IOWR('Q', 7, struct cbq_add_class)
+#define CBQ_DEL_CLASS _IOW('Q', 8, struct cbq_delete_class)
+#define CBQ_MODIFY_CLASS _IOWR('Q', 9, struct cbq_modify_class)
+#define CBQ_ADD_FILTER _IOWR('Q', 10, struct cbq_add_filter)
+#define CBQ_DEL_FILTER _IOW('Q', 11, struct cbq_delete_filter)
+#define CBQ_GETSTATS _IOWR('Q', 12, struct cbq_getstats)
+#endif /* ALTQ3_COMPAT */
+
+#ifdef _KERNEL
+/*
+ * Define macros only good for kernel drivers and modules.
+ */
+#define CBQ_WATCHDOG (hz / 20)
+#define CBQ_TIMEOUT 10
+#define CBQ_LS_TIMEOUT (20 * hz / 1000)
+
+#define CBQ_MAX_CLASSES 256
+
+#ifdef ALTQ3_COMPAT
+#define CBQ_MAX_FILTERS 256
+
+#define DISABLE 0x00
+#define ENABLE 0x01
+#endif /* ALTQ3_COMPAT */
+
+/*
+ * Define State structures.
+ */
+typedef struct cbqstate {
+#ifdef ALTQ3_COMPAT
+ struct cbqstate *cbq_next;
+#endif
+ int cbq_qlen; /* # of packets in cbq */
+ struct rm_class *cbq_class_tbl[CBQ_MAX_CLASSES];
+
+ struct rm_ifdat ifnp;
+ struct callout cbq_callout; /* for timeouts */
+#ifdef ALTQ3_CLFIER_COMPAT
+ struct acc_classifier cbq_classifier;
+#endif
+} cbq_state_t;
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_ALTQ_ALTQ_CBQ_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_classq.h b/sbin/pfctl/missing/altq/altq_classq.h
new file mode 100644
index 0000000..43045b2
--- /dev/null
+++ b/sbin/pfctl/missing/altq/altq_classq.h
@@ -0,0 +1,203 @@
+/* $FreeBSD$ */
+/* $KAME: altq_classq.h,v 1.6 2003/01/07 07:33:38 kjc Exp $ */
+
+/*
+ * Copyright (c) 1991-1997 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.
+ * 4. Neither the name of the University nor of the Laboratory 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.
+ */
+/*
+ * class queue definitions extracted from rm_class.h.
+ */
+#ifndef _ALTQ_ALTQ_CLASSQ_H_
+#define _ALTQ_ALTQ_CLASSQ_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Packet Queue types: RED or DROPHEAD.
+ */
+#define Q_DROPHEAD 0x00
+#define Q_RED 0x01
+#define Q_RIO 0x02
+#define Q_DROPTAIL 0x03
+
+#ifdef _KERNEL
+
+/*
+ * Packet Queue structures and macros to manipulate them.
+ */
+struct _class_queue_ {
+ struct mbuf *tail_; /* Tail of packet queue */
+ int qlen_; /* Queue length (in number of packets) */
+ int qlim_; /* Queue limit (in number of packets*) */
+ int qtype_; /* Queue type */
+};
+
+typedef struct _class_queue_ class_queue_t;
+
+#define qtype(q) (q)->qtype_ /* Get queue type */
+#define qlimit(q) (q)->qlim_ /* Max packets to be queued */
+#define qlen(q) (q)->qlen_ /* Current queue length. */
+#define qtail(q) (q)->tail_ /* Tail of the queue */
+#define qhead(q) ((q)->tail_ ? (q)->tail_->m_nextpkt : NULL)
+
+#define qempty(q) ((q)->qlen_ == 0) /* Is the queue empty?? */
+#define q_is_red(q) ((q)->qtype_ == Q_RED) /* Is the queue a red queue */
+#define q_is_rio(q) ((q)->qtype_ == Q_RIO) /* Is the queue a rio queue */
+#define q_is_red_or_rio(q) ((q)->qtype_ == Q_RED || (q)->qtype_ == Q_RIO)
+
+#if !defined(__GNUC__) || defined(ALTQ_DEBUG)
+
+extern void _addq(class_queue_t *, struct mbuf *);
+extern struct mbuf *_getq(class_queue_t *);
+extern struct mbuf *_getq_tail(class_queue_t *);
+extern struct mbuf *_getq_random(class_queue_t *);
+extern void _removeq(class_queue_t *, struct mbuf *);
+extern void _flushq(class_queue_t *);
+
+#else /* __GNUC__ && !ALTQ_DEBUG */
+/*
+ * inlined versions
+ */
+static __inline void
+_addq(class_queue_t *q, struct mbuf *m)
+{
+ struct mbuf *m0;
+
+ if ((m0 = qtail(q)) != NULL)
+ m->m_nextpkt = m0->m_nextpkt;
+ else
+ m0 = m;
+ m0->m_nextpkt = m;
+ qtail(q) = m;
+ qlen(q)++;
+}
+
+static __inline struct mbuf *
+_getq(class_queue_t *q)
+{
+ struct mbuf *m, *m0;
+
+ if ((m = qtail(q)) == NULL)
+ return (NULL);
+ if ((m0 = m->m_nextpkt) != m)
+ m->m_nextpkt = m0->m_nextpkt;
+ else
+ qtail(q) = NULL;
+ qlen(q)--;
+ m0->m_nextpkt = NULL;
+ return (m0);
+}
+
+/* drop a packet at the tail of the queue */
+static __inline struct mbuf *
+_getq_tail(class_queue_t *q)
+{
+ struct mbuf *m, *m0, *prev;
+
+ if ((m = m0 = qtail(q)) == NULL)
+ return NULL;
+ do {
+ prev = m0;
+ m0 = m0->m_nextpkt;
+ } while (m0 != m);
+ prev->m_nextpkt = m->m_nextpkt;
+ if (prev == m)
+ qtail(q) = NULL;
+ else
+ qtail(q) = prev;
+ qlen(q)--;
+ m->m_nextpkt = NULL;
+ return (m);
+}
+
+/* randomly select a packet in the queue */
+static __inline struct mbuf *
+_getq_random(class_queue_t *q)
+{
+ struct mbuf *m;
+ int i, n;
+
+ if ((m = qtail(q)) == NULL)
+ return NULL;
+ if (m->m_nextpkt == m)
+ qtail(q) = NULL;
+ else {
+ struct mbuf *prev = NULL;
+
+ n = random() % qlen(q) + 1;
+ for (i = 0; i < n; i++) {
+ prev = m;
+ m = m->m_nextpkt;
+ }
+ prev->m_nextpkt = m->m_nextpkt;
+ if (m == qtail(q))
+ qtail(q) = prev;
+ }
+ qlen(q)--;
+ m->m_nextpkt = NULL;
+ return (m);
+}
+
+static __inline void
+_removeq(class_queue_t *q, struct mbuf *m)
+{
+ struct mbuf *m0, *prev;
+
+ m0 = qtail(q);
+ do {
+ prev = m0;
+ m0 = m0->m_nextpkt;
+ } while (m0 != m);
+ prev->m_nextpkt = m->m_nextpkt;
+ if (prev == m)
+ qtail(q) = NULL;
+ else if (qtail(q) == m)
+ qtail(q) = prev;
+ qlen(q)--;
+}
+
+static __inline void
+_flushq(class_queue_t *q)
+{
+ struct mbuf *m;
+
+ while ((m = _getq(q)) != NULL)
+ m_freem(m);
+}
+
+#endif /* __GNUC__ && !ALTQ_DEBUG */
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ALTQ_ALTQ_CLASSQ_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_hfsc.h b/sbin/pfctl/missing/altq/altq_hfsc.h
new file mode 100644
index 0000000..ac24211
--- /dev/null
+++ b/sbin/pfctl/missing/altq/altq_hfsc.h
@@ -0,0 +1,325 @@
+/* $FreeBSD$ */
+/* $KAME: altq_hfsc.h,v 1.10 2003/07/10 12:07:48 kjc Exp $ */
+
+/*
+ * Copyright (c) 1997-1999 Carnegie Mellon University. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation is hereby granted (including for commercial or
+ * for-profit use), provided that both the copyright notice and this
+ * permission notice appear in all copies of the software, derivative
+ * works, or modified versions, and any portions thereof, and that
+ * both notices appear in supporting documentation, and that credit
+ * is given to Carnegie Mellon University in all publications reporting
+ * on direct or indirect use of this code or its derivatives.
+ *
+ * THIS SOFTWARE IS EXPERIMENTAL AND IS KNOWN TO HAVE BUGS, SOME OF
+ * WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON PROVIDES THIS
+ * SOFTWARE IN ITS ``AS IS'' CONDITION, 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 CARNEGIE MELLON UNIVERSITY 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.
+ *
+ * Carnegie Mellon encourages (but does not require) users of this
+ * software to return any improvements or extensions that they make,
+ * and to grant Carnegie Mellon the rights to redistribute these
+ * changes without encumbrance.
+ */
+#ifndef _ALTQ_ALTQ_HFSC_H_
+#define _ALTQ_ALTQ_HFSC_H_
+
+#include <altq/altq.h>
+#include <altq/altq_classq.h>
+#include <altq/altq_red.h>
+#include <altq/altq_rio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct service_curve {
+ u_int m1; /* slope of the first segment in bits/sec */
+ u_int d; /* the x-projection of the first segment in msec */
+ u_int m2; /* slope of the second segment in bits/sec */
+};
+
+/* special class handles */
+#define HFSC_NULLCLASS_HANDLE 0
+#define HFSC_ROOTCLASS_HANDLE 1
+#define HFSC_MAX_CLASSES 64
+
+/* hfsc class flags */
+#define HFCF_RED 0x0001 /* use RED */
+#define HFCF_ECN 0x0002 /* use RED/ECN */
+#define HFCF_RIO 0x0004 /* use RIO */
+#define HFCF_CLEARDSCP 0x0010 /* clear diffserv codepoint */
+#define HFCF_DEFAULTCLASS 0x1000 /* default class */
+
+/* service curve types */
+#define HFSC_REALTIMESC 1
+#define HFSC_LINKSHARINGSC 2
+#define HFSC_UPPERLIMITSC 4
+#define HFSC_DEFAULTSC (HFSC_REALTIMESC|HFSC_LINKSHARINGSC)
+
+struct hfsc_classstats {
+ u_int class_id;
+ u_int32_t class_handle;
+ struct service_curve rsc;
+ struct service_curve fsc;
+ struct service_curve usc; /* upper limit service curve */
+
+ u_int64_t total; /* total work in bytes */
+ u_int64_t cumul; /* cumulative work in bytes
+ done by real-time criteria */
+ u_int64_t d; /* deadline */
+ u_int64_t e; /* eligible time */
+ u_int64_t vt; /* virtual time */
+ u_int64_t f; /* fit time for upper-limit */
+
+ /* info helpful for debugging */
+ u_int64_t initvt; /* init virtual time */
+ u_int64_t vtoff; /* cl_vt_ipoff */
+ u_int64_t cvtmax; /* cl_maxvt */
+ u_int64_t myf; /* cl_myf */
+ u_int64_t cfmin; /* cl_mincf */
+ u_int64_t cvtmin; /* cl_mincvt */
+ u_int64_t myfadj; /* cl_myfadj */
+ u_int64_t vtadj; /* cl_vtadj */
+ u_int64_t cur_time;
+ u_int32_t machclk_freq;
+
+ u_int qlength;
+ u_int qlimit;
+ struct pktcntr xmit_cnt;
+ struct pktcntr drop_cnt;
+ u_int period;
+
+ u_int vtperiod; /* vt period sequence no */
+ u_int parentperiod; /* parent's vt period seqno */
+ int nactive; /* number of active children */
+
+ /* red and rio related info */
+ int qtype;
+ struct redstats red[3];
+};
+
+#ifdef ALTQ3_COMPAT
+struct hfsc_interface {
+ char hfsc_ifname[IFNAMSIZ]; /* interface name (e.g., fxp0) */
+};
+
+struct hfsc_attach {
+ struct hfsc_interface iface;
+ u_int bandwidth; /* link bandwidth in bits/sec */
+};
+
+struct hfsc_add_class {
+ struct hfsc_interface iface;
+ u_int32_t parent_handle;
+ struct service_curve service_curve;
+ int qlimit;
+ int flags;
+
+ u_int32_t class_handle; /* return value */
+};
+
+struct hfsc_delete_class {
+ struct hfsc_interface iface;
+ u_int32_t class_handle;
+};
+
+struct hfsc_modify_class {
+ struct hfsc_interface iface;
+ u_int32_t class_handle;
+ struct service_curve service_curve;
+ int sctype;
+};
+
+struct hfsc_add_filter {
+ struct hfsc_interface iface;
+ u_int32_t class_handle;
+ struct flow_filter filter;
+
+ u_long filter_handle; /* return value */
+};
+
+struct hfsc_delete_filter {
+ struct hfsc_interface iface;
+ u_long filter_handle;
+};
+
+struct hfsc_class_stats {
+ struct hfsc_interface iface;
+ int nskip; /* skip # of classes */
+ int nclasses; /* # of class stats (WR) */
+ u_int64_t cur_time; /* current time */
+ u_int32_t machclk_freq; /* machine clock frequency */
+ u_int hif_classes; /* # of classes in the tree */
+ u_int hif_packets; /* # of packets in the tree */
+ struct hfsc_classstats *stats; /* pointer to stats array */
+};
+
+#define HFSC_IF_ATTACH _IOW('Q', 1, struct hfsc_attach)
+#define HFSC_IF_DETACH _IOW('Q', 2, struct hfsc_interface)
+#define HFSC_ENABLE _IOW('Q', 3, struct hfsc_interface)
+#define HFSC_DISABLE _IOW('Q', 4, struct hfsc_interface)
+#define HFSC_CLEAR_HIERARCHY _IOW('Q', 5, struct hfsc_interface)
+#define HFSC_ADD_CLASS _IOWR('Q', 7, struct hfsc_add_class)
+#define HFSC_DEL_CLASS _IOW('Q', 8, struct hfsc_delete_class)
+#define HFSC_MOD_CLASS _IOW('Q', 9, struct hfsc_modify_class)
+#define HFSC_ADD_FILTER _IOWR('Q', 10, struct hfsc_add_filter)
+#define HFSC_DEL_FILTER _IOW('Q', 11, struct hfsc_delete_filter)
+#define HFSC_GETSTATS _IOWR('Q', 12, struct hfsc_class_stats)
+#endif /* ALTQ3_COMPAT */
+
+#ifdef _KERNEL
+/*
+ * kernel internal service curve representation
+ * coordinates are given by 64 bit unsigned integers.
+ * x-axis: unit is clock count. for the intel x86 architecture,
+ * the raw Pentium TSC (Timestamp Counter) value is used.
+ * virtual time is also calculated in this time scale.
+ * y-axis: unit is byte.
+ *
+ * the service curve parameters are converted to the internal
+ * representation.
+ * the slope values are scaled to avoid overflow.
+ * the inverse slope values as well as the y-projection of the 1st
+ * segment are kept in order to to avoid 64-bit divide operations
+ * that are expensive on 32-bit architectures.
+ *
+ * note: Intel Pentium TSC never wraps around in several thousands of years.
+ * x-axis doesn't wrap around for 1089 years with 1GHz clock.
+ * y-axis doesn't wrap around for 4358 years with 1Gbps bandwidth.
+ */
+
+/* kernel internal representation of a service curve */
+struct internal_sc {
+ u_int64_t sm1; /* scaled slope of the 1st segment */
+ u_int64_t ism1; /* scaled inverse-slope of the 1st segment */
+ u_int64_t dx; /* the x-projection of the 1st segment */
+ u_int64_t dy; /* the y-projection of the 1st segment */
+ u_int64_t sm2; /* scaled slope of the 2nd segment */
+ u_int64_t ism2; /* scaled inverse-slope of the 2nd segment */
+};
+
+/* runtime service curve */
+struct runtime_sc {
+ u_int64_t x; /* current starting position on x-axis */
+ u_int64_t y; /* current starting position on x-axis */
+ u_int64_t sm1; /* scaled slope of the 1st segment */
+ u_int64_t ism1; /* scaled inverse-slope of the 1st segment */
+ u_int64_t dx; /* the x-projection of the 1st segment */
+ u_int64_t dy; /* the y-projection of the 1st segment */
+ u_int64_t sm2; /* scaled slope of the 2nd segment */
+ u_int64_t ism2; /* scaled inverse-slope of the 2nd segment */
+};
+
+/* for TAILQ based ellist and actlist implementation */
+struct hfsc_class;
+typedef TAILQ_HEAD(_eligible, hfsc_class) ellist_t;
+typedef TAILQ_ENTRY(hfsc_class) elentry_t;
+typedef TAILQ_HEAD(_active, hfsc_class) actlist_t;
+typedef TAILQ_ENTRY(hfsc_class) actentry_t;
+#define ellist_first(s) TAILQ_FIRST(s)
+#define actlist_first(s) TAILQ_FIRST(s)
+#define actlist_last(s) TAILQ_LAST(s, _active)
+
+struct hfsc_class {
+ u_int cl_id; /* class id (just for debug) */
+ u_int32_t cl_handle; /* class handle */
+ struct hfsc_if *cl_hif; /* back pointer to struct hfsc_if */
+ int cl_flags; /* misc flags */
+
+ struct hfsc_class *cl_parent; /* parent class */
+ struct hfsc_class *cl_siblings; /* sibling classes */
+ struct hfsc_class *cl_children; /* child classes */
+
+ class_queue_t *cl_q; /* class queue structure */
+ struct red *cl_red; /* RED state */
+ struct altq_pktattr *cl_pktattr; /* saved header used by ECN */
+
+ u_int64_t cl_total; /* total work in bytes */
+ u_int64_t cl_cumul; /* cumulative work in bytes
+ done by real-time criteria */
+ u_int64_t cl_d; /* deadline */
+ u_int64_t cl_e; /* eligible time */
+ u_int64_t cl_vt; /* virtual time */
+ u_int64_t cl_f; /* time when this class will fit for
+ link-sharing, max(myf, cfmin) */
+ u_int64_t cl_myf; /* my fit-time (as calculated from this
+ class's own upperlimit curve) */
+ u_int64_t cl_myfadj; /* my fit-time adjustment
+ (to cancel history dependence) */
+ u_int64_t cl_cfmin; /* earliest children's fit-time (used
+ with cl_myf to obtain cl_f) */
+ u_int64_t cl_cvtmin; /* minimal virtual time among the
+ children fit for link-sharing
+ (monotonic within a period) */
+ u_int64_t cl_vtadj; /* intra-period cumulative vt
+ adjustment */
+ u_int64_t cl_vtoff; /* inter-period cumulative vt offset */
+ u_int64_t cl_cvtmax; /* max child's vt in the last period */
+
+ u_int64_t cl_initvt; /* init virtual time (for debugging) */
+
+ struct internal_sc *cl_rsc; /* internal real-time service curve */
+ struct internal_sc *cl_fsc; /* internal fair service curve */
+ struct internal_sc *cl_usc; /* internal upperlimit service curve */
+ struct runtime_sc cl_deadline; /* deadline curve */
+ struct runtime_sc cl_eligible; /* eligible curve */
+ struct runtime_sc cl_virtual; /* virtual curve */
+ struct runtime_sc cl_ulimit; /* upperlimit curve */
+
+ u_int cl_vtperiod; /* vt period sequence no */
+ u_int cl_parentperiod; /* parent's vt period seqno */
+ int cl_nactive; /* number of active children */
+ actlist_t *cl_actc; /* active children list */
+
+ actentry_t cl_actlist; /* active children list entry */
+ elentry_t cl_ellist; /* eligible list entry */
+
+ struct {
+ struct pktcntr xmit_cnt;
+ struct pktcntr drop_cnt;
+ u_int period;
+ } cl_stats;
+};
+
+/*
+ * hfsc interface state
+ */
+struct hfsc_if {
+ struct hfsc_if *hif_next; /* interface state list */
+ struct ifaltq *hif_ifq; /* backpointer to ifaltq */
+ struct hfsc_class *hif_rootclass; /* root class */
+ struct hfsc_class *hif_defaultclass; /* default class */
+ struct hfsc_class *hif_class_tbl[HFSC_MAX_CLASSES];
+ struct hfsc_class *hif_pollcache; /* cache for poll operation */
+
+ u_int hif_classes; /* # of classes in the tree */
+ u_int hif_packets; /* # of packets in the tree */
+ u_int hif_classid; /* class id sequence number */
+
+ ellist_t *hif_eligible; /* eligible list */
+
+#ifdef ALTQ3_CLFIER_COMPAT
+ struct acc_classifier hif_classifier;
+#endif
+};
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ALTQ_ALTQ_HFSC_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_priq.h b/sbin/pfctl/missing/altq/altq_priq.h
new file mode 100644
index 0000000..526b092
--- /dev/null
+++ b/sbin/pfctl/missing/altq/altq_priq.h
@@ -0,0 +1,172 @@
+/* $FreeBSD$ */
+/* $KAME: altq_priq.h,v 1.5 2003/07/10 12:07:48 kjc Exp $ */
+/*
+ * Copyright (C) 2000-2003
+ * Sony Computer Science Laboratories 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 SONY CSL 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 SONY CSL 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 _ALTQ_ALTQ_PRIQ_H_
+#define _ALTQ_ALTQ_PRIQ_H_
+
+#include <altq/altq.h>
+#include <altq/altq_classq.h>
+#include <altq/altq_red.h>
+#include <altq/altq_rio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PRIQ_MAXPRI 16 /* upper limit of the number of priorities */
+#define PRIQ_MAXQID 256 /* upper limit of queues */
+
+#ifdef ALTQ3_COMPAT
+struct priq_interface {
+ char ifname[IFNAMSIZ]; /* interface name (e.g., fxp0) */
+ u_long arg; /* request-specific argument */
+};
+
+struct priq_add_class {
+ struct priq_interface iface;
+ int pri; /* priority (0 is the lowest) */
+ int qlimit; /* queue size limit */
+ int flags; /* misc flags (see below) */
+
+ u_int32_t class_handle; /* return value */
+};
+#endif /* ALTQ3_COMPAT */
+
+/* priq class flags */
+#define PRCF_RED 0x0001 /* use RED */
+#define PRCF_ECN 0x0002 /* use RED/ECN */
+#define PRCF_RIO 0x0004 /* use RIO */
+#define PRCF_CLEARDSCP 0x0010 /* clear diffserv codepoint */
+#define PRCF_DEFAULTCLASS 0x1000 /* default class */
+
+/* special class handles */
+#define PRIQ_NULLCLASS_HANDLE 0
+
+#ifdef ALTQ3_COMPAT
+struct priq_delete_class {
+ struct priq_interface iface;
+ u_int32_t class_handle;
+};
+
+struct priq_modify_class {
+ struct priq_interface iface;
+ u_int32_t class_handle;
+ int pri;
+ int qlimit;
+ int flags;
+};
+
+struct priq_add_filter {
+ struct priq_interface iface;
+ u_int32_t class_handle;
+ struct flow_filter filter;
+
+ u_long filter_handle; /* return value */
+};
+
+struct priq_delete_filter {
+ struct priq_interface iface;
+ u_long filter_handle;
+};
+#endif /* ALTQ3_COMPAT */
+
+struct priq_classstats {
+ u_int32_t class_handle;
+
+ u_int qlength;
+ u_int qlimit;
+ u_int period;
+ struct pktcntr xmitcnt; /* transmitted packet counter */
+ struct pktcntr dropcnt; /* dropped packet counter */
+
+ /* red and rio related info */
+ int qtype;
+ struct redstats red[3]; /* rio has 3 red stats */
+};
+
+#ifdef ALTQ3_COMPAT
+struct priq_class_stats {
+ struct priq_interface iface;
+ int maxpri; /* in/out */
+
+ struct priq_classstats *stats; /* pointer to stats array */
+};
+
+#define PRIQ_IF_ATTACH _IOW('Q', 1, struct priq_interface)
+#define PRIQ_IF_DETACH _IOW('Q', 2, struct priq_interface)
+#define PRIQ_ENABLE _IOW('Q', 3, struct priq_interface)
+#define PRIQ_DISABLE _IOW('Q', 4, struct priq_interface)
+#define PRIQ_CLEAR _IOW('Q', 5, struct priq_interface)
+#define PRIQ_ADD_CLASS _IOWR('Q', 7, struct priq_add_class)
+#define PRIQ_DEL_CLASS _IOW('Q', 8, struct priq_delete_class)
+#define PRIQ_MOD_CLASS _IOW('Q', 9, struct priq_modify_class)
+#define PRIQ_ADD_FILTER _IOWR('Q', 10, struct priq_add_filter)
+#define PRIQ_DEL_FILTER _IOW('Q', 11, struct priq_delete_filter)
+#define PRIQ_GETSTATS _IOWR('Q', 12, struct priq_class_stats)
+
+#endif /* ALTQ3_COMPAT */
+
+#ifdef _KERNEL
+
+struct priq_class {
+ u_int32_t cl_handle; /* class handle */
+ class_queue_t *cl_q; /* class queue structure */
+ struct red *cl_red; /* RED state */
+ int cl_pri; /* priority */
+ int cl_flags; /* class flags */
+ struct priq_if *cl_pif; /* back pointer to pif */
+ struct altq_pktattr *cl_pktattr; /* saved header used by ECN */
+
+ /* statistics */
+ u_int cl_period; /* backlog period */
+ struct pktcntr cl_xmitcnt; /* transmitted packet counter */
+ struct pktcntr cl_dropcnt; /* dropped packet counter */
+};
+
+/*
+ * priq interface state
+ */
+struct priq_if {
+ struct priq_if *pif_next; /* interface state list */
+ struct ifaltq *pif_ifq; /* backpointer to ifaltq */
+ u_int pif_bandwidth; /* link bandwidth in bps */
+ int pif_maxpri; /* max priority in use */
+ struct priq_class *pif_default; /* default class */
+ struct priq_class *pif_classes[PRIQ_MAXPRI]; /* classes */
+#ifdef ALTQ3_CLFIER_COMPAT
+ struct acc_classifier pif_classifier; /* classifier */
+#endif
+};
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ALTQ_ALTQ_PRIQ_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_red.h b/sbin/pfctl/missing/altq/altq_red.h
new file mode 100644
index 0000000..96e3fae
--- /dev/null
+++ b/sbin/pfctl/missing/altq/altq_red.h
@@ -0,0 +1,199 @@
+/* $FreeBSD$ */
+/* $KAME: altq_red.h,v 1.8 2003/07/10 12:07:49 kjc Exp $ */
+
+/*
+ * Copyright (C) 1997-2003
+ * Sony Computer Science Laboratories 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 SONY CSL 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 SONY CSL 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 _ALTQ_ALTQ_RED_H_
+#define _ALTQ_ALTQ_RED_H_
+
+#include <altq/altq_classq.h>
+
+#ifdef ALTQ3_COMPAT
+struct red_interface {
+ char red_ifname[IFNAMSIZ];
+};
+
+struct red_stats {
+ struct red_interface iface;
+ int q_len;
+ int q_avg;
+
+ struct pktcntr xmit_cnt;
+ struct pktcntr drop_cnt;
+ u_int drop_forced;
+ u_int drop_unforced;
+ u_int marked_packets;
+
+ /* static red parameters */
+ int q_limit;
+ int weight;
+ int inv_pmax;
+ int th_min;
+ int th_max;
+
+ /* flowvalve related stuff */
+ u_int fv_flows;
+ u_int fv_pass;
+ u_int fv_predrop;
+ u_int fv_alloc;
+ u_int fv_escape;
+};
+
+struct red_conf {
+ struct red_interface iface;
+ int red_weight; /* weight for EWMA */
+ int red_inv_pmax; /* inverse of max drop probability */
+ int red_thmin; /* red min threshold */
+ int red_thmax; /* red max threshold */
+ int red_limit; /* max queue length */
+ int red_pkttime; /* average packet time in usec */
+ int red_flags; /* see below */
+};
+#endif /* ALTQ3_COMPAT */
+
+/* red flags */
+#define REDF_ECN4 0x01 /* use packet marking for IPv4 packets */
+#define REDF_ECN6 0x02 /* use packet marking for IPv6 packets */
+#define REDF_ECN (REDF_ECN4 | REDF_ECN6)
+#define REDF_FLOWVALVE 0x04 /* use flowvalve (aka penalty-box) */
+
+/*
+ * simpler versions of red parameters and statistics used by other
+ * disciplines (e.g., CBQ)
+ */
+struct redparams {
+ int th_min; /* red min threshold */
+ int th_max; /* red max threshold */
+ int inv_pmax; /* inverse of max drop probability */
+};
+
+struct redstats {
+ int q_avg;
+ struct pktcntr xmit_cnt;
+ struct pktcntr drop_cnt;
+ u_int drop_forced;
+ u_int drop_unforced;
+ u_int marked_packets;
+};
+
+#ifdef ALTQ3_COMPAT
+/*
+ * IOCTLs for RED
+ */
+#define RED_IF_ATTACH _IOW('Q', 1, struct red_interface)
+#define RED_IF_DETACH _IOW('Q', 2, struct red_interface)
+#define RED_ENABLE _IOW('Q', 3, struct red_interface)
+#define RED_DISABLE _IOW('Q', 4, struct red_interface)
+#define RED_CONFIG _IOWR('Q', 6, struct red_conf)
+#define RED_GETSTATS _IOWR('Q', 12, struct red_stats)
+#define RED_SETDEFAULTS _IOW('Q', 30, struct redparams)
+#endif /* ALTQ3_COMPAT */
+
+#ifdef _KERNEL
+
+#ifdef ALTQ3_COMPAT
+struct flowvalve;
+#endif
+
+/* weight table structure for idle time calibration */
+struct wtab {
+ struct wtab *w_next;
+ int w_weight;
+ int w_param_max;
+ int w_refcount;
+ int32_t w_tab[32];
+};
+
+typedef struct red {
+ int red_pkttime; /* average packet time in micro sec
+ used for idle calibration */
+ int red_flags; /* red flags */
+
+ /* red parameters */
+ int red_weight; /* weight for EWMA */
+ int red_inv_pmax; /* inverse of max drop probability */
+ int red_thmin; /* red min threshold */
+ int red_thmax; /* red max threshold */
+
+ /* variables for internal use */
+ int red_wshift; /* log(red_weight) */
+ int red_thmin_s; /* th_min scaled by avgshift */
+ int red_thmax_s; /* th_max scaled by avgshift */
+ int red_probd; /* drop probability denominator */
+
+ int red_avg; /* queue len avg scaled by avgshift */
+ int red_count; /* packet count since last dropped/
+ marked packet */
+ int red_idle; /* queue was empty */
+ int red_old; /* avg is above th_min */
+ struct wtab *red_wtab; /* weight table */
+ struct timeval red_last; /* time when the queue becomes idle */
+
+#ifdef ALTQ3_COMPAT
+ struct flowvalve *red_flowvalve; /* flowvalve state */
+#endif
+
+ struct {
+ struct pktcntr xmit_cnt;
+ struct pktcntr drop_cnt;
+ u_int drop_forced;
+ u_int drop_unforced;
+ u_int marked_packets;
+ } red_stats;
+} red_t;
+
+#ifdef ALTQ3_COMPAT
+typedef struct red_queue {
+ struct red_queue *rq_next; /* next red_state in the list */
+ struct ifaltq *rq_ifq; /* backpointer to ifaltq */
+
+ class_queue_t *rq_q;
+
+ red_t *rq_red;
+} red_queue_t;
+#endif /* ALTQ3_COMPAT */
+
+/* red drop types */
+#define DTYPE_NODROP 0 /* no drop */
+#define DTYPE_FORCED 1 /* a "forced" drop */
+#define DTYPE_EARLY 2 /* an "unforced" (early) drop */
+
+extern red_t *red_alloc(int, int, int, int, int, int);
+extern void red_destroy(red_t *);
+extern void red_getstats(red_t *, struct redstats *);
+extern int red_addq(red_t *, class_queue_t *, struct mbuf *,
+ struct altq_pktattr *);
+extern struct mbuf *red_getq(red_t *, class_queue_t *);
+extern int drop_early(int, int, int);
+extern int mark_ecn(struct mbuf *, struct altq_pktattr *, int);
+extern struct wtab *wtab_alloc(int);
+extern int wtab_destroy(struct wtab *);
+extern int32_t pow_w(struct wtab *, int);
+
+#endif /* _KERNEL */
+
+#endif /* _ALTQ_ALTQ_RED_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_rio.h b/sbin/pfctl/missing/altq/altq_rio.h
new file mode 100644
index 0000000..eda338a
--- /dev/null
+++ b/sbin/pfctl/missing/altq/altq_rio.h
@@ -0,0 +1,145 @@
+/* $FreeBSD$ */
+/* $KAME: altq_rio.h,v 1.9 2003/07/10 12:07:49 kjc Exp $ */
+
+/*
+ * Copyright (C) 1998-2003
+ * Sony Computer Science Laboratories 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 SONY CSL 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 SONY CSL 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 _ALTQ_ALTQ_RIO_H_
+#define _ALTQ_ALTQ_RIO_H_
+
+#include <altq/altq_classq.h>
+
+/*
+ * RIO: RED with IN/OUT bit
+ * (extended to support more than 2 drop precedence values)
+ */
+#define RIO_NDROPPREC 3 /* number of drop precedence values */
+
+#ifdef ALTQ3_COMPAT
+struct rio_interface {
+ char rio_ifname[IFNAMSIZ];
+};
+
+struct rio_stats {
+ struct rio_interface iface;
+ int q_len[RIO_NDROPPREC];
+ struct redstats q_stats[RIO_NDROPPREC];
+
+ /* static red parameters */
+ int q_limit;
+ int weight;
+ int flags;
+ struct redparams q_params[RIO_NDROPPREC];
+};
+
+struct rio_conf {
+ struct rio_interface iface;
+ struct redparams q_params[RIO_NDROPPREC];
+ int rio_weight; /* weight for EWMA */
+ int rio_limit; /* max queue length */
+ int rio_pkttime; /* average packet time in usec */
+ int rio_flags; /* see below */
+};
+#endif /* ALTQ3_COMPAT */
+
+/* rio flags */
+#define RIOF_ECN4 0x01 /* use packet marking for IPv4 packets */
+#define RIOF_ECN6 0x02 /* use packet marking for IPv6 packets */
+#define RIOF_ECN (RIOF_ECN4 | RIOF_ECN6)
+#define RIOF_CLEARDSCP 0x200 /* clear diffserv codepoint */
+
+#ifdef ALTQ3_COMPAT
+/*
+ * IOCTLs for RIO
+ */
+#define RIO_IF_ATTACH _IOW('Q', 1, struct rio_interface)
+#define RIO_IF_DETACH _IOW('Q', 2, struct rio_interface)
+#define RIO_ENABLE _IOW('Q', 3, struct rio_interface)
+#define RIO_DISABLE _IOW('Q', 4, struct rio_interface)
+#define RIO_CONFIG _IOWR('Q', 6, struct rio_conf)
+#define RIO_GETSTATS _IOWR('Q', 12, struct rio_stats)
+#define RIO_SETDEFAULTS _IOW('Q', 30, struct redparams[RIO_NDROPPREC])
+#endif /* ALTQ3_COMPAT */
+
+#ifdef _KERNEL
+
+typedef struct rio {
+ /* per drop precedence structure */
+ struct dropprec_state {
+ /* red parameters */
+ int inv_pmax; /* inverse of max drop probability */
+ int th_min; /* red min threshold */
+ int th_max; /* red max threshold */
+
+ /* variables for internal use */
+ int th_min_s; /* th_min scaled by avgshift */
+ int th_max_s; /* th_max scaled by avgshift */
+ int probd; /* drop probability denominator */
+
+ int qlen; /* queue length */
+ int avg; /* (scaled) queue length average */
+ int count; /* packet count since the last dropped/
+ marked packet */
+ int idle; /* queue was empty */
+ int old; /* avg is above th_min */
+ struct timeval last; /* timestamp when queue becomes idle */
+ } rio_precstate[RIO_NDROPPREC];
+
+ int rio_wshift; /* log(red_weight) */
+ int rio_weight; /* weight for EWMA */
+ struct wtab *rio_wtab; /* weight table */
+
+ int rio_pkttime; /* average packet time in micro sec
+ used for idle calibration */
+ int rio_flags; /* rio flags */
+
+ u_int8_t rio_codepoint; /* codepoint value to tag packets */
+ u_int8_t rio_codepointmask; /* codepoint mask bits */
+
+ struct redstats q_stats[RIO_NDROPPREC]; /* statistics */
+} rio_t;
+
+#ifdef ALTQ3_COMPAT
+typedef struct rio_queue {
+ struct rio_queue *rq_next; /* next red_state in the list */
+ struct ifaltq *rq_ifq; /* backpointer to ifaltq */
+
+ class_queue_t *rq_q;
+
+ rio_t *rq_rio;
+} rio_queue_t;
+#endif /* ALTQ3_COMPAT */
+
+extern rio_t *rio_alloc(int, struct redparams *, int, int);
+extern void rio_destroy(rio_t *);
+extern void rio_getstats(rio_t *, struct redstats *);
+extern int rio_addq(rio_t *, class_queue_t *, struct mbuf *,
+ struct altq_pktattr *);
+extern struct mbuf *rio_getq(rio_t *, class_queue_t *);
+
+#endif /* _KERNEL */
+
+#endif /* _ALTQ_ALTQ_RIO_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_rmclass.h b/sbin/pfctl/missing/altq/altq_rmclass.h
new file mode 100644
index 0000000..87e3e63
--- /dev/null
+++ b/sbin/pfctl/missing/altq/altq_rmclass.h
@@ -0,0 +1,263 @@
+/* $FreeBSD$ */
+/* $KAME: altq_rmclass.h,v 1.10 2003/08/20 23:30:23 itojun Exp $ */
+
+/*
+ * Copyright (c) 1991-1997 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.
+ * 4. Neither the name of the University nor of the Laboratory 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 _ALTQ_ALTQ_RMCLASS_H_
+#define _ALTQ_ALTQ_RMCLASS_H_
+
+#include <altq/altq_classq.h>
+
+/* #pragma ident "@(#)rm_class.h 1.20 97/10/23 SMI" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RM_MAXPRIO 8 /* Max priority */
+
+#ifdef _KERNEL
+
+typedef struct mbuf mbuf_t;
+typedef struct rm_ifdat rm_ifdat_t;
+typedef struct rm_class rm_class_t;
+
+struct red;
+
+/*
+ * Macros for dealing with time values. We assume all times are
+ * 'timevals'. `microtime' is used to get the best available clock
+ * resolution. If `microtime' *doesn't* return a value that's about
+ * ten times smaller than the average packet time on the fastest
+ * link that will use these routines, a slightly different clock
+ * scheme than this one should be used.
+ * (Bias due to truncation error in this scheme will overestimate utilization
+ * and discriminate against high bandwidth classes. To remove this bias an
+ * integrator needs to be added. The simplest integrator uses a history of
+ * 10 * avg.packet.time / min.tick.time packet completion entries. This is
+ * straight forward to add but we don't want to pay the extra memory
+ * traffic to maintain it if it's not necessary (occasionally a vendor
+ * accidentally builds a workstation with a decent clock - e.g., Sun & HP).)
+ */
+
+#define RM_GETTIME(now) microtime(&now)
+
+#define TV_LT(a, b) (((a)->tv_sec < (b)->tv_sec) || \
+ (((a)->tv_usec < (b)->tv_usec) && ((a)->tv_sec <= (b)->tv_sec)))
+
+#define TV_DELTA(a, b, delta) { \
+ register int xxs; \
+ \
+ delta = (a)->tv_usec - (b)->tv_usec; \
+ if ((xxs = (a)->tv_sec - (b)->tv_sec)) { \
+ switch (xxs) { \
+ default: \
+ /* if (xxs < 0) \
+ printf("rm_class: bogus time values\n"); */ \
+ delta = 0; \
+ /* fall through */ \
+ case 2: \
+ delta += 1000000; \
+ /* fall through */ \
+ case 1: \
+ delta += 1000000; \
+ break; \
+ } \
+ } \
+}
+
+#define TV_ADD_DELTA(a, delta, res) { \
+ register int xxus = (a)->tv_usec + (delta); \
+ \
+ (res)->tv_sec = (a)->tv_sec; \
+ while (xxus >= 1000000) { \
+ ++((res)->tv_sec); \
+ xxus -= 1000000; \
+ } \
+ (res)->tv_usec = xxus; \
+}
+
+#define RM_TIMEOUT 2 /* 1 Clock tick. */
+
+#if 1
+#define RM_MAXQUEUED 1 /* this isn't used in ALTQ/CBQ */
+#else
+#define RM_MAXQUEUED 16 /* Max number of packets downstream of CBQ */
+#endif
+#define RM_MAXQUEUE 64 /* Max queue length */
+#define RM_FILTER_GAIN 5 /* log2 of gain, e.g., 5 => 31/32 */
+#define RM_POWER (1 << RM_FILTER_GAIN)
+#define RM_MAXDEPTH 32
+#define RM_NS_PER_SEC (1000000000)
+
+typedef struct _rm_class_stats_ {
+ u_int handle;
+ u_int depth;
+
+ struct pktcntr xmit_cnt; /* packets sent in this class */
+ struct pktcntr drop_cnt; /* dropped packets */
+ u_int over; /* # times went over limit */
+ u_int borrows; /* # times tried to borrow */
+ u_int overactions; /* # times invoked overlimit action */
+ u_int delays; /* # times invoked delay actions */
+} rm_class_stats_t;
+
+/*
+ * CBQ Class state structure
+ */
+struct rm_class {
+ class_queue_t *q_; /* Queue of packets */
+ rm_ifdat_t *ifdat_;
+ int pri_; /* Class priority. */
+ int depth_; /* Class depth */
+ u_int ns_per_byte_; /* NanoSeconds per byte. */
+ u_int maxrate_; /* Bytes per second for this class. */
+ u_int allotment_; /* Fraction of link bandwidth. */
+ u_int w_allotment_; /* Weighted allotment for WRR */
+ int bytes_alloc_; /* Allocation for round of WRR */
+
+ int avgidle_;
+ int maxidle_;
+ int minidle_;
+ int offtime_;
+ int sleeping_; /* != 0 if delaying */
+ int qthresh_; /* Queue threshold for formal link sharing */
+ int leaf_; /* Note whether leaf class or not.*/
+
+ rm_class_t *children_; /* Children of this class */
+ rm_class_t *next_; /* Next pointer, used if child */
+
+ rm_class_t *peer_; /* Peer class */
+ rm_class_t *borrow_; /* Borrow class */
+ rm_class_t *parent_; /* Parent class */
+
+ void (*overlimit)(struct rm_class *, struct rm_class *);
+ void (*drop)(struct rm_class *); /* Class drop action. */
+
+ struct red *red_; /* RED state pointer */
+ struct altq_pktattr *pktattr_; /* saved hdr used by RED/ECN */
+ int flags_;
+
+ int last_pkttime_; /* saved pkt_time */
+ struct timeval undertime_; /* time can next send */
+ struct timeval last_; /* time last packet sent */
+ struct timeval overtime_;
+ struct callout callout_; /* for timeout() calls */
+
+ rm_class_stats_t stats_; /* Class Statistics */
+};
+
+/*
+ * CBQ Interface state
+ */
+struct rm_ifdat {
+ int queued_; /* # pkts queued downstream */
+ int efficient_; /* Link Efficency bit */
+ int wrr_; /* Enable Weighted Round-Robin */
+ u_long ns_per_byte_; /* Link byte speed. */
+ int maxqueued_; /* Max packets to queue */
+ int maxpkt_; /* Max packet size. */
+ int qi_; /* In/out pointers for downstream */
+ int qo_; /* packets */
+
+ /*
+ * Active class state and WRR state.
+ */
+ rm_class_t *active_[RM_MAXPRIO]; /* Active cl's in each pri */
+ int na_[RM_MAXPRIO]; /* # of active cl's in a pri */
+ int num_[RM_MAXPRIO]; /* # of cl's per pri */
+ int alloc_[RM_MAXPRIO]; /* Byte Allocation */
+ u_long M_[RM_MAXPRIO]; /* WRR weights. */
+
+ /*
+ * Network Interface/Solaris Queue state pointer.
+ */
+ struct ifaltq *ifq_;
+ rm_class_t *default_; /* Default Pkt class, BE */
+ rm_class_t *root_; /* Root Link class. */
+ rm_class_t *ctl_; /* Control Traffic class. */
+ void (*restart)(struct ifaltq *); /* Restart routine. */
+
+ /*
+ * Current packet downstream packet state and dynamic state.
+ */
+ rm_class_t *borrowed_[RM_MAXQUEUED]; /* Class borrowed last */
+ rm_class_t *class_[RM_MAXQUEUED]; /* class sending */
+ int curlen_[RM_MAXQUEUED]; /* Current pktlen */
+ struct timeval now_[RM_MAXQUEUED]; /* Current packet time. */
+ int is_overlimit_[RM_MAXQUEUED];/* Current packet time. */
+
+ int cutoff_; /* Cut-off depth for borrowing */
+
+ struct timeval ifnow_; /* expected xmit completion time */
+#if 1 /* ALTQ4PPP */
+ int maxiftime_; /* max delay inside interface */
+#endif
+ rm_class_t *pollcache_; /* cached rm_class by poll operation */
+};
+
+/* flags for rmc_init and rmc_newclass */
+/* class flags */
+#define RMCF_RED 0x0001
+#define RMCF_ECN 0x0002
+#define RMCF_RIO 0x0004
+#define RMCF_FLOWVALVE 0x0008 /* use flowvalve (aka penalty-box) */
+#define RMCF_CLEARDSCP 0x0010 /* clear diffserv codepoint */
+
+/* flags for rmc_init */
+#define RMCF_WRR 0x0100
+#define RMCF_EFFICIENT 0x0200
+
+#define is_a_parent_class(cl) ((cl)->children_ != NULL)
+
+extern rm_class_t *rmc_newclass(int, struct rm_ifdat *, u_int,
+ void (*)(struct rm_class *, struct rm_class *),
+ int, struct rm_class *, struct rm_class *,
+ u_int, int, u_int, int, int);
+extern void rmc_delete_class(struct rm_ifdat *, struct rm_class *);
+extern int rmc_modclass(struct rm_class *, u_int, int,
+ u_int, int, u_int, int);
+extern void rmc_init(struct ifaltq *, struct rm_ifdat *, u_int,
+ void (*)(struct ifaltq *),
+ int, int, u_int, int, u_int, int);
+extern int rmc_queue_packet(struct rm_class *, mbuf_t *);
+extern mbuf_t *rmc_dequeue_next(struct rm_ifdat *, int);
+extern void rmc_update_class_util(struct rm_ifdat *);
+extern void rmc_delay_action(struct rm_class *, struct rm_class *);
+extern void rmc_dropall(struct rm_class *);
+extern int rmc_get_weight(struct rm_ifdat *, int);
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ALTQ_ALTQ_RMCLASS_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_rmclass_debug.h b/sbin/pfctl/missing/altq/altq_rmclass_debug.h
new file mode 100644
index 0000000..7fcc86b
--- /dev/null
+++ b/sbin/pfctl/missing/altq/altq_rmclass_debug.h
@@ -0,0 +1,113 @@
+/* $FreeBSD$ */
+/* $KAME: altq_rmclass_debug.h,v 1.3 2002/11/29 04:36:24 kjc Exp $ */
+
+/*
+ * Copyright (c) Sun Microsystems, Inc. 1998 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 the SMCC Technology
+ * Development Group at Sun Microsystems, Inc.
+ *
+ * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE
+ * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is
+ * provided "as is" without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this software.
+ */
+
+#ifndef _ALTQ_ALTQ_RMCLASS_DEBUG_H_
+#define _ALTQ_ALTQ_RMCLASS_DEBUG_H_
+
+/* #pragma ident "@(#)rm_class_debug.h 1.7 98/05/04 SMI" */
+
+/*
+ * Cbq debugging macros
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef CBQ_TRACE
+#ifndef NCBQTRACE
+#define NCBQTRACE (16 * 1024)
+#endif
+
+/*
+ * To view the trace output, using adb, type:
+ * adb -k /dev/ksyms /dev/mem <cr>, then type
+ * cbqtrace_count/D to get the count, then type
+ * cbqtrace_buffer,0tcount/Dp4C" "Xn
+ * This will dump the trace buffer from 0 to count.
+ */
+/*
+ * in ALTQ, "call cbqtrace_dump(N)" from DDB to display 20 events
+ * from Nth event in the circular buffer.
+ */
+
+struct cbqtrace {
+ int count;
+ int function; /* address of function */
+ int trace_action; /* descriptive 4 characters */
+ int object; /* object operated on */
+};
+
+extern struct cbqtrace cbqtrace_buffer[];
+extern struct cbqtrace *cbqtrace_ptr;
+extern int cbqtrace_count;
+
+#define CBQTRACEINIT() { \
+ if (cbqtrace_ptr == NULL) \
+ cbqtrace_ptr = cbqtrace_buffer; \
+ else { \
+ cbqtrace_ptr = cbqtrace_buffer; \
+ bzero((void *)cbqtrace_ptr, sizeof(cbqtrace_buffer)); \
+ cbqtrace_count = 0; \
+ } \
+}
+
+#define LOCK_TRACE() splimp()
+#define UNLOCK_TRACE(x) splx(x)
+
+#define CBQTRACE(func, act, obj) { \
+ int __s = LOCK_TRACE(); \
+ int *_p = &cbqtrace_ptr->count; \
+ *_p++ = ++cbqtrace_count; \
+ *_p++ = (int)(func); \
+ *_p++ = (int)(act); \
+ *_p++ = (int)(obj); \
+ if ((struct cbqtrace *)(void *)_p >= &cbqtrace_buffer[NCBQTRACE])\
+ cbqtrace_ptr = cbqtrace_buffer; \
+ else \
+ cbqtrace_ptr = (struct cbqtrace *)(void *)_p; \
+ UNLOCK_TRACE(__s); \
+ }
+#else
+
+/* If no tracing, define no-ops */
+#define CBQTRACEINIT()
+#define CBQTRACE(a, b, c)
+
+#endif /* !CBQ_TRACE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ALTQ_ALTQ_RMCLASS_DEBUG_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_var.h b/sbin/pfctl/missing/altq/altq_var.h
new file mode 100644
index 0000000..165b5af
--- /dev/null
+++ b/sbin/pfctl/missing/altq/altq_var.h
@@ -0,0 +1,267 @@
+/* $FreeBSD$ */
+/* $KAME: altq_var.h,v 1.15 2003/07/10 12:07:49 kjc Exp $ */
+
+/*
+ * Copyright (C) 1998-2003
+ * Sony Computer Science Laboratories 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 SONY CSL 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 SONY CSL 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 _ALTQ_ALTQ_VAR_H_
+#define _ALTQ_ALTQ_VAR_H_
+
+#ifdef _KERNEL
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/queue.h>
+
+#ifdef ALTQ3_CLFIER_COMPAT
+/*
+ * filter structure for altq common classifier
+ */
+struct acc_filter {
+ LIST_ENTRY(acc_filter) f_chain;
+ void *f_class; /* pointer to the class */
+ u_long f_handle; /* filter id */
+ u_int32_t f_fbmask; /* filter bitmask */
+ struct flow_filter f_filter; /* filter value */
+};
+
+/*
+ * XXX ACC_FILTER_TABLESIZE can't be larger than 2048 unless we fix
+ * the handle assignment.
+ */
+#define ACC_FILTER_TABLESIZE (256+1)
+#define ACC_FILTER_MASK (ACC_FILTER_TABLESIZE - 2)
+#define ACC_WILDCARD_INDEX (ACC_FILTER_TABLESIZE - 1)
+#ifdef __GNUC__
+#define ACC_GET_HASH_INDEX(addr) \
+ ({int x = (addr) + ((addr) >> 16); (x + (x >> 8)) & ACC_FILTER_MASK;})
+#else
+#define ACC_GET_HASH_INDEX(addr) \
+ (((addr) + ((addr) >> 8) + ((addr) >> 16) + ((addr) >> 24)) \
+ & ACC_FILTER_MASK)
+#endif
+#define ACC_GET_HINDEX(handle) ((handle) >> 20)
+
+#if (__FreeBSD_version > 500000)
+#define ACC_LOCK_INIT(ac) mtx_init(&(ac)->acc_mtx, "classifier", MTX_DEF)
+#define ACC_LOCK_DESTROY(ac) mtx_destroy(&(ac)->acc_mtx)
+#define ACC_LOCK(ac) mtx_lock(&(ac)->acc_mtx)
+#define ACC_UNLOCK(ac) mtx_unlock(&(ac)->acc_mtx)
+#else
+#define ACC_LOCK_INIT(ac)
+#define ACC_LOCK_DESTROY(ac)
+#define ACC_LOCK(ac)
+#define ACC_UNLOCK(ac)
+#endif
+
+struct acc_classifier {
+ u_int32_t acc_fbmask;
+ LIST_HEAD(filt, acc_filter) acc_filters[ACC_FILTER_TABLESIZE];
+
+#if (__FreeBSD_version > 500000)
+ struct mtx acc_mtx;
+#endif
+};
+
+/*
+ * flowinfo mask bits used by classifier
+ */
+/* for ipv4 */
+#define FIMB4_PROTO 0x0001
+#define FIMB4_TOS 0x0002
+#define FIMB4_DADDR 0x0004
+#define FIMB4_SADDR 0x0008
+#define FIMB4_DPORT 0x0010
+#define FIMB4_SPORT 0x0020
+#define FIMB4_GPI 0x0040
+#define FIMB4_ALL 0x007f
+/* for ipv6 */
+#define FIMB6_PROTO 0x0100
+#define FIMB6_TCLASS 0x0200
+#define FIMB6_DADDR 0x0400
+#define FIMB6_SADDR 0x0800
+#define FIMB6_DPORT 0x1000
+#define FIMB6_SPORT 0x2000
+#define FIMB6_GPI 0x4000
+#define FIMB6_FLABEL 0x8000
+#define FIMB6_ALL 0xff00
+
+#define FIMB_ALL (FIMB4_ALL|FIMB6_ALL)
+
+#define FIMB4_PORTS (FIMB4_DPORT|FIMB4_SPORT|FIMB4_GPI)
+#define FIMB6_PORTS (FIMB6_DPORT|FIMB6_SPORT|FIMB6_GPI)
+#endif /* ALTQ3_CLFIER_COMPAT */
+
+/*
+ * machine dependent clock
+ * a 64bit high resolution time counter.
+ */
+extern int machclk_usepcc;
+extern u_int32_t machclk_freq;
+extern u_int32_t machclk_per_tick;
+extern void init_machclk(void);
+extern u_int64_t read_machclk(void);
+
+/*
+ * debug support
+ */
+#ifdef ALTQ_DEBUG
+#ifdef __STDC__
+#define ASSERT(e) ((e) ? (void)0 : altq_assert(__FILE__, __LINE__, #e))
+#else /* PCC */
+#define ASSERT(e) ((e) ? (void)0 : altq_assert(__FILE__, __LINE__, "e"))
+#endif
+#else
+#define ASSERT(e) ((void)0)
+#endif
+
+/*
+ * misc stuff for compatibility
+ */
+/* ioctl cmd type */
+#if defined(__FreeBSD__) && (__FreeBSD__ < 3)
+typedef int ioctlcmd_t;
+#else
+typedef u_long ioctlcmd_t;
+#endif
+
+/*
+ * queue macros:
+ * the interface of TAILQ_LAST macro changed after the introduction
+ * of softupdate. redefine it here to make it work with pre-2.2.7.
+ */
+#undef TAILQ_LAST
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#ifndef TAILQ_EMPTY
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+#endif
+#ifndef TAILQ_FOREACH
+#define TAILQ_FOREACH(var, head, field) \
+ for (var = TAILQ_FIRST(head); var; var = TAILQ_NEXT(var, field))
+#endif
+
+/* macro for timeout/untimeout */
+#if (__FreeBSD_version > 300000) || defined(__NetBSD__)
+/* use callout */
+#include <sys/callout.h>
+
+#if (__FreeBSD_version > 500000)
+#define CALLOUT_INIT(c) callout_init((c), 0)
+#else
+#define CALLOUT_INIT(c) callout_init((c))
+#endif
+#define CALLOUT_RESET(c,t,f,a) callout_reset((c),(t),(f),(a))
+#define CALLOUT_STOP(c) callout_stop((c))
+#ifndef CALLOUT_INITIALIZER
+#define CALLOUT_INITIALIZER { { { NULL } }, 0, NULL, NULL, 0 }
+#endif
+#elif defined(__OpenBSD__)
+#include <sys/timeout.h>
+/* callout structure as a wrapper of struct timeout */
+struct callout {
+ struct timeout c_to;
+};
+#define CALLOUT_INIT(c) do { bzero((c), sizeof(*(c))); } while (/*CONSTCOND*/ 0)
+#define CALLOUT_RESET(c,t,f,a) do { if (!timeout_initialized(&(c)->c_to)) \
+ timeout_set(&(c)->c_to, (f), (a)); \
+ timeout_add(&(c)->c_to, (t)); } while (/*CONSTCOND*/ 0)
+#define CALLOUT_STOP(c) timeout_del(&(c)->c_to)
+#define CALLOUT_INITIALIZER { { { NULL }, NULL, NULL, 0, 0 } }
+#else
+/* use old-style timeout/untimeout */
+/* dummy callout structure */
+struct callout {
+ void *c_arg; /* function argument */
+ void (*c_func)(void *); /* functiuon to call */
+};
+#define CALLOUT_INIT(c) do { bzero((c), sizeof(*(c))); } while (/*CONSTCOND*/ 0)
+#define CALLOUT_RESET(c,t,f,a) do { (c)->c_arg = (a); \
+ (c)->c_func = (f); \
+ timeout((f),(a),(t)); } while (/*CONSTCOND*/ 0)
+#define CALLOUT_STOP(c) untimeout((c)->c_func,(c)->c_arg)
+#define CALLOUT_INITIALIZER { NULL, NULL }
+#endif
+#if !defined(__FreeBSD__)
+typedef void (timeout_t)(void *);
+#endif
+
+#define m_pktlen(m) ((m)->m_pkthdr.len)
+
+extern int pfaltq_running;
+
+struct ifnet; struct mbuf;
+struct pf_altq;
+#ifdef ALTQ3_CLFIER_COMPAT
+struct flowinfo;
+#endif
+
+void *altq_lookup(char *, int);
+#ifdef ALTQ3_CLFIER_COMPAT
+int altq_extractflow(struct mbuf *, int, struct flowinfo *, u_int32_t);
+int acc_add_filter(struct acc_classifier *, struct flow_filter *,
+ void *, u_long *);
+int acc_delete_filter(struct acc_classifier *, u_long);
+int acc_discard_filters(struct acc_classifier *, void *, int);
+void *acc_classify(void *, struct mbuf *, int);
+#endif
+u_int8_t read_dsfield(struct mbuf *, struct altq_pktattr *);
+void write_dsfield(struct mbuf *, struct altq_pktattr *, u_int8_t);
+void altq_assert(const char *, int, const char *);
+int tbr_set(struct ifaltq *, struct tb_profile *);
+int tbr_get(struct ifaltq *, struct tb_profile *);
+
+int altq_pfattach(struct pf_altq *);
+int altq_pfdetach(struct pf_altq *);
+int altq_add(struct pf_altq *);
+int altq_remove(struct pf_altq *);
+int altq_add_queue(struct pf_altq *);
+int altq_remove_queue(struct pf_altq *);
+int altq_getqstats(struct pf_altq *, void *, int *);
+
+int cbq_pfattach(struct pf_altq *);
+int cbq_add_altq(struct pf_altq *);
+int cbq_remove_altq(struct pf_altq *);
+int cbq_add_queue(struct pf_altq *);
+int cbq_remove_queue(struct pf_altq *);
+int cbq_getqstats(struct pf_altq *, void *, int *);
+
+int priq_pfattach(struct pf_altq *);
+int priq_add_altq(struct pf_altq *);
+int priq_remove_altq(struct pf_altq *);
+int priq_add_queue(struct pf_altq *);
+int priq_remove_queue(struct pf_altq *);
+int priq_getqstats(struct pf_altq *, void *, int *);
+
+int hfsc_pfattach(struct pf_altq *);
+int hfsc_add_altq(struct pf_altq *);
+int hfsc_remove_altq(struct pf_altq *);
+int hfsc_add_queue(struct pf_altq *);
+int hfsc_remove_queue(struct pf_altq *);
+int hfsc_getqstats(struct pf_altq *, void *, int *);
+
+#endif /* _KERNEL */
+#endif /* _ALTQ_ALTQ_VAR_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_wfq.h b/sbin/pfctl/missing/altq/altq_wfq.h
new file mode 100644
index 0000000..ca163cf
--- /dev/null
+++ b/sbin/pfctl/missing/altq/altq_wfq.h
@@ -0,0 +1,129 @@
+/* $FreeBSD$ */
+/* $KAME: altq_wfq.h,v 1.8 2003/07/10 12:07:49 kjc Exp $ */
+
+/*
+ * Copyright (C) 1997-2002
+ * Sony Computer Science Laboratories 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 SONY CSL 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 SONY CSL 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.
+ */
+/*
+ * March 27, 1997. Written by Hiroshi Kyusojin of Keio University
+ * (kyu@mt.cs.keio.ac.jp).
+ */
+
+#ifndef _ALTQ_ALTQ_WFQ_H_
+#define _ALTQ_ALTQ_WFQ_H_
+
+#include <altq/altq.h>
+
+#define DEFAULT_QSIZE 256
+#define MAX_QSIZE 2048
+
+struct wfq_interface{
+ char wfq_ifacename[IFNAMSIZ];
+};
+
+struct wfq_getqid{
+ struct wfq_interface iface;
+#ifdef ALTQ3_CLFIER_COMPAT
+ struct flowinfo flow;
+#endif
+ u_long qid;
+};
+
+struct wfq_setweight {
+ struct wfq_interface iface;
+ int qid;
+ int weight;
+};
+
+typedef struct each_queue_stats {
+ int bytes; /* bytes in this queue */
+ int weight; /* weight in percent */
+ struct pktcntr xmit_cnt;
+ struct pktcntr drop_cnt;
+} queue_stats;
+
+struct wfq_getstats {
+ struct wfq_interface iface;
+ int qid;
+ queue_stats stats;
+};
+
+struct wfq_conf {
+ struct wfq_interface iface;
+ int hash_policy; /* hash policy */
+ int nqueues; /* number of queues */
+ int qlimit; /* queue size in bytes */
+};
+
+#define WFQ_HASH_DSTADDR 0 /* hash by dst address */
+#define WFQ_HASH_SRCPORT 1 /* hash by src port */
+#define WFQ_HASH_FULL 2 /* hash by all fields */
+
+#define WFQ_IF_ATTACH _IOW('Q', 1, struct wfq_interface)
+#define WFQ_IF_DETACH _IOW('Q', 2, struct wfq_interface)
+#define WFQ_ENABLE _IOW('Q', 3, struct wfq_interface)
+#define WFQ_DISABLE _IOW('Q', 4, struct wfq_interface)
+#define WFQ_CONFIG _IOWR('Q', 6, struct wfq_conf)
+#define WFQ_GET_STATS _IOWR('Q', 12, struct wfq_getstats)
+#define WFQ_GET_QID _IOWR('Q', 30, struct wfq_getqid)
+#define WFQ_SET_WEIGHT _IOWR('Q', 31, struct wfq_setweight)
+
+#ifdef _KERNEL
+
+#define HWM (64 * 1024)
+#define WFQ_QUOTA 512 /* quota bytes to send at a time */
+#define WFQ_ADDQUOTA(q) ((q)->quota += WFQ_QUOTA * (q)->weight / 100)
+#define ENABLE 0
+#define DISABLE 1
+
+typedef struct weighted_fair_queue{
+ struct weighted_fair_queue *next, *prev;
+ struct mbuf *head, *tail;
+ int bytes; /* bytes in this queue */
+ int quota; /* bytes sent in this round */
+ int weight; /* weight in percent */
+
+ struct pktcntr xmit_cnt;
+ struct pktcntr drop_cnt;
+} wfq;
+
+
+typedef struct wfqstate {
+ struct wfqstate *next; /* for wfqstate list */
+ struct ifaltq *ifq;
+ int nums; /* number of queues */
+ int hwm; /* high water mark */
+ int bytes; /* total bytes in all the queues */
+ wfq *rrp; /* round robin pointer */
+ wfq *queue; /* pointer to queue list */
+#ifdef ALTQ3_CLFIER_COMPAT
+ u_long (*hash_func)(struct flowinfo *, int);
+#endif
+ u_int32_t fbmask; /* filter bitmask */
+} wfq_state_t;
+
+#endif /* _KERNEL */
+
+#endif /* _ALTQ_ALTQ_WFQ_H */
diff --git a/sbin/pflogd/Makefile b/sbin/pflogd/Makefile
new file mode 100644
index 0000000..01c41d0
--- /dev/null
+++ b/sbin/pflogd/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/pf/pflogd
+
+PROG= pflogd
+SRCS= pflogd.c pidfile.c privsep.c privsep_fdpass.c
+MAN= pflogd.8
+
+CFLAGS+=-Wall -Werror -Wmissing-prototypes -Wshadow
+LDADD= -lpcap -lutil
+DPADD= ${LIBPCAP} ${LIBUTIL}
+
+.include <bsd.prog.mk>
diff --git a/sbin/ping/Makefile b/sbin/ping/Makefile
new file mode 100644
index 0000000..24882d3
--- /dev/null
+++ b/sbin/ping/Makefile
@@ -0,0 +1,21 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= ping
+MAN= ping.8
+BINOWN= root
+BINMODE=4555
+.if ${MACHINE_ARCH} == "alpha"
+CFLAGS+=-fno-builtin # GCC's builtin memcpy doesn't do unaligned copies
+.endif
+WARNS?= 2
+DPADD= ${LIBM}
+LDADD= -lm
+
+.if !defined(RELEASE_CRUNCH)
+CFLAGS+=-DIPSEC
+DPADD+= ${LIBIPSEC}
+LDADD+= -lipsec
+.endif
+
+.include <bsd.prog.mk>
diff --git a/sbin/ping/ping.8 b/sbin/ping/ping.8
new file mode 100644
index 0000000..0e80de1
--- /dev/null
+++ b/sbin/ping/ping.8
@@ -0,0 +1,544 @@
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)ping.8 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.Dd August 15, 2005
+.Dt PING 8
+.Os
+.Sh NAME
+.Nm ping
+.Nd send
+.Tn ICMP ECHO_REQUEST
+packets to network hosts
+.Sh SYNOPSIS
+.Nm
+.Op Fl AaDdfnoQqRrv
+.Op Fl c Ar count
+.Op Fl G Ar sweepmaxsize
+.Op Fl g Ar sweepminsize
+.Op Fl h Ar sweepincrsize
+.Op Fl i Ar wait
+.Op Fl l Ar preload
+.Op Fl M Cm mask | time
+.Op Fl m Ar ttl
+.Op Fl P Ar policy
+.Op Fl p Ar pattern
+.Op Fl S Ar src_addr
+.Op Fl s Ar packetsize
+.Op Fl t Ar timeout
+.Op Fl z Ar tos
+.Ar host
+.Nm
+.Op Fl AaDdfLnoQqRrv
+.Op Fl c Ar count
+.Op Fl I Ar iface
+.Op Fl i Ar wait
+.Op Fl l Ar preload
+.Op Fl M Cm mask | time
+.Op Fl m Ar ttl
+.Op Fl P Ar policy
+.Op Fl p Ar pattern
+.Op Fl S Ar src_addr
+.Op Fl s Ar packetsize
+.Op Fl T Ar ttl
+.Op Fl t Ar timeout
+.Op Fl z Ar tos
+.Ar mcast-group
+.Sh DESCRIPTION
+The
+.Nm
+utility uses the
+.Tn ICMP
+.No protocol Ap s mandatory
+.Tn ECHO_REQUEST
+datagram to elicit an
+.Tn ICMP ECHO_RESPONSE
+from a host or gateway.
+.Tn ECHO_REQUEST
+datagrams
+.Pq Dq pings
+have an IP and
+.Tn ICMP
+header, followed by a
+.Dq struct timeval
+and then an arbitrary number of
+.Dq pad
+bytes used to fill out the packet.
+The options are as follows:
+.Bl -tag -width indent
+.It Fl A
+Audible.
+Output a bell
+.Tn ( ASCII
+0x07)
+character when no packet is received before the next packet
+is transmitted.
+To cater for round-trip times that are longer than the interval
+between transmissions, further missing packets cause a bell only
+if the maximum number of unreceived packets has increased.
+.It Fl a
+Audible.
+Include a bell
+.Tn ( ASCII
+0x07)
+character in the output when any packet is received.
+This option is ignored
+if other format options are present.
+.It Fl c Ar count
+Stop after sending
+(and receiving)
+.Ar count
+.Tn ECHO_RESPONSE
+packets.
+If this option is not specified,
+.Nm
+will operate until interrupted.
+If this option is specified in conjunction with ping sweeps,
+each sweep will consist of
+.Ar count
+packets.
+.It Fl D
+Set the Don't Fragment bit.
+.It Fl d
+Set the
+.Dv SO_DEBUG
+option on the socket being used.
+.It Fl f
+Flood ping.
+Outputs packets as fast as they come back or one hundred times per second,
+whichever is more.
+For every
+.Tn ECHO_REQUEST
+sent a period
+.Dq .\&
+is printed, while for every
+.Tn ECHO_REPLY
+received a backspace is printed.
+This provides a rapid display of how many packets are being dropped.
+Only the super-user may use this option.
+.Bf -emphasis
+This can be very hard on a network and should be used with caution.
+.Ef
+.It Fl G Ar sweepmaxsize
+Specify the maximum size of
+.Tn ICMP
+payload when sending sweeping pings.
+This option is required for ping sweeps.
+.It Fl g Ar sweepminsize
+Specify the size of
+.Tn ICMP
+payload to start with when sending sweeping pings.
+The default value is 0.
+.It Fl h Ar sweepincrsize
+Specify the number of bytes to increment the size of
+.Tn ICMP
+payload after
+each sweep when sending sweeping pings.
+The default value is 1.
+.It Fl I Ar iface
+Source multicast packets with the given interface address.
+This flag only applies if the ping destination is a multicast address.
+.It Fl i Ar wait
+Wait
+.Ar wait
+seconds
+.Em between sending each packet .
+The default is to wait for one second between each packet.
+The wait time may be fractional, but only the super-user may specify
+values less than 1 second.
+This option is incompatible with the
+.Fl f
+option.
+.It Fl L
+Suppress loopback of multicast packets.
+This flag only applies if the ping destination is a multicast address.
+.It Fl l Ar preload
+If
+.Ar preload
+is specified,
+.Nm
+sends that many packets as fast as possible before falling into its normal
+mode of behavior.
+Only the super-user may use this option.
+.It Fl M Cm mask | time
+Use
+.Dv ICMP_MASKREQ
+or
+.Dv ICMP_TSTAMP
+instead of
+.Dv ICMP_ECHO .
+For
+.Cm mask ,
+print the netmask of the remote machine.
+Set the
+.Va net.inet.icmp.maskrepl
+MIB variable to enable
+.Dv ICMP_MASKREPLY .
+For
+.Cm time ,
+print the origination, reception and transmission timestamps.
+.It Fl m Ar ttl
+Set the IP Time To Live for outgoing packets.
+If not specified, the kernel uses the value of the
+.Va net.inet.ip.ttl
+MIB variable.
+.It Fl n
+Numeric output only.
+No attempt will be made to lookup symbolic names for host addresses.
+.It Fl o
+Exit successfully after receiving one reply packet.
+.It Fl P Ar policy
+.Ar policy
+specifies IPsec policy for the ping session.
+For details please refer to
+.Xr ipsec 4
+and
+.Xr ipsec_set_policy 3 .
+.It Fl p Ar pattern
+You may specify up to 16
+.Dq pad
+bytes to fill out the packet you send.
+This is useful for diagnosing data-dependent problems in a network.
+For example,
+.Dq Li \-p ff
+will cause the sent packet to be filled with all
+ones.
+.It Fl Q
+Somewhat quiet output.
+.No Don Ap t
+display ICMP error messages that are in response to our query messages.
+Originally, the
+.Fl v
+flag was required to display such errors, but
+.Fl v
+displays all ICMP error messages.
+On a busy machine, this output can be overbearing.
+Without the
+.Fl Q
+flag,
+.Nm
+prints out any ICMP error messages caused by its own ECHO_REQUEST
+messages.
+.It Fl q
+Quiet output.
+Nothing is displayed except the summary lines at startup time and
+when finished.
+.It Fl R
+Record route.
+Includes the
+.Tn RECORD_ROUTE
+option in the
+.Tn ECHO_REQUEST
+packet and displays
+the route buffer on returned packets.
+Note that the IP header is only large enough for nine such routes;
+the
+.Xr traceroute 8
+command is usually better at determining the route packets take to a
+particular destination.
+If more routes come back than should, such as due to an illegal spoofed
+packet, ping will print the route list and then truncate it at the correct
+spot.
+Many hosts ignore or discard the
+.Tn RECORD_ROUTE
+option.
+.It Fl r
+Bypass the normal routing tables and send directly to a host on an attached
+network.
+If the host is not on a directly-attached network, an error is returned.
+This option can be used to ping a local host through an interface
+that has no route through it
+(e.g., after the interface was dropped by
+.Xr routed 8 ) .
+.It Fl S Ar src_addr
+Use the following IP address as the source address in outgoing packets.
+On hosts with more than one IP address, this option can be used to
+force the source address to be something other than the IP address
+of the interface the probe packet is sent on.
+If the IP address
+is not one of this machine's interface addresses, an error is
+returned and nothing is sent.
+.It Fl s Ar packetsize
+Specify the number of data bytes to be sent.
+The default is 56, which translates into 64
+.Tn ICMP
+data bytes when combined
+with the 8 bytes of
+.Tn ICMP
+header data.
+Only the super-user may specify values more than default.
+This option cannot be used with ping sweeps.
+.It Fl T Ar ttl
+Set the IP Time To Live for multicasted packets.
+This flag only applies if the ping destination is a multicast address.
+.It Fl t Ar timeout
+Specify a timeout, in seconds, before ping exits regardless of how
+many packets have been received.
+.It Fl v
+Verbose output.
+.Tn ICMP
+packets other than
+.Tn ECHO_RESPONSE
+that are received are listed.
+.It Fl z Ar tos
+Use the specified type of service.
+.El
+.Pp
+When using
+.Nm
+for fault isolation, it should first be run on the local host, to verify
+that the local network interface is up and running.
+Then, hosts and gateways further and further away should be
+.Dq pinged .
+Round-trip times and packet loss statistics are computed.
+If duplicate packets are received, they are not included in the packet
+loss calculation, although the round trip time of these packets is used
+in calculating the round-trip time statistics.
+When the specified number of packets have been sent
+(and received)
+or if the program is terminated with a
+.Dv SIGINT ,
+a brief summary is displayed, showing the number of packets sent and
+received, and the minimum, mean, maximum, and standard deviation of
+the round-trip times.
+.Pp
+If
+.Nm
+receives a
+.Dv SIGINFO
+(see the
+.Cm status
+argument for
+.Xr stty 1 )
+signal, the current number of packets sent and received, and the
+minimum, mean, and maximum of the round-trip times will be written to
+the standard error output.
+.Pp
+This program is intended for use in network testing, measurement and
+management.
+Because of the load it can impose on the network, it is unwise to use
+.Nm
+during normal operations or from automated scripts.
+.Sh ICMP PACKET DETAILS
+An IP header without options is 20 bytes.
+An
+.Tn ICMP
+.Tn ECHO_REQUEST
+packet contains an additional 8 bytes worth of
+.Tn ICMP
+header followed by an arbitrary amount of data.
+When a
+.Ar packetsize
+is given, this indicated the size of this extra piece of data
+(the default is 56).
+Thus the amount of data received inside of an IP packet of type
+.Tn ICMP
+.Tn ECHO_REPLY
+will always be 8 bytes more than the requested data space
+(the
+.Tn ICMP
+header).
+.Pp
+If the data space is at least eight bytes large,
+.Nm
+uses the first eight bytes of this space to include a timestamp which
+it uses in the computation of round trip times.
+If less than eight bytes of pad are specified, no round trip times are
+given.
+.Sh DUPLICATE AND DAMAGED PACKETS
+The
+.Nm
+utility will report duplicate and damaged packets.
+Duplicate packets should never occur when pinging a unicast address,
+and seem to be caused by
+inappropriate link-level retransmissions.
+Duplicates may occur in many situations and are rarely
+(if ever)
+a good sign, although the presence of low levels of duplicates may not
+always be cause for alarm.
+Duplicates are expected when pinging a broadcast or multicast address,
+since they are not really duplicates but replies from different hosts
+to the same request.
+.Pp
+Damaged packets are obviously serious cause for alarm and often
+indicate broken hardware somewhere in the
+.Nm
+packet's path (in the network or in the hosts).
+.Sh TRYING DIFFERENT DATA PATTERNS
+The
+(inter)network
+layer should never treat packets differently depending on the data
+contained in the data portion.
+Unfortunately, data-dependent problems have been known to sneak into
+networks and remain undetected for long periods of time.
+In many cases the particular pattern that will have problems is something
+that does not have sufficient
+.Dq transitions ,
+such as all ones or all zeros, or a pattern right at the edge, such as
+almost all zeros.
+It is not
+necessarily enough to specify a data pattern of all zeros (for example)
+on the command line because the pattern that is of interest is
+at the data link level, and the relationship between what you type and
+what the controllers transmit can be complicated.
+.Pp
+This means that if you have a data-dependent problem you will probably
+have to do a lot of testing to find it.
+If you are lucky, you may manage to find a file that either
+cannot
+be sent across your network or that takes much longer to transfer than
+other similar length files.
+You can then examine this file for repeated patterns that you can test
+using the
+.Fl p
+option of
+.Nm .
+.Sh TTL DETAILS
+The
+.Tn TTL
+value of an IP packet represents the maximum number of IP routers
+that the packet can go through before being thrown away.
+In current practice you can expect each router in the Internet to decrement
+the
+.Tn TTL
+field by exactly one.
+.Pp
+The
+.Tn TCP/IP
+specification recommends setting the
+.Tn TTL
+field for
+.Tn IP
+packets to 64, but many systems use smaller values
+.No ( Bx 4.3
+uses 30,
+.Bx 4.2
+used 15).
+.Pp
+The maximum possible value of this field is 255, and most
+.Ux
+systems set
+the
+.Tn TTL
+field of
+.Tn ICMP ECHO_REQUEST
+packets to 255.
+This is why you will find you can
+.Dq ping
+some hosts, but not reach them with
+.Xr telnet 1
+or
+.Xr ftp 1 .
+.Pp
+In normal operation
+.Nm
+prints the ttl value from the packet it receives.
+When a remote system receives a ping packet, it can do one of three things
+with the
+.Tn TTL
+field in its response:
+.Bl -bullet
+.It
+Not change it; this is what
+.Bx
+systems did before the
+.Bx 4.3 tahoe
+release.
+In this case the
+.Tn TTL
+value in the received packet will be 255 minus the
+number of routers in the round-trip path.
+.It
+Set it to 255; this is what current
+.Bx
+systems do.
+In this case the
+.Tn TTL
+value in the received packet will be 255 minus the
+number of routers in the path
+.Em from
+the remote system
+.Em to
+the
+.Nm Ns Em ing
+host.
+.It
+Set it to some other value.
+Some machines use the same value for
+.Tn ICMP
+packets that they use for
+.Tn TCP
+packets, for example either 30 or 60.
+Others may use completely wild values.
+.El
+.Sh RETURN VALUES
+The
+.Nm
+utility returns an exit status of zero if at least one response was
+heard from the specified
+.Ar host ;
+a status of two if the transmission was successful but no responses
+were received; or another value
+(from
+.In sysexits.h )
+if an error occurred.
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr ifconfig 8 ,
+.Xr routed 8 ,
+.Xr traceroute 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 .
+.Sh AUTHORS
+The original
+.Nm
+utility was written by
+.An Mike Muuss
+while at the US Army Ballistics
+Research Laboratory.
+.Sh BUGS
+Many Hosts and Gateways ignore the
+.Tn RECORD_ROUTE
+option.
+.Pp
+The maximum IP header length is too small for options like
+.Tn RECORD_ROUTE
+to be completely useful.
+.No There Ap s
+not much that can be done about this, however.
+.Pp
+Flood pinging is not recommended in general, and flood pinging the
+broadcast address should only be done under very controlled conditions.
+.Pp
+The
+.Fl v
+option is not worth much on busy hosts.
diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c
new file mode 100644
index 0000000..1cd2897
--- /dev/null
+++ b/sbin/ping/ping.c
@@ -0,0 +1,1698 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * P I N G . C
+ *
+ * Using the Internet Control Message Protocol (ICMP) "ECHO" facility,
+ * measure round-trip-delays and packet loss across network paths.
+ *
+ * Author -
+ * Mike Muuss
+ * U. S. Army Ballistic Research Laboratory
+ * December, 1983
+ *
+ * Status -
+ * Public Domain. Distribution Unlimited.
+ * Bugs -
+ * More statistics could always be gathered.
+ * This program has to run SUID to ROOT to access the ICMP socket.
+ */
+
+#include <sys/param.h> /* NB: we rely on this for <sys/types.h> */
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/ip_var.h>
+#include <arpa/inet.h>
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#endif /*IPSEC*/
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <math.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#define INADDR_LEN ((int)sizeof(in_addr_t))
+#define TIMEVAL_LEN ((int)sizeof(struct tv32))
+#define MASK_LEN (ICMP_MASKLEN - ICMP_MINLEN)
+#define TS_LEN (ICMP_TSLEN - ICMP_MINLEN)
+#define DEFDATALEN 56 /* default data length */
+#define FLOOD_BACKOFF 20000 /* usecs to back off if F_FLOOD mode */
+ /* runs out of buffer space */
+#define MAXIPLEN (sizeof(struct ip) + MAX_IPOPTLEN)
+#define MAXICMPLEN (ICMP_ADVLENMIN + MAX_IPOPTLEN)
+#define MAXWAIT 10 /* max seconds to wait for response */
+#define MAXALARM (60 * 60) /* max seconds for alarm timeout */
+#define MAXTOS 255
+
+#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
+#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
+#define SET(bit) (A(bit) |= B(bit))
+#define CLR(bit) (A(bit) &= (~B(bit)))
+#define TST(bit) (A(bit) & B(bit))
+
+struct tv32 {
+ int32_t tv32_sec;
+ int32_t tv32_usec;
+};
+
+/* various options */
+int options;
+#define F_FLOOD 0x0001
+#define F_INTERVAL 0x0002
+#define F_NUMERIC 0x0004
+#define F_PINGFILLED 0x0008
+#define F_QUIET 0x0010
+#define F_RROUTE 0x0020
+#define F_SO_DEBUG 0x0040
+#define F_SO_DONTROUTE 0x0080
+#define F_VERBOSE 0x0100
+#define F_QUIET2 0x0200
+#define F_NOLOOP 0x0400
+#define F_MTTL 0x0800
+#define F_MIF 0x1000
+#define F_AUDIBLE 0x2000
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+#define F_POLICY 0x4000
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif /*IPSEC*/
+#define F_TTL 0x8000
+#define F_MISSED 0x10000
+#define F_ONCE 0x20000
+#define F_HDRINCL 0x40000
+#define F_MASK 0x80000
+#define F_TIME 0x100000
+#define F_SWEEP 0x200000
+
+/*
+ * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum
+ * number of received sequence numbers we can keep track of. Change 128
+ * to 8192 for complete accuracy...
+ */
+#define MAX_DUP_CHK (8 * 128)
+int mx_dup_ck = MAX_DUP_CHK;
+char rcvd_tbl[MAX_DUP_CHK / 8];
+
+struct sockaddr_in whereto; /* who to ping */
+int datalen = DEFDATALEN;
+int maxpayload;
+int s; /* socket file descriptor */
+u_char outpackhdr[IP_MAXPACKET], *outpack;
+char BBELL = '\a'; /* characters written for MISSED and AUDIBLE */
+char BSPACE = '\b'; /* characters written for flood */
+char DOT = '.';
+char *hostname;
+char *shostname;
+int ident; /* process id to identify our packets */
+int uid; /* cached uid for micro-optimization */
+u_char icmp_type = ICMP_ECHO;
+u_char icmp_type_rsp = ICMP_ECHOREPLY;
+int phdr_len = 0;
+int send_len;
+
+/* counters */
+long nmissedmax; /* max value of ntransmitted - nreceived - 1 */
+long npackets; /* max packets to transmit */
+long nreceived; /* # of packets we got back */
+long nrepeats; /* number of duplicates */
+long ntransmitted; /* sequence # for outbound packets = #sent */
+long snpackets; /* max packets to transmit in one sweep */
+long snreceived; /* # of packets we got back in this sweep */
+long sntransmitted; /* # of packets we sent in this sweep */
+int sweepmax; /* max value of payload in sweep */
+int sweepmin = 0; /* start value of payload in sweep */
+int sweepincr = 1; /* payload increment in sweep */
+int interval = 1000; /* interval between packets, ms */
+
+/* timing */
+int timing; /* flag to do timing */
+double tmin = 999999999.0; /* minimum round trip time */
+double tmax = 0.0; /* maximum round trip time */
+double tsum = 0.0; /* sum of all times, for doing average */
+double tsumsq = 0.0; /* sum of all times squared, for std. dev. */
+
+volatile sig_atomic_t finish_up; /* nonzero if we've been told to finish up */
+volatile sig_atomic_t siginfo_p;
+
+static void fill(char *, char *);
+static u_short in_cksum(u_short *, int);
+static void check_status(void);
+static void finish(void) __dead2;
+static void pinger(void);
+static char *pr_addr(struct in_addr);
+static char *pr_ntime(n_time);
+static void pr_icmph(struct icmp *);
+static void pr_iph(struct ip *);
+static void pr_pack(char *, int, struct sockaddr_in *, struct timeval *);
+static void pr_retip(struct ip *);
+static void status(int);
+static void stopit(int);
+static void tvsub(struct timeval *, struct timeval *);
+static void usage(void) __dead2;
+
+int
+main(argc, argv)
+ int argc;
+ char *const *argv;
+{
+ struct sockaddr_in from, sock_in;
+ struct in_addr ifaddr;
+ struct timeval last, intvl;
+ struct iovec iov;
+ struct ip *ip;
+ struct msghdr msg;
+ struct sigaction si_sa;
+ size_t sz;
+ u_char *datap, packet[IP_MAXPACKET];
+ char *ep, *source, *target, *payload;
+ struct hostent *hp;
+#ifdef IPSEC_POLICY_IPSEC
+ char *policy_in, *policy_out;
+#endif
+ struct sockaddr_in *to;
+ double t;
+ u_long alarmtimeout, ultmp;
+ int almost_done, ch, df, hold, i, icmp_len, mib[4], preload, sockerrno,
+ tos, ttl;
+ char ctrl[CMSG_SPACE(sizeof(struct timeval))];
+ char hnamebuf[MAXHOSTNAMELEN], snamebuf[MAXHOSTNAMELEN];
+#ifdef IP_OPTIONS
+ char rspace[MAX_IPOPTLEN]; /* record route space */
+#endif
+ unsigned char loop, mttl;
+
+ payload = source = NULL;
+#ifdef IPSEC_POLICY_IPSEC
+ policy_in = policy_out = NULL;
+#endif
+
+ /*
+ * Do the stuff that we need root priv's for *first*, and
+ * then drop our setuid bit. Save error reporting for
+ * after arg parsing.
+ */
+ s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ sockerrno = errno;
+
+ setuid(getuid());
+ uid = getuid();
+
+ alarmtimeout = df = preload = tos = 0;
+
+ outpack = outpackhdr + sizeof(struct ip);
+ while ((ch = getopt(argc, argv,
+ "Aac:DdfG:g:h:I:i:Ll:M:m:nop:QqRrS:s:T:t:vz:"
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ "P:"
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif /*IPSEC*/
+ )) != -1)
+ {
+ switch(ch) {
+ case 'A':
+ options |= F_MISSED;
+ break;
+ case 'a':
+ options |= F_AUDIBLE;
+ break;
+ case 'c':
+ ultmp = strtoul(optarg, &ep, 0);
+ if (*ep || ep == optarg || ultmp > LONG_MAX || !ultmp)
+ errx(EX_USAGE,
+ "invalid count of packets to transmit: `%s'",
+ optarg);
+ npackets = ultmp;
+ break;
+ case 'D':
+ options |= F_HDRINCL;
+ df = 1;
+ break;
+ case 'd':
+ options |= F_SO_DEBUG;
+ break;
+ case 'f':
+ if (uid) {
+ errno = EPERM;
+ err(EX_NOPERM, "-f flag");
+ }
+ options |= F_FLOOD;
+ setbuf(stdout, (char *)NULL);
+ break;
+ case 'G': /* Maximum packet size for ping sweep */
+ ultmp = strtoul(optarg, &ep, 0);
+ if (*ep || ep == optarg)
+ errx(EX_USAGE, "invalid packet size: `%s'",
+ optarg);
+ if (uid != 0 && ultmp > DEFDATALEN) {
+ errno = EPERM;
+ err(EX_NOPERM,
+ "packet size too large: %lu > %u",
+ ultmp, DEFDATALEN);
+ }
+ options |= F_SWEEP;
+ sweepmax = ultmp;
+ break;
+ case 'g': /* Minimum packet size for ping sweep */
+ ultmp = strtoul(optarg, &ep, 0);
+ if (*ep || ep == optarg)
+ errx(EX_USAGE, "invalid packet size: `%s'",
+ optarg);
+ if (uid != 0 && ultmp > DEFDATALEN) {
+ errno = EPERM;
+ err(EX_NOPERM,
+ "packet size too large: %lu > %u",
+ ultmp, DEFDATALEN);
+ }
+ options |= F_SWEEP;
+ sweepmin = ultmp;
+ break;
+ case 'h': /* Packet size increment for ping sweep */
+ ultmp = strtoul(optarg, &ep, 0);
+ if (*ep || ep == optarg || ultmp < 1)
+ errx(EX_USAGE, "invalid increment size: `%s'",
+ optarg);
+ if (uid != 0 && ultmp > DEFDATALEN) {
+ errno = EPERM;
+ err(EX_NOPERM,
+ "packet size too large: %lu > %u",
+ ultmp, DEFDATALEN);
+ }
+ options |= F_SWEEP;
+ sweepincr = ultmp;
+ break;
+ case 'I': /* multicast interface */
+ if (inet_aton(optarg, &ifaddr) == 0)
+ errx(EX_USAGE,
+ "invalid multicast interface: `%s'",
+ optarg);
+ options |= F_MIF;
+ break;
+ case 'i': /* wait between sending packets */
+ t = strtod(optarg, &ep) * 1000.0;
+ if (*ep || ep == optarg || t > (double)INT_MAX)
+ errx(EX_USAGE, "invalid timing interval: `%s'",
+ optarg);
+ options |= F_INTERVAL;
+ interval = (int)t;
+ if (uid && interval < 1000) {
+ errno = EPERM;
+ err(EX_NOPERM, "-i interval too short");
+ }
+ break;
+ case 'L':
+ options |= F_NOLOOP;
+ loop = 0;
+ break;
+ case 'l':
+ ultmp = strtoul(optarg, &ep, 0);
+ if (*ep || ep == optarg || ultmp > INT_MAX)
+ errx(EX_USAGE,
+ "invalid preload value: `%s'", optarg);
+ if (uid) {
+ errno = EPERM;
+ err(EX_NOPERM, "-l flag");
+ }
+ preload = ultmp;
+ break;
+ case 'M':
+ switch(optarg[0]) {
+ case 'M':
+ case 'm':
+ options |= F_MASK;
+ break;
+ case 'T':
+ case 't':
+ options |= F_TIME;
+ break;
+ default:
+ errx(EX_USAGE, "invalid message: `%c'", optarg[0]);
+ break;
+ }
+ break;
+ case 'm': /* TTL */
+ ultmp = strtoul(optarg, &ep, 0);
+ if (*ep || ep == optarg || ultmp > MAXTTL)
+ errx(EX_USAGE, "invalid TTL: `%s'", optarg);
+ ttl = ultmp;
+ options |= F_TTL;
+ break;
+ case 'n':
+ options |= F_NUMERIC;
+ break;
+ case 'o':
+ options |= F_ONCE;
+ break;
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ case 'P':
+ options |= F_POLICY;
+ if (!strncmp("in", optarg, 2))
+ policy_in = strdup(optarg);
+ else if (!strncmp("out", optarg, 3))
+ policy_out = strdup(optarg);
+ else
+ errx(1, "invalid security policy");
+ break;
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif /*IPSEC*/
+ case 'p': /* fill buffer with user pattern */
+ options |= F_PINGFILLED;
+ payload = optarg;
+ break;
+ case 'Q':
+ options |= F_QUIET2;
+ break;
+ case 'q':
+ options |= F_QUIET;
+ break;
+ case 'R':
+ options |= F_RROUTE;
+ break;
+ case 'r':
+ options |= F_SO_DONTROUTE;
+ break;
+ case 'S':
+ source = optarg;
+ break;
+ case 's': /* size of packet to send */
+ ultmp = strtoul(optarg, &ep, 0);
+ if (*ep || ep == optarg)
+ errx(EX_USAGE, "invalid packet size: `%s'",
+ optarg);
+ if (uid != 0 && ultmp > DEFDATALEN) {
+ errno = EPERM;
+ err(EX_NOPERM,
+ "packet size too large: %lu > %u",
+ ultmp, DEFDATALEN);
+ }
+ datalen = ultmp;
+ break;
+ case 'T': /* multicast TTL */
+ ultmp = strtoul(optarg, &ep, 0);
+ if (*ep || ep == optarg || ultmp > MAXTTL)
+ errx(EX_USAGE, "invalid multicast TTL: `%s'",
+ optarg);
+ mttl = ultmp;
+ options |= F_MTTL;
+ break;
+ case 't':
+ alarmtimeout = strtoul(optarg, &ep, 0);
+ if ((alarmtimeout < 1) || (alarmtimeout == ULONG_MAX))
+ errx(EX_USAGE, "invalid timeout: `%s'",
+ optarg);
+ if (alarmtimeout > MAXALARM)
+ errx(EX_USAGE, "invalid timeout: `%s' > %d",
+ optarg, MAXALARM);
+ alarm((int)alarmtimeout);
+ break;
+ case 'v':
+ options |= F_VERBOSE;
+ break;
+ case 'z':
+ options |= F_HDRINCL;
+ ultmp = strtoul(optarg, &ep, 0);
+ if (*ep || ep == optarg || ultmp > MAXTOS)
+ errx(EX_USAGE, "invalid TOS: `%s'", optarg);
+ tos = ultmp;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (argc - optind != 1)
+ usage();
+ target = argv[optind];
+
+ switch (options & (F_MASK|F_TIME)) {
+ case 0: break;
+ case F_MASK:
+ icmp_type = ICMP_MASKREQ;
+ icmp_type_rsp = ICMP_MASKREPLY;
+ phdr_len = MASK_LEN;
+ if (!(options & F_QUIET))
+ (void)printf("ICMP_MASKREQ\n");
+ break;
+ case F_TIME:
+ icmp_type = ICMP_TSTAMP;
+ icmp_type_rsp = ICMP_TSTAMPREPLY;
+ phdr_len = TS_LEN;
+ if (!(options & F_QUIET))
+ (void)printf("ICMP_TSTAMP\n");
+ break;
+ default:
+ errx(EX_USAGE, "ICMP_TSTAMP and ICMP_MASKREQ are exclusive.");
+ break;
+ }
+ icmp_len = sizeof(struct ip) + ICMP_MINLEN + phdr_len;
+ if (options & F_RROUTE)
+ icmp_len += MAX_IPOPTLEN;
+ maxpayload = IP_MAXPACKET - icmp_len;
+ if (datalen > maxpayload)
+ errx(EX_USAGE, "packet size too large: %d > %d", datalen,
+ maxpayload);
+ send_len = icmp_len + datalen;
+ datap = &outpack[ICMP_MINLEN + phdr_len + TIMEVAL_LEN];
+ if (options & F_PINGFILLED) {
+ fill((char *)datap, payload);
+ }
+ if (source) {
+ bzero((char *)&sock_in, sizeof(sock_in));
+ sock_in.sin_family = AF_INET;
+ if (inet_aton(source, &sock_in.sin_addr) != 0) {
+ shostname = source;
+ } else {
+ hp = gethostbyname2(source, AF_INET);
+ if (!hp)
+ errx(EX_NOHOST, "cannot resolve %s: %s",
+ source, hstrerror(h_errno));
+
+ sock_in.sin_len = sizeof sock_in;
+ if ((unsigned)hp->h_length > sizeof(sock_in.sin_addr) ||
+ hp->h_length < 0)
+ errx(1, "gethostbyname2: illegal address");
+ memcpy(&sock_in.sin_addr, hp->h_addr_list[0],
+ sizeof(sock_in.sin_addr));
+ (void)strncpy(snamebuf, hp->h_name,
+ sizeof(snamebuf) - 1);
+ snamebuf[sizeof(snamebuf) - 1] = '\0';
+ shostname = snamebuf;
+ }
+ if (bind(s, (struct sockaddr *)&sock_in, sizeof sock_in) == -1)
+ err(1, "bind");
+ }
+
+ bzero(&whereto, sizeof(whereto));
+ to = &whereto;
+ to->sin_family = AF_INET;
+ to->sin_len = sizeof *to;
+ if (inet_aton(target, &to->sin_addr) != 0) {
+ hostname = target;
+ } else {
+ hp = gethostbyname2(target, AF_INET);
+ if (!hp)
+ errx(EX_NOHOST, "cannot resolve %s: %s",
+ target, hstrerror(h_errno));
+
+ if ((unsigned)hp->h_length > sizeof(to->sin_addr))
+ errx(1, "gethostbyname2 returned an illegal address");
+ memcpy(&to->sin_addr, hp->h_addr_list[0], sizeof to->sin_addr);
+ (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
+ hnamebuf[sizeof(hnamebuf) - 1] = '\0';
+ hostname = hnamebuf;
+ }
+
+ if (options & F_FLOOD && options & F_INTERVAL)
+ errx(EX_USAGE, "-f and -i: incompatible options");
+
+ if (options & F_FLOOD && IN_MULTICAST(ntohl(to->sin_addr.s_addr)))
+ errx(EX_USAGE,
+ "-f flag cannot be used with multicast destination");
+ if (options & (F_MIF | F_NOLOOP | F_MTTL)
+ && !IN_MULTICAST(ntohl(to->sin_addr.s_addr)))
+ errx(EX_USAGE,
+ "-I, -L, -T flags cannot be used with unicast destination");
+
+ if (datalen >= TIMEVAL_LEN) /* can we time transfer */
+ timing = 1;
+
+ if (!(options & F_PINGFILLED))
+ for (i = TIMEVAL_LEN; i < datalen; ++i)
+ *datap++ = i;
+
+ ident = getpid() & 0xFFFF;
+
+ if (s < 0) {
+ errno = sockerrno;
+ err(EX_OSERR, "socket");
+ }
+ hold = 1;
+ if (options & F_SO_DEBUG)
+ (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold,
+ sizeof(hold));
+ if (options & F_SO_DONTROUTE)
+ (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold,
+ sizeof(hold));
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ if (options & F_POLICY) {
+ char *buf;
+ if (policy_in != NULL) {
+ buf = ipsec_set_policy(policy_in, strlen(policy_in));
+ if (buf == NULL)
+ errx(EX_CONFIG, "%s", ipsec_strerror());
+ if (setsockopt(s, IPPROTO_IP, IP_IPSEC_POLICY,
+ buf, ipsec_get_policylen(buf)) < 0)
+ err(EX_CONFIG,
+ "ipsec policy cannot be configured");
+ free(buf);
+ }
+
+ if (policy_out != NULL) {
+ buf = ipsec_set_policy(policy_out, strlen(policy_out));
+ if (buf == NULL)
+ errx(EX_CONFIG, "%s", ipsec_strerror());
+ if (setsockopt(s, IPPROTO_IP, IP_IPSEC_POLICY,
+ buf, ipsec_get_policylen(buf)) < 0)
+ err(EX_CONFIG,
+ "ipsec policy cannot be configured");
+ free(buf);
+ }
+ }
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif /*IPSEC*/
+
+ if (options & F_HDRINCL) {
+ ip = (struct ip*)outpackhdr;
+ if (!(options & (F_TTL | F_MTTL))) {
+ mib[0] = CTL_NET;
+ mib[1] = PF_INET;
+ mib[2] = IPPROTO_IP;
+ mib[3] = IPCTL_DEFTTL;
+ sz = sizeof(ttl);
+ if (sysctl(mib, 4, &ttl, &sz, NULL, 0) == -1)
+ err(1, "sysctl(net.inet.ip.ttl)");
+ }
+ setsockopt(s, IPPROTO_IP, IP_HDRINCL, &hold, sizeof(hold));
+ ip->ip_v = IPVERSION;
+ ip->ip_hl = sizeof(struct ip) >> 2;
+ ip->ip_tos = tos;
+ ip->ip_id = 0;
+ ip->ip_off = df ? IP_DF : 0;
+ ip->ip_ttl = ttl;
+ ip->ip_p = IPPROTO_ICMP;
+ ip->ip_src.s_addr = source ? sock_in.sin_addr.s_addr : INADDR_ANY;
+ ip->ip_dst = to->sin_addr;
+ }
+ /* record route option */
+ if (options & F_RROUTE) {
+#ifdef IP_OPTIONS
+ bzero(rspace, sizeof(rspace));
+ rspace[IPOPT_OPTVAL] = IPOPT_RR;
+ rspace[IPOPT_OLEN] = sizeof(rspace) - 1;
+ rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
+ rspace[sizeof(rspace) - 1] = IPOPT_EOL;
+ if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace,
+ sizeof(rspace)) < 0)
+ err(EX_OSERR, "setsockopt IP_OPTIONS");
+#else
+ errx(EX_UNAVAILABLE,
+ "record route not available in this implementation");
+#endif /* IP_OPTIONS */
+ }
+
+ if (options & F_TTL) {
+ if (setsockopt(s, IPPROTO_IP, IP_TTL, &ttl,
+ sizeof(ttl)) < 0) {
+ err(EX_OSERR, "setsockopt IP_TTL");
+ }
+ }
+ if (options & F_NOLOOP) {
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &loop,
+ sizeof(loop)) < 0) {
+ err(EX_OSERR, "setsockopt IP_MULTICAST_LOOP");
+ }
+ }
+ if (options & F_MTTL) {
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &mttl,
+ sizeof(mttl)) < 0) {
+ err(EX_OSERR, "setsockopt IP_MULTICAST_TTL");
+ }
+ }
+ if (options & F_MIF) {
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr,
+ sizeof(ifaddr)) < 0) {
+ err(EX_OSERR, "setsockopt IP_MULTICAST_IF");
+ }
+ }
+#ifdef SO_TIMESTAMP
+ { int on = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)) < 0)
+ err(EX_OSERR, "setsockopt SO_TIMESTAMP");
+ }
+#endif
+ if (sweepmax) {
+ if (sweepmin >= sweepmax)
+ errx(EX_USAGE, "Maximum packet size must be greater than the minimum packet size");
+
+ if (datalen != DEFDATALEN)
+ errx(EX_USAGE, "Packet size and ping sweep are mutually exclusive");
+
+ if (npackets > 0) {
+ snpackets = npackets;
+ npackets = 0;
+ } else
+ snpackets = 1;
+ datalen = sweepmin;
+ send_len = icmp_len + sweepmin;
+ }
+ if (options & F_SWEEP && !sweepmax)
+ errx(EX_USAGE, "Maximum sweep size must be specified");
+
+ /*
+ * When pinging the broadcast address, you can get a lot of answers.
+ * Doing something so evil is useful if you are trying to stress the
+ * ethernet, or just want to fill the arp cache to get some stuff for
+ * /etc/ethers. But beware: RFC 1122 allows hosts to ignore broadcast
+ * or multicast pings if they wish.
+ */
+
+ /*
+ * XXX receive buffer needs undetermined space for mbuf overhead
+ * as well.
+ */
+ hold = IP_MAXPACKET + 128;
+ (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold,
+ sizeof(hold));
+ if (uid == 0)
+ (void)setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&hold,
+ sizeof(hold));
+
+ if (to->sin_family == AF_INET) {
+ (void)printf("PING %s (%s)", hostname,
+ inet_ntoa(to->sin_addr));
+ if (source)
+ (void)printf(" from %s", shostname);
+ if (sweepmax)
+ (void)printf(": (%d ... %d) data bytes\n",
+ sweepmin, sweepmax);
+ else
+ (void)printf(": %d data bytes\n", datalen);
+
+ } else {
+ if (sweepmax)
+ (void)printf("PING %s: (%d ... %d) data bytes\n",
+ hostname, sweepmin, sweepmax);
+ else
+ (void)printf("PING %s: %d data bytes\n", hostname, datalen);
+ }
+
+ /*
+ * Use sigaction() instead of signal() to get unambiguous semantics,
+ * in particular with SA_RESTART not set.
+ */
+
+ sigemptyset(&si_sa.sa_mask);
+ si_sa.sa_flags = 0;
+
+ si_sa.sa_handler = stopit;
+ if (sigaction(SIGINT, &si_sa, 0) == -1) {
+ err(EX_OSERR, "sigaction SIGINT");
+ }
+
+ si_sa.sa_handler = status;
+ if (sigaction(SIGINFO, &si_sa, 0) == -1) {
+ err(EX_OSERR, "sigaction");
+ }
+
+ if (alarmtimeout > 0) {
+ si_sa.sa_handler = stopit;
+ if (sigaction(SIGALRM, &si_sa, 0) == -1)
+ err(EX_OSERR, "sigaction SIGALRM");
+ }
+
+ bzero(&msg, sizeof(msg));
+ msg.msg_name = (caddr_t)&from;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+#ifdef SO_TIMESTAMP
+ msg.msg_control = (caddr_t)ctrl;
+#endif
+ iov.iov_base = packet;
+ iov.iov_len = IP_MAXPACKET;
+
+ if (preload == 0)
+ pinger(); /* send the first ping */
+ else {
+ if (npackets != 0 && preload > npackets)
+ preload = npackets;
+ while (preload--) /* fire off them quickies */
+ pinger();
+ }
+ (void)gettimeofday(&last, NULL);
+
+ if (options & F_FLOOD) {
+ intvl.tv_sec = 0;
+ intvl.tv_usec = 10000;
+ } else {
+ intvl.tv_sec = interval / 1000;
+ intvl.tv_usec = interval % 1000 * 1000;
+ }
+
+ almost_done = 0;
+ while (!finish_up) {
+ struct timeval now, timeout;
+ fd_set rfds;
+ int cc, n;
+
+ check_status();
+ if ((unsigned)s >= FD_SETSIZE)
+ errx(EX_OSERR, "descriptor too large");
+ FD_ZERO(&rfds);
+ FD_SET(s, &rfds);
+ (void)gettimeofday(&now, NULL);
+ timeout.tv_sec = last.tv_sec + intvl.tv_sec - now.tv_sec;
+ timeout.tv_usec = last.tv_usec + intvl.tv_usec - now.tv_usec;
+ while (timeout.tv_usec < 0) {
+ timeout.tv_usec += 1000000;
+ timeout.tv_sec--;
+ }
+ while (timeout.tv_usec >= 1000000) {
+ timeout.tv_usec -= 1000000;
+ timeout.tv_sec++;
+ }
+ if (timeout.tv_sec < 0)
+ timeout.tv_sec = timeout.tv_usec = 0;
+ n = select(s + 1, &rfds, NULL, NULL, &timeout);
+ if (n < 0)
+ continue; /* Must be EINTR. */
+ if (n == 1) {
+ struct timeval *tv = NULL;
+#ifdef SO_TIMESTAMP
+ struct cmsghdr *cmsg = (struct cmsghdr *)&ctrl;
+
+ msg.msg_controllen = sizeof(ctrl);
+#endif
+ msg.msg_namelen = sizeof(from);
+ if ((cc = recvmsg(s, &msg, 0)) < 0) {
+ if (errno == EINTR)
+ continue;
+ warn("recvmsg");
+ continue;
+ }
+#ifdef SO_TIMESTAMP
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_TIMESTAMP &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof *tv)) {
+ /* Copy to avoid alignment problems: */
+ memcpy(&now, CMSG_DATA(cmsg), sizeof(now));
+ tv = &now;
+ }
+#endif
+ if (tv == NULL) {
+ (void)gettimeofday(&now, NULL);
+ tv = &now;
+ }
+ pr_pack((char *)packet, cc, &from, tv);
+ if ((options & F_ONCE && nreceived) ||
+ (npackets && nreceived >= npackets))
+ break;
+ }
+ if (n == 0 || options & F_FLOOD) {
+ if (sweepmax && sntransmitted == snpackets) {
+ for (i = 0; i < sweepincr ; ++i)
+ *datap++ = i;
+ datalen += sweepincr;
+ if (datalen > sweepmax)
+ break;
+ send_len = icmp_len + datalen;
+ sntransmitted = 0;
+ }
+ if (!npackets || ntransmitted < npackets)
+ pinger();
+ else {
+ if (almost_done)
+ break;
+ almost_done = 1;
+ intvl.tv_usec = 0;
+ if (nreceived) {
+ intvl.tv_sec = 2 * tmax / 1000;
+ if (!intvl.tv_sec)
+ intvl.tv_sec = 1;
+ } else
+ intvl.tv_sec = MAXWAIT;
+ }
+ (void)gettimeofday(&last, NULL);
+ if (ntransmitted - nreceived - 1 > nmissedmax) {
+ nmissedmax = ntransmitted - nreceived - 1;
+ if (options & F_MISSED)
+ (void)write(STDOUT_FILENO, &BBELL, 1);
+ }
+ }
+ }
+ finish();
+ /* NOTREACHED */
+ exit(0); /* Make the compiler happy */
+}
+
+/*
+ * stopit --
+ * Set the global bit that causes the main loop to quit.
+ * Do NOT call finish() from here, since finish() does far too much
+ * to be called from a signal handler.
+ */
+void
+stopit(sig)
+ int sig __unused;
+{
+
+ /*
+ * When doing reverse DNS lookups, the finish_up flag might not
+ * be noticed for a while. Just exit if we get a second SIGINT.
+ */
+ if (!(options & F_NUMERIC) && finish_up)
+ _exit(nreceived ? 0 : 2);
+ finish_up = 1;
+}
+
+/*
+ * pinger --
+ * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
+ * will be added on by the kernel. The ID field is our UNIX process ID,
+ * and the sequence number is an ascending integer. The first TIMEVAL_LEN
+ * bytes of the data portion are used to hold a UNIX "timeval" struct in
+ * host byte-order, to compute the round-trip time.
+ */
+static void
+pinger(void)
+{
+ struct timeval now;
+ struct tv32 tv32;
+ struct ip *ip;
+ struct icmp *icp;
+ int cc, i;
+ u_char *packet;
+
+ packet = outpack;
+ icp = (struct icmp *)outpack;
+ icp->icmp_type = icmp_type;
+ icp->icmp_code = 0;
+ icp->icmp_cksum = 0;
+ icp->icmp_seq = htons(ntransmitted);
+ icp->icmp_id = ident; /* ID */
+
+ CLR(ntransmitted % mx_dup_ck);
+
+ if ((options & F_TIME) || timing) {
+ (void)gettimeofday(&now, NULL);
+
+ tv32.tv32_sec = htonl(now.tv_sec);
+ tv32.tv32_usec = htonl(now.tv_usec);
+ if (options & F_TIME)
+ icp->icmp_otime = htonl((now.tv_sec % (24*60*60))
+ * 1000 + now.tv_usec / 1000);
+ if (timing)
+ bcopy((void *)&tv32,
+ (void *)&outpack[ICMP_MINLEN + phdr_len],
+ sizeof(tv32));
+ }
+
+ cc = ICMP_MINLEN + phdr_len + datalen;
+
+ /* compute ICMP checksum here */
+ icp->icmp_cksum = in_cksum((u_short *)icp, cc);
+
+ if (options & F_HDRINCL) {
+ cc += sizeof(struct ip);
+ ip = (struct ip *)outpackhdr;
+ ip->ip_len = cc;
+ ip->ip_sum = in_cksum((u_short *)outpackhdr, cc);
+ packet = outpackhdr;
+ }
+ i = sendto(s, (char *)packet, cc, 0, (struct sockaddr *)&whereto,
+ sizeof(whereto));
+
+ if (i < 0 || i != cc) {
+ if (i < 0) {
+ if (options & F_FLOOD && errno == ENOBUFS) {
+ usleep(FLOOD_BACKOFF);
+ return;
+ }
+ warn("sendto");
+ } else {
+ warn("%s: partial write: %d of %d bytes",
+ hostname, i, cc);
+ }
+ }
+ ntransmitted++;
+ sntransmitted++;
+ if (!(options & F_QUIET) && options & F_FLOOD)
+ (void)write(STDOUT_FILENO, &DOT, 1);
+}
+
+/*
+ * pr_pack --
+ * Print out the packet, if it came from us. This logic is necessary
+ * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
+ * which arrive ('tis only fair). This permits multiple copies of this
+ * program to be run without having intermingled output (or statistics!).
+ */
+static void
+pr_pack(buf, cc, from, tv)
+ char *buf;
+ int cc;
+ struct sockaddr_in *from;
+ struct timeval *tv;
+{
+ struct in_addr ina;
+ u_char *cp, *dp;
+ struct icmp *icp;
+ struct ip *ip;
+ const void *tp;
+ double triptime;
+ int dupflag, hlen, i, j, recv_len, seq;
+ static int old_rrlen;
+ static char old_rr[MAX_IPOPTLEN];
+
+ /* Check the IP header */
+ ip = (struct ip *)buf;
+ hlen = ip->ip_hl << 2;
+ recv_len = cc;
+ if (cc < hlen + ICMP_MINLEN) {
+ if (options & F_VERBOSE)
+ warn("packet too short (%d bytes) from %s", cc,
+ inet_ntoa(from->sin_addr));
+ return;
+ }
+
+ /* Now the ICMP part */
+ cc -= hlen;
+ icp = (struct icmp *)(buf + hlen);
+ if (icp->icmp_type == icmp_type_rsp) {
+ if (icp->icmp_id != ident)
+ return; /* 'Twas not our ECHO */
+ ++nreceived;
+ triptime = 0.0;
+ if (timing) {
+ struct timeval tv1;
+ struct tv32 tv32;
+#ifndef icmp_data
+ tp = &icp->icmp_ip;
+#else
+ tp = icp->icmp_data;
+#endif
+ tp = (const char *)tp + phdr_len;
+
+ if (cc - ICMP_MINLEN - phdr_len >= sizeof(tv1)) {
+ /* Copy to avoid alignment problems: */
+ memcpy(&tv32, tp, sizeof(tv32));
+ tv1.tv_sec = ntohl(tv32.tv32_sec);
+ tv1.tv_usec = ntohl(tv32.tv32_usec);
+ tvsub(tv, &tv1);
+ triptime = ((double)tv->tv_sec) * 1000.0 +
+ ((double)tv->tv_usec) / 1000.0;
+ tsum += triptime;
+ tsumsq += triptime * triptime;
+ if (triptime < tmin)
+ tmin = triptime;
+ if (triptime > tmax)
+ tmax = triptime;
+ } else
+ timing = 0;
+ }
+
+ seq = ntohs(icp->icmp_seq);
+
+ if (TST(seq % mx_dup_ck)) {
+ ++nrepeats;
+ --nreceived;
+ dupflag = 1;
+ } else {
+ SET(seq % mx_dup_ck);
+ dupflag = 0;
+ }
+
+ if (options & F_QUIET)
+ return;
+
+ if (options & F_FLOOD)
+ (void)write(STDOUT_FILENO, &BSPACE, 1);
+ else {
+ (void)printf("%d bytes from %s: icmp_seq=%u", cc,
+ inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr),
+ seq);
+ (void)printf(" ttl=%d", ip->ip_ttl);
+ if (timing)
+ (void)printf(" time=%.3f ms", triptime);
+ if (dupflag)
+ (void)printf(" (DUP!)");
+ if (options & F_AUDIBLE)
+ (void)write(STDOUT_FILENO, &BBELL, 1);
+ if (options & F_MASK) {
+ /* Just prentend this cast isn't ugly */
+ (void)printf(" mask=%s",
+ pr_addr(*(struct in_addr *)&(icp->icmp_mask)));
+ }
+ if (options & F_TIME) {
+ (void)printf(" tso=%s", pr_ntime(icp->icmp_otime));
+ (void)printf(" tsr=%s", pr_ntime(icp->icmp_rtime));
+ (void)printf(" tst=%s", pr_ntime(icp->icmp_ttime));
+ }
+ if (recv_len != send_len) {
+ (void)printf(
+ "\nwrong total length %d instead of %d",
+ recv_len, send_len);
+ }
+ /* check the data */
+ cp = (u_char*)&icp->icmp_data[phdr_len];
+ dp = &outpack[ICMP_MINLEN + phdr_len];
+ cc -= ICMP_MINLEN + phdr_len;
+ i = 0;
+ if (timing) { /* don't check variable timestamp */
+ cp += TIMEVAL_LEN;
+ dp += TIMEVAL_LEN;
+ cc -= TIMEVAL_LEN;
+ i += TIMEVAL_LEN;
+ }
+ for (; i < datalen && cc > 0; ++i, ++cp, ++dp, --cc) {
+ if (*cp != *dp) {
+ (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x",
+ i, *dp, *cp);
+ (void)printf("\ncp:");
+ cp = (u_char*)&icp->icmp_data[0];
+ for (i = 0; i < datalen; ++i, ++cp) {
+ if ((i % 16) == 8)
+ (void)printf("\n\t");
+ (void)printf("%2x ", *cp);
+ }
+ (void)printf("\ndp:");
+ cp = &outpack[ICMP_MINLEN];
+ for (i = 0; i < datalen; ++i, ++cp) {
+ if ((i % 16) == 8)
+ (void)printf("\n\t");
+ (void)printf("%2x ", *cp);
+ }
+ break;
+ }
+ }
+ }
+ } else {
+ /*
+ * We've got something other than an ECHOREPLY.
+ * See if it's a reply to something that we sent.
+ * We can compare IP destination, protocol,
+ * and ICMP type and ID.
+ *
+ * Only print all the error messages if we are running
+ * as root to avoid leaking information not normally
+ * available to those not running as root.
+ */
+#ifndef icmp_data
+ struct ip *oip = &icp->icmp_ip;
+#else
+ struct ip *oip = (struct ip *)icp->icmp_data;
+#endif
+ struct icmp *oicmp = (struct icmp *)(oip + 1);
+
+ if (((options & F_VERBOSE) && uid == 0) ||
+ (!(options & F_QUIET2) &&
+ (oip->ip_dst.s_addr == whereto.sin_addr.s_addr) &&
+ (oip->ip_p == IPPROTO_ICMP) &&
+ (oicmp->icmp_type == ICMP_ECHO) &&
+ (oicmp->icmp_id == ident))) {
+ (void)printf("%d bytes from %s: ", cc,
+ pr_addr(from->sin_addr));
+ pr_icmph(icp);
+ } else
+ return;
+ }
+
+ /* Display any IP options */
+ cp = (u_char *)buf + sizeof(struct ip);
+
+ for (; hlen > (int)sizeof(struct ip); --hlen, ++cp)
+ switch (*cp) {
+ case IPOPT_EOL:
+ hlen = 0;
+ break;
+ case IPOPT_LSRR:
+ case IPOPT_SSRR:
+ (void)printf(*cp == IPOPT_LSRR ?
+ "\nLSRR: " : "\nSSRR: ");
+ j = cp[IPOPT_OLEN] - IPOPT_MINOFF + 1;
+ hlen -= 2;
+ cp += 2;
+ if (j >= INADDR_LEN &&
+ j <= hlen - (int)sizeof(struct ip)) {
+ for (;;) {
+ bcopy(++cp, &ina.s_addr, INADDR_LEN);
+ if (ina.s_addr == 0)
+ (void)printf("\t0.0.0.0");
+ else
+ (void)printf("\t%s",
+ pr_addr(ina));
+ hlen -= INADDR_LEN;
+ cp += INADDR_LEN - 1;
+ j -= INADDR_LEN;
+ if (j < INADDR_LEN)
+ break;
+ (void)putchar('\n');
+ }
+ } else
+ (void)printf("\t(truncated route)\n");
+ break;
+ case IPOPT_RR:
+ j = cp[IPOPT_OLEN]; /* get length */
+ i = cp[IPOPT_OFFSET]; /* and pointer */
+ hlen -= 2;
+ cp += 2;
+ if (i > j)
+ i = j;
+ i = i - IPOPT_MINOFF + 1;
+ if (i < 0 || i > (hlen - (int)sizeof(struct ip))) {
+ old_rrlen = 0;
+ continue;
+ }
+ if (i == old_rrlen
+ && !bcmp((char *)cp, old_rr, i)
+ && !(options & F_FLOOD)) {
+ (void)printf("\t(same route)");
+ hlen -= i;
+ cp += i;
+ break;
+ }
+ old_rrlen = i;
+ bcopy((char *)cp, old_rr, i);
+ (void)printf("\nRR: ");
+ if (i >= INADDR_LEN &&
+ i <= hlen - (int)sizeof(struct ip)) {
+ for (;;) {
+ bcopy(++cp, &ina.s_addr, INADDR_LEN);
+ if (ina.s_addr == 0)
+ (void)printf("\t0.0.0.0");
+ else
+ (void)printf("\t%s",
+ pr_addr(ina));
+ hlen -= INADDR_LEN;
+ cp += INADDR_LEN - 1;
+ i -= INADDR_LEN;
+ if (i < INADDR_LEN)
+ break;
+ (void)putchar('\n');
+ }
+ } else
+ (void)printf("\t(truncated route)");
+ break;
+ case IPOPT_NOP:
+ (void)printf("\nNOP");
+ break;
+ default:
+ (void)printf("\nunknown option %x", *cp);
+ break;
+ }
+ if (!(options & F_FLOOD)) {
+ (void)putchar('\n');
+ (void)fflush(stdout);
+ }
+}
+
+/*
+ * in_cksum --
+ * Checksum routine for Internet Protocol family headers (C Version)
+ */
+u_short
+in_cksum(addr, len)
+ u_short *addr;
+ int len;
+{
+ int nleft, sum;
+ u_short *w;
+ union {
+ u_short us;
+ u_char uc[2];
+ } last;
+ u_short answer;
+
+ nleft = len;
+ sum = 0;
+ w = addr;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum), we add
+ * sequential 16 bit words to it, and at the end, fold back all the
+ * carry bits from the top 16 bits into the lower 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1) {
+ last.uc[0] = *(u_char *)w;
+ last.uc[1] = 0;
+ sum += last.us;
+ }
+
+ /* add back carry outs from top 16 bits to low 16 bits */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return(answer);
+}
+
+/*
+ * tvsub --
+ * Subtract 2 timeval structs: out = out - in. Out is assumed to
+ * be >= in.
+ */
+static void
+tvsub(out, in)
+ struct timeval *out, *in;
+{
+
+ if ((out->tv_usec -= in->tv_usec) < 0) {
+ --out->tv_sec;
+ out->tv_usec += 1000000;
+ }
+ out->tv_sec -= in->tv_sec;
+}
+
+/*
+ * status --
+ * Print out statistics when SIGINFO is received.
+ */
+
+static void
+status(sig)
+ int sig __unused;
+{
+
+ siginfo_p = 1;
+}
+
+static void
+check_status()
+{
+
+ if (siginfo_p) {
+ siginfo_p = 0;
+ (void)fprintf(stderr, "\r%ld/%ld packets received (%.0f%%)",
+ nreceived, ntransmitted,
+ ntransmitted ? nreceived * 100.0 / ntransmitted : 0.0);
+ if (nreceived && timing)
+ (void)fprintf(stderr, " %.3f min / %.3f avg / %.3f max",
+ tmin, tsum / (nreceived + nrepeats), tmax);
+ (void)fprintf(stderr, "\n");
+ }
+}
+
+/*
+ * finish --
+ * Print out statistics, and give up.
+ */
+static void
+finish()
+{
+
+ (void)signal(SIGINT, SIG_IGN);
+ (void)signal(SIGALRM, SIG_IGN);
+ (void)putchar('\n');
+ (void)fflush(stdout);
+ (void)printf("--- %s ping statistics ---\n", hostname);
+ (void)printf("%ld packets transmitted, ", ntransmitted);
+ (void)printf("%ld packets received, ", nreceived);
+ if (nrepeats)
+ (void)printf("+%ld duplicates, ", nrepeats);
+ if (ntransmitted) {
+ if (nreceived > ntransmitted)
+ (void)printf("-- somebody's printing up packets!");
+ else
+ (void)printf("%d%% packet loss",
+ (int)(((ntransmitted - nreceived) * 100) /
+ ntransmitted));
+ }
+ (void)putchar('\n');
+ if (nreceived && timing) {
+ double n = nreceived + nrepeats;
+ double avg = tsum / n;
+ double vari = tsumsq / n - avg * avg;
+ (void)printf(
+ "round-trip min/avg/max/stddev = %.3f/%.3f/%.3f/%.3f ms\n",
+ tmin, avg, tmax, sqrt(vari));
+ }
+
+ if (nreceived)
+ exit(0);
+ else
+ exit(2);
+}
+
+#ifdef notdef
+static char *ttab[] = {
+ "Echo Reply", /* ip + seq + udata */
+ "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */
+ "Source Quench", /* IP */
+ "Redirect", /* redirect type, gateway, + IP */
+ "Echo",
+ "Time Exceeded", /* transit, frag reassem + IP */
+ "Parameter Problem", /* pointer + IP */
+ "Timestamp", /* id + seq + three timestamps */
+ "Timestamp Reply", /* " */
+ "Info Request", /* id + sq */
+ "Info Reply" /* " */
+};
+#endif
+
+/*
+ * pr_icmph --
+ * Print a descriptive string about an ICMP header.
+ */
+static void
+pr_icmph(icp)
+ struct icmp *icp;
+{
+
+ switch(icp->icmp_type) {
+ case ICMP_ECHOREPLY:
+ (void)printf("Echo Reply\n");
+ /* XXX ID + Seq + Data */
+ break;
+ case ICMP_UNREACH:
+ switch(icp->icmp_code) {
+ case ICMP_UNREACH_NET:
+ (void)printf("Destination Net Unreachable\n");
+ break;
+ case ICMP_UNREACH_HOST:
+ (void)printf("Destination Host Unreachable\n");
+ break;
+ case ICMP_UNREACH_PROTOCOL:
+ (void)printf("Destination Protocol Unreachable\n");
+ break;
+ case ICMP_UNREACH_PORT:
+ (void)printf("Destination Port Unreachable\n");
+ break;
+ case ICMP_UNREACH_NEEDFRAG:
+ (void)printf("frag needed and DF set (MTU %d)\n",
+ ntohs(icp->icmp_nextmtu));
+ break;
+ case ICMP_UNREACH_SRCFAIL:
+ (void)printf("Source Route Failed\n");
+ break;
+ case ICMP_UNREACH_FILTER_PROHIB:
+ (void)printf("Communication prohibited by filter\n");
+ break;
+ default:
+ (void)printf("Dest Unreachable, Bad Code: %d\n",
+ icp->icmp_code);
+ break;
+ }
+ /* Print returned IP header information */
+#ifndef icmp_data
+ pr_retip(&icp->icmp_ip);
+#else
+ pr_retip((struct ip *)icp->icmp_data);
+#endif
+ break;
+ case ICMP_SOURCEQUENCH:
+ (void)printf("Source Quench\n");
+#ifndef icmp_data
+ pr_retip(&icp->icmp_ip);
+#else
+ pr_retip((struct ip *)icp->icmp_data);
+#endif
+ break;
+ case ICMP_REDIRECT:
+ switch(icp->icmp_code) {
+ case ICMP_REDIRECT_NET:
+ (void)printf("Redirect Network");
+ break;
+ case ICMP_REDIRECT_HOST:
+ (void)printf("Redirect Host");
+ break;
+ case ICMP_REDIRECT_TOSNET:
+ (void)printf("Redirect Type of Service and Network");
+ break;
+ case ICMP_REDIRECT_TOSHOST:
+ (void)printf("Redirect Type of Service and Host");
+ break;
+ default:
+ (void)printf("Redirect, Bad Code: %d", icp->icmp_code);
+ break;
+ }
+ (void)printf("(New addr: %s)\n", inet_ntoa(icp->icmp_gwaddr));
+#ifndef icmp_data
+ pr_retip(&icp->icmp_ip);
+#else
+ pr_retip((struct ip *)icp->icmp_data);
+#endif
+ break;
+ case ICMP_ECHO:
+ (void)printf("Echo Request\n");
+ /* XXX ID + Seq + Data */
+ break;
+ case ICMP_TIMXCEED:
+ switch(icp->icmp_code) {
+ case ICMP_TIMXCEED_INTRANS:
+ (void)printf("Time to live exceeded\n");
+ break;
+ case ICMP_TIMXCEED_REASS:
+ (void)printf("Frag reassembly time exceeded\n");
+ break;
+ default:
+ (void)printf("Time exceeded, Bad Code: %d\n",
+ icp->icmp_code);
+ break;
+ }
+#ifndef icmp_data
+ pr_retip(&icp->icmp_ip);
+#else
+ pr_retip((struct ip *)icp->icmp_data);
+#endif
+ break;
+ case ICMP_PARAMPROB:
+ (void)printf("Parameter problem: pointer = 0x%02x\n",
+ icp->icmp_hun.ih_pptr);
+#ifndef icmp_data
+ pr_retip(&icp->icmp_ip);
+#else
+ pr_retip((struct ip *)icp->icmp_data);
+#endif
+ break;
+ case ICMP_TSTAMP:
+ (void)printf("Timestamp\n");
+ /* XXX ID + Seq + 3 timestamps */
+ break;
+ case ICMP_TSTAMPREPLY:
+ (void)printf("Timestamp Reply\n");
+ /* XXX ID + Seq + 3 timestamps */
+ break;
+ case ICMP_IREQ:
+ (void)printf("Information Request\n");
+ /* XXX ID + Seq */
+ break;
+ case ICMP_IREQREPLY:
+ (void)printf("Information Reply\n");
+ /* XXX ID + Seq */
+ break;
+ case ICMP_MASKREQ:
+ (void)printf("Address Mask Request\n");
+ break;
+ case ICMP_MASKREPLY:
+ (void)printf("Address Mask Reply\n");
+ break;
+ case ICMP_ROUTERADVERT:
+ (void)printf("Router Advertisement\n");
+ break;
+ case ICMP_ROUTERSOLICIT:
+ (void)printf("Router Solicitation\n");
+ break;
+ default:
+ (void)printf("Bad ICMP type: %d\n", icp->icmp_type);
+ }
+}
+
+/*
+ * pr_iph --
+ * Print an IP header with options.
+ */
+static void
+pr_iph(ip)
+ struct ip *ip;
+{
+ u_char *cp;
+ int hlen;
+
+ hlen = ip->ip_hl << 2;
+ cp = (u_char *)ip + 20; /* point to options */
+
+ (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst\n");
+ (void)printf(" %1x %1x %02x %04x %04x",
+ ip->ip_v, ip->ip_hl, ip->ip_tos, ntohs(ip->ip_len),
+ ntohs(ip->ip_id));
+ (void)printf(" %1lx %04lx",
+ (u_long) (ntohl(ip->ip_off) & 0xe000) >> 13,
+ (u_long) ntohl(ip->ip_off) & 0x1fff);
+ (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p,
+ ntohs(ip->ip_sum));
+ (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr));
+ (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr));
+ /* dump any option bytes */
+ while (hlen-- > 20) {
+ (void)printf("%02x", *cp++);
+ }
+ (void)putchar('\n');
+}
+
+/*
+ * pr_addr --
+ * Return an ascii host address as a dotted quad and optionally with
+ * a hostname.
+ */
+static char *
+pr_addr(ina)
+ struct in_addr ina;
+{
+ struct hostent *hp;
+ static char buf[16 + 3 + MAXHOSTNAMELEN];
+
+ if ((options & F_NUMERIC) ||
+ !(hp = gethostbyaddr((char *)&ina, 4, AF_INET)))
+ return inet_ntoa(ina);
+ else
+ (void)snprintf(buf, sizeof(buf), "%s (%s)", hp->h_name,
+ inet_ntoa(ina));
+ return(buf);
+}
+
+/*
+ * pr_retip --
+ * Dump some info on a returned (via ICMP) IP packet.
+ */
+static void
+pr_retip(ip)
+ struct ip *ip;
+{
+ u_char *cp;
+ int hlen;
+
+ pr_iph(ip);
+ hlen = ip->ip_hl << 2;
+ cp = (u_char *)ip + hlen;
+
+ if (ip->ip_p == 6)
+ (void)printf("TCP: from port %u, to port %u (decimal)\n",
+ (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
+ else if (ip->ip_p == 17)
+ (void)printf("UDP: from port %u, to port %u (decimal)\n",
+ (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
+}
+
+static char *
+pr_ntime (n_time timestamp)
+{
+ static char buf[10];
+ int hour, min, sec;
+
+ sec = ntohl(timestamp) / 1000;
+ hour = sec / 60 / 60;
+ min = (sec % (60 * 60)) / 60;
+ sec = (sec % (60 * 60)) % 60;
+
+ (void)snprintf(buf, sizeof(buf), "%02d:%02d:%02d", hour, min, sec);
+
+ return (buf);
+}
+
+static void
+fill(bp, patp)
+ char *bp, *patp;
+{
+ char *cp;
+ int pat[16];
+ u_int ii, jj, kk;
+
+ for (cp = patp; *cp; cp++) {
+ if (!isxdigit(*cp))
+ errx(EX_USAGE,
+ "patterns must be specified as hex digits");
+
+ }
+ ii = sscanf(patp,
+ "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
+ &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6],
+ &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12],
+ &pat[13], &pat[14], &pat[15]);
+
+ if (ii > 0)
+ for (kk = 0; kk <= maxpayload - (TIMEVAL_LEN + ii); kk += ii)
+ for (jj = 0; jj < ii; ++jj)
+ bp[jj + kk] = pat[jj];
+ if (!(options & F_QUIET)) {
+ (void)printf("PATTERN: 0x");
+ for (jj = 0; jj < ii; ++jj)
+ (void)printf("%02x", bp[jj] & 0xFF);
+ (void)printf("\n");
+ }
+}
+
+#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+#define SECOPT " [-P policy]"
+#else
+#define SECOPT ""
+#endif
+static void
+usage()
+{
+
+ (void)fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+"usage: ping [-AaDdfnoQqRrv] [-c count] [-G sweepmaxsize] [-g sweepminsize]",
+" [-h sweepincrsize] [-i wait] [-l preload] [-M mask | time] [-m ttl]",
+" " SECOPT " [-p pattern] [-S src_addr] [-s packetsize] [-t timeout]",
+" [-z tos] host",
+" ping [-AaDdfLnoQqRrv] [-c count] [-I iface] [-i wait] [-l preload]",
+" [-M mask | time] [-m ttl]" SECOPT " [-p pattern] [-S src_addr]",
+" [-s packetsize] [-T ttl] [-t timeout] [-z tos] mcast-group");
+ exit(EX_USAGE);
+}
diff --git a/sbin/ping6/Makefile b/sbin/ping6/Makefile
new file mode 100644
index 0000000..2d0cad4
--- /dev/null
+++ b/sbin/ping6/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+PROG= ping6
+MAN= ping6.8
+
+CFLAGS+=-DINET6 -DIPSEC -DKAME_SCOPEID -DUSE_RFC2292BIS \
+ -DHAVE_POLL_H -DHAVE_ARC4RANDOM
+WARNS?= 2
+
+BINOWN= root
+BINMODE=4555
+
+LDADD= -lipsec -lm -lmd
+DPADD= ${LIBIPSEC} ${LIBM} ${LIBMD}
+
+.include <bsd.prog.mk>
diff --git a/sbin/ping6/ping6.8 b/sbin/ping6/ping6.8
new file mode 100644
index 0000000..89a8168
--- /dev/null
+++ b/sbin/ping6/ping6.8
@@ -0,0 +1,510 @@
+.\" $KAME: ping6.8,v 1.58 2003/06/20 12:00:22 itojun Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE 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.
+.\" 3. Neither the name of the project 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 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 17, 1998
+.Dt PING6 8
+.Os
+.Sh NAME
+.Nm ping6
+.Nd send
+.Tn ICMPv6 ECHO_REQUEST
+packets to network hosts
+.Sh SYNOPSIS
+.Nm
+.\" without ipsec, or new ipsec
+.Op Fl dfHmnNqtvwW
+.\" old ipsec
+.\" .Op Fl AdEfmnNqRtvwW
+.Bk -words
+.Op Fl a Ar addrtype
+.Ek
+.Bk -words
+.Op Fl b Ar bufsiz
+.Ek
+.Bk -words
+.Op Fl c Ar count
+.Ek
+.Bk -words
+.Op Fl g Ar gateway
+.Ek
+.Bk -words
+.Op Fl h Ar hoplimit
+.Ek
+.Bk -words
+.Op Fl I Ar interface
+.Ek
+.Bk -words
+.Op Fl i Ar wait
+.Ek
+.Bk -words
+.Op Fl l Ar preload
+.Ek
+.Bk -words
+.\" new ipsec
+.Op Fl P Ar policy
+.Ek
+.Bk -words
+.Op Fl p Ar pattern
+.Ek
+.Bk -words
+.Op Fl S Ar sourceaddr
+.Ek
+.Bk -words
+.Op Fl s Ar packetsize
+.Ek
+.Bk -words
+.Op Ar hops ...
+.Ek
+.Bk -words
+.Ar host
+.Ek
+.Sh DESCRIPTION
+The
+.Nm
+utility uses the
+.Tn ICMPv6
+protocol's mandatory
+.Tn ICMP6_ECHO_REQUEST
+datagram to elicit an
+.Tn ICMP6_ECHO_REPLY
+from a host or gateway.
+.Tn ICMP6_ECHO_REQUEST
+datagrams (``pings'') have an IPv6 header,
+and
+.Tn ICMPv6
+header formatted as documented in RFC2463.
+The options are as follows:
+.Bl -tag -width Ds
+.\" old ipsec
+.\" .It Fl A
+.\" Enables transport-mode IPsec authentication header
+.\" (experimental).
+.It Fl a Ar addrtype
+Generate ICMPv6 Node Information Node Addresses query, rather than echo-request.
+.Ar addrtype
+must be a string constructed of the following characters.
+.Bl -tag -width Ds -compact
+.It Ic a
+requests unicast addresses from all of the responder's interfaces.
+If the character is omitted,
+only those addresses which belong to the interface which has the
+responder's address are requests.
+.It Ic c
+requests responder's IPv4-compatible and IPv4-mapped addresses.
+.It Ic g
+requests responder's global-scope addresses.
+.It Ic s
+requests responder's site-local addresses.
+.It Ic l
+requests responder's link-local addresses.
+.It Ic A
+requests responder's anycast addresses.
+Without this character, the responder will return unicast addresses only.
+With this character, the responder will return anycast addresses only.
+Note that the specification does not specify how to get responder's
+anycast addresses.
+This is an experimental option.
+.El
+.It Fl b Ar bufsiz
+Set socket buffer size.
+.It Fl c Ar count
+Stop after sending
+(and receiving)
+.Ar count
+.Tn ECHO_RESPONSE
+packets.
+.It Fl d
+Set the
+.Dv SO_DEBUG
+option on the socket being used.
+.\" .It Fl E
+.\" Enables transport-mode IPsec encapsulated security payload
+.\" (experimental).
+.It Fl f
+Flood ping.
+Outputs packets as fast as they come back or one hundred times per second,
+whichever is more.
+For every
+.Tn ECHO_REQUEST
+sent a period
+.Dq \&.
+is printed, while for every
+.Tn ECHO_REPLY
+received a backspace is printed.
+This provides a rapid display of how many packets are being dropped.
+Only the super-user may use this option.
+.Bf -emphasis
+This can be very hard on a network and should be used with caution.
+.Ef
+.It Fl g Ar gateway
+Specifies to use
+.Ar gateway
+as the next hop to the destination.
+The gateway must be a neighbor of the sending node.
+.It Fl H
+Specifies to try reverse-lookup of IPv6 addresses.
+The
+.Nm
+utility does not try reverse-lookup unless the option is specified.
+.It Fl h Ar hoplimit
+Set the IPv6 hoplimit.
+.It Fl I Ar interface
+Source packets with the given interface address.
+This flag applies if the ping destination is a multicast address,
+or link-local/site-local unicast address.
+.It Fl i Ar wait
+Wait
+.Ar wait
+seconds
+.Em between sending each packet .
+The default is to wait for one second between each packet.
+This option is incompatible with the
+.Fl f
+option.
+.It Fl l Ar preload
+If
+.Ar preload
+is specified,
+.Nm
+sends that many packets as fast as possible before falling into its normal
+mode of behavior.
+Only the super-user may use this option.
+.It Fl m
+By default,
+.Nm
+asks the kernel to fragment packets to fit into the minimum IPv6 MTU.
+The
+.Fl m
+option
+will suppress the behavior in the following two levels:
+when the option is specified once, the behavior will be disabled for
+unicast packets.
+When the option is more than once, it will be disabled for both
+unicast and multicast packets.
+.It Fl n
+Numeric output only.
+No attempt will be made to lookup symbolic names from addresses in the reply.
+.It Fl N
+Probe node information multicast group
+.Pq Li ff02::2:xxxx:xxxx .
+.Ar host
+must be string hostname of the target
+(must not be a numeric IPv6 address).
+Node information multicast group will be computed based on given
+.Ar host ,
+and will be used as the final destination.
+Since node information multicast group is a link-local multicast group,
+outgoing interface needs to be specified by
+.Fl I
+option.
+.It Fl p Ar pattern
+You may specify up to 16
+.Dq pad
+bytes to fill out the packet you send.
+This is useful for diagnosing data-dependent problems in a network.
+For example,
+.Dq Li \-p ff
+will cause the sent packet to be filled with all
+ones.
+.\" new ipsec
+.It Fl P Ar policy
+.Ar policy
+specifies IPsec policy to be used for the probe.
+.It Fl q
+Quiet output.
+Nothing is displayed except the summary lines at startup time and
+when finished.
+.It Fl S Ar sourceaddr
+Specifies the source address of request packets.
+The source address must be one of the unicast addresses of the sending node,
+and must be numeric.
+.It Fl s Ar packetsize
+Specifies the number of data bytes to be sent.
+The default is 56, which translates into 64
+.Tn ICMP
+data bytes when combined
+with the 8 bytes of
+.Tn ICMP
+header data.
+You may need to specify
+.Fl b
+as well to extend socket buffer size.
+.It Fl t
+Generate ICMPv6 Node Information supported query types query,
+rather than echo-request.
+.Fl s
+has no effect if
+.Fl t
+is specified.
+.It Fl v
+Verbose output.
+.Tn ICMP
+packets other than
+.Tn ECHO_RESPONSE
+that are received are listed.
+.It Fl w
+Generate ICMPv6 Node Information DNS Name query, rather than echo-request.
+.Fl s
+has no effect if
+.Fl w
+is specified.
+.It Fl W
+Same as
+.Fl w ,
+but with old packet format based on 03 draft.
+This option is present for backward compatibility.
+.Fl s
+has no effect if
+.Fl w
+is specified.
+.It Ar hops
+IPv6 addresses for intermediate nodes,
+which will be put into type 0 routing header.
+.It Ar host
+IPv6 address of the final destination node.
+.El
+.Pp
+When using
+.Nm
+for fault isolation, it should first be run on the local host, to verify
+that the local network interface is up and running.
+Then, hosts and gateways further and further away should be
+.Dq pinged .
+Round-trip times and packet loss statistics are computed.
+If duplicate packets are received, they are not included in the packet
+loss calculation, although the round trip time of these packets is used
+in calculating the round-trip time statistics.
+When the specified number of packets have been sent
+(and received)
+or if the program is terminated with a
+.Dv SIGINT ,
+a brief summary is displayed, showing the number of packets sent and
+received, and the minimum, mean, maximum, and standard deviation of
+the round-trip times.
+.Pp
+If
+.Nm
+receives a
+.Dv SIGINFO
+(see the
+.Cm status
+argument for
+.Xr stty 1 )
+signal, the current number of packets sent and received, and the
+minimum, mean, maximum, and standard deviation of the round-trip times
+will be written to the standard output in the same format as the
+standard completion message.
+.Pp
+This program is intended for use in network testing, measurement and
+management.
+Because of the load it can impose on the network, it is unwise to use
+.Nm
+during normal operations or from automated scripts.
+.\" .Sh ICMP PACKET DETAILS
+.\" An IP header without options is 20 bytes.
+.\" An
+.\" .Tn ICMP
+.\" .Tn ECHO_REQUEST
+.\" packet contains an additional 8 bytes worth of
+.\" .Tn ICMP
+.\" header followed by an arbitrary amount of data.
+.\" When a
+.\" .Ar packetsize
+.\" is given, this indicated the size of this extra piece of data
+.\" (the default is 56).
+.\" Thus the amount of data received inside of an IP packet of type
+.\" .Tn ICMP
+.\" .Tn ECHO_REPLY
+.\" will always be 8 bytes more than the requested data space
+.\" (the
+.\" .Tn ICMP
+.\" header).
+.\" .Pp
+.\" If the data space is at least eight bytes large,
+.\" .Nm
+.\" uses the first eight bytes of this space to include a timestamp which
+.\" it uses in the computation of round trip times.
+.\" If less than eight bytes of pad are specified, no round trip times are
+.\" given.
+.Sh DUPLICATE AND DAMAGED PACKETS
+The
+.Nm
+utility will report duplicate and damaged packets.
+Duplicate packets should never occur when pinging a unicast address,
+and seem to be caused by
+inappropriate link-level retransmissions.
+Duplicates may occur in many situations and are rarely
+(if ever)
+a good sign, although the presence of low levels of duplicates may not
+always be cause for alarm.
+Duplicates are expected when pinging a broadcast or multicast address,
+since they are not really duplicates but replies from different hosts
+to the same request.
+.Pp
+Damaged packets are obviously serious cause for alarm and often
+indicate broken hardware somewhere in the
+.Nm
+packet's path
+(in the network or in the hosts).
+.Sh TRYING DIFFERENT DATA PATTERNS
+The
+(inter)network
+layer should never treat packets differently depending on the data
+contained in the data portion.
+Unfortunately, data-dependent problems have been known to sneak into
+networks and remain undetected for long periods of time.
+In many cases the particular pattern that will have problems is something
+that does not have sufficient
+.Dq transitions ,
+such as all ones or all zeros, or a pattern right at the edge, such as
+almost all zeros.
+It is not
+necessarily enough to specify a data pattern of all zeros (for example)
+on the command line because the pattern that is of interest is
+at the data link level, and the relationship between what you type and
+what the controllers transmit can be complicated.
+.Pp
+This means that if you have a data-dependent problem you will probably
+have to do a lot of testing to find it.
+If you are lucky, you may manage to find a file that either
+cannot
+be sent across your network or that takes much longer to transfer than
+other similar length files.
+You can then examine this file for repeated patterns that you can test
+using the
+.Fl p
+option of
+.Nm .
+.Sh EXIT STATUS
+The
+.Nm
+utility returns 0 on success (the host is alive),
+and non-zero if the arguments are incorrect or the host is not responding.
+.Sh EXAMPLES
+Normally,
+.Nm
+works just like
+.Xr ping 8
+would work; the following will send ICMPv6 echo request to
+.Li dst.foo.com .
+.Bd -literal -offset indent
+ping6 -n dst.foo.com
+.Ed
+.Pp
+The following will probe hostnames for all nodes on the network link attached to
+.Li wi0
+interface.
+The address
+.Li ff02::1
+is named the link-local all-node multicast address, and the packet would
+reach every node on the network link.
+.Bd -literal -offset indent
+ping6 -w ff02::1%wi0
+.Ed
+.Pp
+The following will probe addresses assigned to the destination node,
+.Li dst.foo.com .
+.Bd -literal -offset indent
+ping6 -a agl dst.foo.com
+.Ed
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr icmp6 4 ,
+.Xr inet6 4 ,
+.Xr ip6 4 ,
+.Xr ifconfig 8 ,
+.Xr ping 8 ,
+.Xr routed 8 ,
+.Xr traceroute 8 ,
+.Xr traceroute6 8
+.Rs
+.%A A. Conta
+.%A S. Deering
+.%T "Internet Control Message Protocol (ICMPv6) for the Internet Protocol Version 6 (IPv6) Specification"
+.%N RFC2463
+.%D December 1998
+.Re
+.Rs
+.%A Matt Crawford
+.%T "IPv6 Node Information Queries"
+.%N draft-ietf-ipngwg-icmp-name-lookups-09.txt
+.%D May 2002
+.%O work in progress material
+.Re
+.Sh HISTORY
+The
+.Xr ping 8
+utility appeared in
+.Bx 4.3 .
+The
+.Nm
+utility with IPv6 support first appeared in the WIDE Hydrangea IPv6
+protocol stack kit.
+.Pp
+IPv6 and IPsec support based on the KAME Project
+.Pq Pa http://www.kame.net/
+stack was initially integrated into
+.Fx 4.0 .
+.Sh BUGS
+The
+.Nm
+utility
+is intentionally separate from
+.Xr ping 8 .
+.Pp
+There have been many discussions on why we separate
+.Nm
+and
+.Xr ping 8 .
+Some people argued that it would be more convenient to uniform the
+ping command for both IPv4 and IPv6.
+The followings are an answer to the request.
+.Pp
+From a developer's point of view:
+since the underling raw sockets API is totally different between IPv4
+and IPv6, we would end up having two types of code base.
+There would actually be less benefit to uniform the two commands
+into a single command from the developer's standpoint.
+.Pp
+From an operator's point of view: unlike ordinary network applications
+like remote login tools, we are usually aware of address family when using
+network management tools.
+We do not just want to know the reachability to the host, but want to know the
+reachability to the host via a particular network protocol such as
+IPv6.
+Thus, even if we had a unified
+.Xr ping 8
+command for both IPv4 and IPv6, we would usually type a
+.Fl 6
+or
+.Fl 4
+option (or something like those) to specify the particular address family.
+This essentially means that we have two different commands.
diff --git a/sbin/ping6/ping6.c b/sbin/ping6/ping6.c
new file mode 100644
index 0000000..d9c2f82
--- /dev/null
+++ b/sbin/ping6/ping6.c
@@ -0,0 +1,2727 @@
+/* $KAME: ping6.c,v 1.169 2003/07/25 06:01:47 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE 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.
+ * 3. Neither the name of the project 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 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.
+ */
+
+/* BSDI ping.c,v 2.3 1996/01/21 17:56:50 jch Exp */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * 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 the University of
+ * California, Berkeley and its contributors.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
+ * measure round-trip-delays and packet loss across network paths.
+ *
+ * Author -
+ * Mike Muuss
+ * U. S. Army Ballistic Research Laboratory
+ * December, 1983
+ *
+ * Status -
+ * Public Domain. Distribution Unlimited.
+ * Bugs -
+ * More statistics could always be gathered.
+ * This program has to run SUID to ROOT to access the ICMP socket.
+ */
+/*
+ * NOTE:
+ * USE_SIN6_SCOPE_ID assumes that sin6_scope_id has the same semantics
+ * as IPV6_PKTINFO. Some people object it (sin6_scope_id specifies *link*
+ * while IPV6_PKTINFO specifies *interface*. Link is defined as collection of
+ * network attached to 1 or more interfaces)
+ */
+
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+
+#ifdef IPSEC
+#include <netinet6/ah.h>
+#include <netinet6/ipsec.h>
+#endif
+
+#include <md5.h>
+
+struct tv32 {
+ u_int32_t tv32_sec;
+ u_int32_t tv32_usec;
+};
+
+#define MAXPACKETLEN 131072
+#define IP6LEN 40
+#define ICMP6ECHOLEN 8 /* icmp echo header len excluding time */
+#define ICMP6ECHOTMLEN sizeof(struct tv32)
+#define ICMP6_NIQLEN (ICMP6ECHOLEN + 8)
+/* FQDN case, 64 bits of nonce + 32 bits ttl */
+#define ICMP6_NIRLEN (ICMP6ECHOLEN + 12)
+#define EXTRA 256 /* for AH and various other headers. weird. */
+#define DEFDATALEN ICMP6ECHOTMLEN
+#define MAXDATALEN MAXPACKETLEN - IP6LEN - ICMP6ECHOLEN
+#define NROUTES 9 /* number of record route slots */
+
+#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
+#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
+#define SET(bit) (A(bit) |= B(bit))
+#define CLR(bit) (A(bit) &= (~B(bit)))
+#define TST(bit) (A(bit) & B(bit))
+
+#define F_FLOOD 0x0001
+#define F_INTERVAL 0x0002
+#define F_PINGFILLED 0x0008
+#define F_QUIET 0x0010
+#define F_RROUTE 0x0020
+#define F_SO_DEBUG 0x0040
+#define F_VERBOSE 0x0100
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+#define F_POLICY 0x0400
+#else
+#define F_AUTHHDR 0x0200
+#define F_ENCRYPT 0x0400
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif /*IPSEC*/
+#define F_NODEADDR 0x0800
+#define F_FQDN 0x1000
+#define F_INTERFACE 0x2000
+#define F_SRCADDR 0x4000
+#define F_HOSTNAME 0x10000
+#define F_FQDNOLD 0x20000
+#define F_NIGROUP 0x40000
+#define F_SUPTYPES 0x80000
+#define F_NOMINMTU 0x100000
+#define F_NOUSERDATA (F_NODEADDR | F_FQDN | F_FQDNOLD | F_SUPTYPES)
+u_int options;
+
+#define IN6LEN sizeof(struct in6_addr)
+#define SA6LEN sizeof(struct sockaddr_in6)
+#define DUMMY_PORT 10101
+
+#define SIN6(s) ((struct sockaddr_in6 *)(s))
+
+/*
+ * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum
+ * number of received sequence numbers we can keep track of. Change 128
+ * to 8192 for complete accuracy...
+ */
+#define MAX_DUP_CHK (8 * 8192)
+int mx_dup_ck = MAX_DUP_CHK;
+char rcvd_tbl[MAX_DUP_CHK / 8];
+
+struct addrinfo *res;
+struct sockaddr_in6 dst; /* who to ping6 */
+struct sockaddr_in6 src; /* src addr of this packet */
+socklen_t srclen;
+int datalen = DEFDATALEN;
+int s; /* socket file descriptor */
+u_char outpack[MAXPACKETLEN];
+char BSPACE = '\b'; /* characters written for flood */
+char DOT = '.';
+char *hostname;
+int ident; /* process id to identify our packets */
+u_int8_t nonce[8]; /* nonce field for node information */
+int hoplimit = -1; /* hoplimit */
+int pathmtu = 0; /* path MTU for the destination. 0 = unspec. */
+
+/* counters */
+long npackets; /* max packets to transmit */
+long nreceived; /* # of packets we got back */
+long nrepeats; /* number of duplicates */
+long ntransmitted; /* sequence # for outbound packets = #sent */
+struct timeval interval = {1, 0}; /* interval between packets */
+
+/* timing */
+int timing; /* flag to do timing */
+double tmin = 999999999.0; /* minimum round trip time */
+double tmax = 0.0; /* maximum round trip time */
+double tsum = 0.0; /* sum of all times, for doing average */
+double tsumsq = 0.0; /* sum of all times squared, for std. dev. */
+
+/* for node addresses */
+u_short naflags;
+
+/* for ancillary data(advanced API) */
+struct msghdr smsghdr;
+struct iovec smsgiov;
+char *scmsg = 0;
+
+volatile sig_atomic_t seenalrm;
+volatile sig_atomic_t seenint;
+#ifdef SIGINFO
+volatile sig_atomic_t seeninfo;
+#endif
+
+int main(int, char *[]);
+void fill(char *, char *);
+int get_hoplim(struct msghdr *);
+int get_pathmtu(struct msghdr *);
+struct in6_pktinfo *get_rcvpktinfo(struct msghdr *);
+void onsignal(int);
+void retransmit(void);
+void onint(int);
+size_t pingerlen(void);
+int pinger(void);
+const char *pr_addr(struct sockaddr *, int);
+void pr_icmph(struct icmp6_hdr *, u_char *);
+void pr_iph(struct ip6_hdr *);
+void pr_suptypes(struct icmp6_nodeinfo *, size_t);
+void pr_nodeaddr(struct icmp6_nodeinfo *, int);
+int myechoreply(const struct icmp6_hdr *);
+int mynireply(const struct icmp6_nodeinfo *);
+char *dnsdecode(const u_char **, const u_char *, const u_char *,
+ char *, size_t);
+void pr_pack(u_char *, int, struct msghdr *);
+void pr_exthdrs(struct msghdr *);
+void pr_ip6opt(void *);
+void pr_rthdr(void *);
+int pr_bitrange(u_int32_t, int, int);
+void pr_retip(struct ip6_hdr *, u_char *);
+void summary(void);
+void tvsub(struct timeval *, struct timeval *);
+int setpolicy(int, char *);
+char *nigroup(char *);
+void usage(void);
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct itimerval itimer;
+ struct sockaddr_in6 from;
+#ifndef HAVE_ARC4RANDOM
+ struct timeval seed;
+#endif
+#ifdef HAVE_POLL_H
+ int timeout;
+#else
+ struct timeval timeout, *tv;
+#endif
+ struct addrinfo hints;
+#ifdef HAVE_POLL_H
+ struct pollfd fdmaskp[1];
+#else
+ fd_set *fdmaskp;
+ int fdmasks;
+#endif
+ int cc, i;
+ int ch, hold, packlen, preload, optval, ret_ga;
+ u_char *datap, *packet;
+ char *e, *target, *ifname = NULL, *gateway = NULL;
+ int ip6optlen = 0;
+ struct cmsghdr *scmsgp = NULL;
+#if defined(SO_SNDBUF) && defined(SO_RCVBUF)
+ u_long lsockbufsize;
+ int sockbufsize = 0;
+#endif
+ int usepktinfo = 0;
+ struct in6_pktinfo *pktinfo = NULL;
+#ifdef USE_RFC2292BIS
+ struct ip6_rthdr *rthdr = NULL;
+#endif
+#ifdef IPSEC_POLICY_IPSEC
+ char *policy_in = NULL;
+ char *policy_out = NULL;
+#endif
+ double intval;
+ size_t rthlen;
+#ifdef IPV6_USE_MIN_MTU
+ int mflag = 0;
+#endif
+
+ /* just to be sure */
+ memset(&smsghdr, 0, sizeof(smsghdr));
+ memset(&smsgiov, 0, sizeof(smsgiov));
+
+ preload = 0;
+ datap = &outpack[ICMP6ECHOLEN + ICMP6ECHOTMLEN];
+#ifndef IPSEC
+#define ADDOPTS
+#else
+#ifdef IPSEC_POLICY_IPSEC
+#define ADDOPTS "P:"
+#else
+#define ADDOPTS "AE"
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif
+ while ((ch = getopt(argc, argv,
+ "a:b:c:dfHg:h:I:i:l:mnNp:qS:s:tvwW" ADDOPTS)) != -1) {
+#undef ADDOPTS
+ switch (ch) {
+ case 'a':
+ {
+ char *cp;
+
+ options &= ~F_NOUSERDATA;
+ options |= F_NODEADDR;
+ for (cp = optarg; *cp != '\0'; cp++) {
+ switch (*cp) {
+ case 'a':
+ naflags |= NI_NODEADDR_FLAG_ALL;
+ break;
+ case 'c':
+ case 'C':
+ naflags |= NI_NODEADDR_FLAG_COMPAT;
+ break;
+ case 'l':
+ case 'L':
+ naflags |= NI_NODEADDR_FLAG_LINKLOCAL;
+ break;
+ case 's':
+ case 'S':
+ naflags |= NI_NODEADDR_FLAG_SITELOCAL;
+ break;
+ case 'g':
+ case 'G':
+ naflags |= NI_NODEADDR_FLAG_GLOBAL;
+ break;
+ case 'A': /* experimental. not in the spec */
+#ifdef NI_NODEADDR_FLAG_ANYCAST
+ naflags |= NI_NODEADDR_FLAG_ANYCAST;
+ break;
+#else
+ errx(1,
+"-a A is not supported on the platform");
+ /*NOTREACHED*/
+#endif
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+ break;
+ }
+ case 'b':
+#if defined(SO_SNDBUF) && defined(SO_RCVBUF)
+ errno = 0;
+ e = NULL;
+ lsockbufsize = strtoul(optarg, &e, 10);
+ sockbufsize = lsockbufsize;
+ if (errno || !*optarg || *e ||
+ sockbufsize != lsockbufsize)
+ errx(1, "invalid socket buffer size");
+#else
+ errx(1,
+"-b option ignored: SO_SNDBUF/SO_RCVBUF socket options not supported");
+#endif
+ break;
+ case 'c':
+ npackets = strtol(optarg, &e, 10);
+ if (npackets <= 0 || *optarg == '\0' || *e != '\0')
+ errx(1,
+ "illegal number of packets -- %s", optarg);
+ break;
+ case 'd':
+ options |= F_SO_DEBUG;
+ break;
+ case 'f':
+ if (getuid()) {
+ errno = EPERM;
+ errx(1, "Must be superuser to flood ping");
+ }
+ options |= F_FLOOD;
+ setbuf(stdout, (char *)NULL);
+ break;
+ case 'g':
+ gateway = optarg;
+ break;
+ case 'H':
+ options |= F_HOSTNAME;
+ break;
+ case 'h': /* hoplimit */
+ hoplimit = strtol(optarg, &e, 10);
+ if (*optarg == '\0' || *e != '\0')
+ errx(1, "illegal hoplimit %s", optarg);
+ if (255 < hoplimit || hoplimit < -1)
+ errx(1,
+ "illegal hoplimit -- %s", optarg);
+ break;
+ case 'I':
+ ifname = optarg;
+ options |= F_INTERFACE;
+#ifndef USE_SIN6_SCOPE_ID
+ usepktinfo++;
+#endif
+ break;
+ case 'i': /* wait between sending packets */
+ intval = strtod(optarg, &e);
+ if (*optarg == '\0' || *e != '\0')
+ errx(1, "illegal timing interval %s", optarg);
+ if (intval < 1 && getuid()) {
+ errx(1, "%s: only root may use interval < 1s",
+ strerror(EPERM));
+ }
+ interval.tv_sec = (long)intval;
+ interval.tv_usec =
+ (long)((intval - interval.tv_sec) * 1000000);
+ if (interval.tv_sec < 0)
+ errx(1, "illegal timing interval %s", optarg);
+ /* less than 1/hz does not make sense */
+ if (interval.tv_sec == 0 && interval.tv_usec < 10000) {
+ warnx("too small interval, raised to 0.01");
+ interval.tv_usec = 10000;
+ }
+ options |= F_INTERVAL;
+ break;
+ case 'l':
+ if (getuid()) {
+ errno = EPERM;
+ errx(1, "Must be superuser to preload");
+ }
+ preload = strtol(optarg, &e, 10);
+ if (preload < 0 || *optarg == '\0' || *e != '\0')
+ errx(1, "illegal preload value -- %s", optarg);
+ break;
+ case 'm':
+#ifdef IPV6_USE_MIN_MTU
+ mflag++;
+ break;
+#else
+ errx(1, "-%c is not supported on this platform", ch);
+ /*NOTREACHED*/
+#endif
+ case 'n':
+ options &= ~F_HOSTNAME;
+ break;
+ case 'N':
+ options |= F_NIGROUP;
+ break;
+ case 'p': /* fill buffer with user pattern */
+ options |= F_PINGFILLED;
+ fill((char *)datap, optarg);
+ break;
+ case 'q':
+ options |= F_QUIET;
+ break;
+ case 'S':
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_flags = AI_NUMERICHOST; /* allow hostname? */
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_RAW;
+ hints.ai_protocol = IPPROTO_ICMPV6;
+
+ ret_ga = getaddrinfo(optarg, NULL, &hints, &res);
+ if (ret_ga) {
+ errx(1, "invalid source address: %s",
+ gai_strerror(ret_ga));
+ }
+ /*
+ * res->ai_family must be AF_INET6 and res->ai_addrlen
+ * must be sizeof(src).
+ */
+ memcpy(&src, res->ai_addr, res->ai_addrlen);
+ srclen = res->ai_addrlen;
+ freeaddrinfo(res);
+ options |= F_SRCADDR;
+ break;
+ case 's': /* size of packet to send */
+ datalen = strtol(optarg, &e, 10);
+ if (datalen <= 0 || *optarg == '\0' || *e != '\0')
+ errx(1, "illegal datalen value -- %s", optarg);
+ if (datalen > MAXDATALEN) {
+ errx(1,
+ "datalen value too large, maximum is %d",
+ MAXDATALEN);
+ }
+ break;
+ case 't':
+ options &= ~F_NOUSERDATA;
+ options |= F_SUPTYPES;
+ break;
+ case 'v':
+ options |= F_VERBOSE;
+ break;
+ case 'w':
+ options &= ~F_NOUSERDATA;
+ options |= F_FQDN;
+ break;
+ case 'W':
+ options &= ~F_NOUSERDATA;
+ options |= F_FQDNOLD;
+ break;
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ case 'P':
+ options |= F_POLICY;
+ if (!strncmp("in", optarg, 2)) {
+ if ((policy_in = strdup(optarg)) == NULL)
+ errx(1, "strdup");
+ } else if (!strncmp("out", optarg, 3)) {
+ if ((policy_out = strdup(optarg)) == NULL)
+ errx(1, "strdup");
+ } else
+ errx(1, "invalid security policy");
+ break;
+#else
+ case 'A':
+ options |= F_AUTHHDR;
+ break;
+ case 'E':
+ options |= F_ENCRYPT;
+ break;
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif /*IPSEC*/
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ /*NOTREACHED*/
+ }
+
+ if (argc > 1) {
+#ifdef IPV6_RECVRTHDR /* 2292bis */
+ rthlen = CMSG_SPACE(inet6_rth_space(IPV6_RTHDR_TYPE_0,
+ argc - 1));
+#else /* RFC2292 */
+ rthlen = inet6_rthdr_space(IPV6_RTHDR_TYPE_0, argc - 1);
+#endif
+ if (rthlen == 0) {
+ errx(1, "too many intermediate hops");
+ /*NOTREACHED*/
+ }
+ ip6optlen += rthlen;
+ }
+
+ if (options & F_NIGROUP) {
+ target = nigroup(argv[argc - 1]);
+ if (target == NULL) {
+ usage();
+ /*NOTREACHED*/
+ }
+ } else
+ target = argv[argc - 1];
+
+ /* getaddrinfo */
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_RAW;
+ hints.ai_protocol = IPPROTO_ICMPV6;
+
+ ret_ga = getaddrinfo(target, NULL, &hints, &res);
+ if (ret_ga)
+ errx(1, "%s", gai_strerror(ret_ga));
+ if (res->ai_canonname)
+ hostname = res->ai_canonname;
+ else
+ hostname = target;
+
+ if (!res->ai_addr)
+ errx(1, "getaddrinfo failed");
+
+ (void)memcpy(&dst, res->ai_addr, res->ai_addrlen);
+
+ if ((s = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol)) < 0)
+ err(1, "socket");
+
+ /* set the source address if specified. */
+ if ((options & F_SRCADDR) &&
+ bind(s, (struct sockaddr *)&src, srclen) != 0) {
+ err(1, "bind");
+ }
+
+ /* set the gateway (next hop) if specified */
+ if (gateway) {
+ struct addrinfo ghints, *gres;
+ int error;
+
+ memset(&ghints, 0, sizeof(ghints));
+ ghints.ai_family = AF_INET6;
+ ghints.ai_socktype = SOCK_RAW;
+ ghints.ai_protocol = IPPROTO_ICMPV6;
+
+ error = getaddrinfo(gateway, NULL, &hints, &gres);
+ if (error) {
+ errx(1, "getaddrinfo for the gateway %s: %s",
+ gateway, gai_strerror(error));
+ }
+ if (gres->ai_next && (options & F_VERBOSE))
+ warnx("gateway resolves to multiple addresses");
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_NEXTHOP,
+ gres->ai_addr, gres->ai_addrlen)) {
+ err(1, "setsockopt(IPV6_NEXTHOP)");
+ }
+
+ freeaddrinfo(gres);
+ }
+
+ /*
+ * let the kerel pass extension headers of incoming packets,
+ * for privileged socket options
+ */
+ if ((options & F_VERBOSE) != 0) {
+ int opton = 1;
+
+#ifdef IPV6_RECVHOPOPTS
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPOPTS, &opton,
+ sizeof(opton)))
+ err(1, "setsockopt(IPV6_RECVHOPOPTS)");
+#else /* old adv. API */
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_HOPOPTS, &opton,
+ sizeof(opton)))
+ err(1, "setsockopt(IPV6_HOPOPTS)");
+#endif
+#ifdef IPV6_RECVDSTOPTS
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVDSTOPTS, &opton,
+ sizeof(opton)))
+ err(1, "setsockopt(IPV6_RECVDSTOPTS)");
+#else /* old adv. API */
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_DSTOPTS, &opton,
+ sizeof(opton)))
+ err(1, "setsockopt(IPV6_DSTOPTS)");
+#endif
+#ifdef IPV6_RECVRTHDRDSTOPTS
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDRDSTOPTS, &opton,
+ sizeof(opton)))
+ err(1, "setsockopt(IPV6_RECVRTHDRDSTOPTS)");
+#endif
+ }
+
+ /* revoke root privilege */
+ seteuid(getuid());
+ setuid(getuid());
+
+ if ((options & F_FLOOD) && (options & F_INTERVAL))
+ errx(1, "-f and -i incompatible options");
+
+ if ((options & F_NOUSERDATA) == 0) {
+ if (datalen >= sizeof(struct tv32)) {
+ /* we can time transfer */
+ timing = 1;
+ } else
+ timing = 0;
+ /* in F_VERBOSE case, we may get non-echoreply packets*/
+ if (options & F_VERBOSE)
+ packlen = 2048 + IP6LEN + ICMP6ECHOLEN + EXTRA;
+ else
+ packlen = datalen + IP6LEN + ICMP6ECHOLEN + EXTRA;
+ } else {
+ /* suppress timing for node information query */
+ timing = 0;
+ datalen = 2048;
+ packlen = 2048 + IP6LEN + ICMP6ECHOLEN + EXTRA;
+ }
+
+ if (!(packet = (u_char *)malloc((u_int)packlen)))
+ err(1, "Unable to allocate packet");
+ if (!(options & F_PINGFILLED))
+ for (i = ICMP6ECHOLEN; i < packlen; ++i)
+ *datap++ = i;
+
+ ident = getpid() & 0xFFFF;
+#ifndef HAVE_ARC4RANDOM
+ gettimeofday(&seed, NULL);
+ srand((unsigned int)(seed.tv_sec ^ seed.tv_usec ^ (long)ident));
+ memset(nonce, 0, sizeof(nonce));
+ for (i = 0; i < sizeof(nonce); i += sizeof(int))
+ *((int *)&nonce[i]) = rand();
+#else
+ memset(nonce, 0, sizeof(nonce));
+ for (i = 0; i < sizeof(nonce); i += sizeof(u_int32_t))
+ *((u_int32_t *)&nonce[i]) = arc4random();
+#endif
+
+ hold = 1;
+
+ if (options & F_SO_DEBUG)
+ (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold,
+ sizeof(hold));
+ optval = IPV6_DEFHLIM;
+ if (IN6_IS_ADDR_MULTICAST(&dst.sin6_addr))
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ &optval, sizeof(optval)) == -1)
+ err(1, "IPV6_MULTICAST_HOPS");
+#ifdef IPV6_USE_MIN_MTU
+ if (mflag != 1) {
+ optval = mflag > 1 ? 0 : 1;
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU,
+ &optval, sizeof(optval)) == -1)
+ err(1, "setsockopt(IPV6_USE_MIN_MTU)");
+ }
+#ifdef IPV6_RECVPATHMTU
+ else {
+ optval = 1;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPATHMTU,
+ &optval, sizeof(optval)) == -1)
+ err(1, "setsockopt(IPV6_RECVPATHMTU)");
+ }
+#endif /* IPV6_RECVPATHMTU */
+#endif /* IPV6_USE_MIN_MTU */
+
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ if (options & F_POLICY) {
+ if (setpolicy(s, policy_in) < 0)
+ errx(1, "%s", ipsec_strerror());
+ if (setpolicy(s, policy_out) < 0)
+ errx(1, "%s", ipsec_strerror());
+ }
+#else
+ if (options & F_AUTHHDR) {
+ optval = IPSEC_LEVEL_REQUIRE;
+#ifdef IPV6_AUTH_TRANS_LEVEL
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL,
+ &optval, sizeof(optval)) == -1)
+ err(1, "setsockopt(IPV6_AUTH_TRANS_LEVEL)");
+#else /* old def */
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_AUTH_LEVEL,
+ &optval, sizeof(optval)) == -1)
+ err(1, "setsockopt(IPV6_AUTH_LEVEL)");
+#endif
+ }
+ if (options & F_ENCRYPT) {
+ optval = IPSEC_LEVEL_REQUIRE;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL,
+ &optval, sizeof(optval)) == -1)
+ err(1, "setsockopt(IPV6_ESP_TRANS_LEVEL)");
+ }
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif
+
+#ifdef ICMP6_FILTER
+ {
+ struct icmp6_filter filt;
+ if (!(options & F_VERBOSE)) {
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ if ((options & F_FQDN) || (options & F_FQDNOLD) ||
+ (options & F_NODEADDR) || (options & F_SUPTYPES))
+ ICMP6_FILTER_SETPASS(ICMP6_NI_REPLY, &filt);
+ else
+ ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
+ } else {
+ ICMP6_FILTER_SETPASSALL(&filt);
+ }
+ if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) < 0)
+ err(1, "setsockopt(ICMP6_FILTER)");
+ }
+#endif /*ICMP6_FILTER*/
+
+ /* let the kerel pass extension headers of incoming packets */
+ if ((options & F_VERBOSE) != 0) {
+ int opton = 1;
+
+#ifdef IPV6_RECVRTHDR
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDR, &opton,
+ sizeof(opton)))
+ err(1, "setsockopt(IPV6_RECVRTHDR)");
+#else /* old adv. API */
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_RTHDR, &opton,
+ sizeof(opton)))
+ err(1, "setsockopt(IPV6_RTHDR)");
+#endif
+ }
+
+/*
+ optval = 1;
+ if (IN6_IS_ADDR_MULTICAST(&dst.sin6_addr))
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ &optval, sizeof(optval)) == -1)
+ err(1, "IPV6_MULTICAST_LOOP");
+*/
+
+ /* Specify the outgoing interface and/or the source address */
+ if (usepktinfo)
+ ip6optlen += CMSG_SPACE(sizeof(struct in6_pktinfo));
+
+ if (hoplimit != -1)
+ ip6optlen += CMSG_SPACE(sizeof(int));
+
+ /* set IP6 packet options */
+ if (ip6optlen) {
+ if ((scmsg = (char *)malloc(ip6optlen)) == 0)
+ errx(1, "can't allocate enough memory");
+ smsghdr.msg_control = (caddr_t)scmsg;
+ smsghdr.msg_controllen = ip6optlen;
+ scmsgp = (struct cmsghdr *)scmsg;
+ }
+ if (usepktinfo) {
+ pktinfo = (struct in6_pktinfo *)(CMSG_DATA(scmsgp));
+ memset(pktinfo, 0, sizeof(*pktinfo));
+ scmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ scmsgp->cmsg_level = IPPROTO_IPV6;
+ scmsgp->cmsg_type = IPV6_PKTINFO;
+ scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
+ }
+
+ /* set the outgoing interface */
+ if (ifname) {
+#ifndef USE_SIN6_SCOPE_ID
+ /* pktinfo must have already been allocated */
+ if ((pktinfo->ipi6_ifindex = if_nametoindex(ifname)) == 0)
+ errx(1, "%s: invalid interface name", ifname);
+#else
+ if ((dst.sin6_scope_id = if_nametoindex(ifname)) == 0)
+ errx(1, "%s: invalid interface name", ifname);
+#endif
+ }
+ if (hoplimit != -1) {
+ scmsgp->cmsg_len = CMSG_LEN(sizeof(int));
+ scmsgp->cmsg_level = IPPROTO_IPV6;
+ scmsgp->cmsg_type = IPV6_HOPLIMIT;
+ *(int *)(CMSG_DATA(scmsgp)) = hoplimit;
+
+ scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
+ }
+
+ if (argc > 1) { /* some intermediate addrs are specified */
+ int hops, error;
+#ifdef USE_RFC2292BIS
+ int rthdrlen;
+#endif
+
+#ifdef USE_RFC2292BIS
+ rthdrlen = inet6_rth_space(IPV6_RTHDR_TYPE_0, argc - 1);
+ scmsgp->cmsg_len = CMSG_LEN(rthdrlen);
+ scmsgp->cmsg_level = IPPROTO_IPV6;
+ scmsgp->cmsg_type = IPV6_RTHDR;
+ rthdr = (struct ip6_rthdr *)CMSG_DATA(scmsgp);
+ rthdr = inet6_rth_init((void *)rthdr, rthdrlen,
+ IPV6_RTHDR_TYPE_0, argc - 1);
+ if (rthdr == NULL)
+ errx(1, "can't initialize rthdr");
+#else /* old advanced API */
+ if ((scmsgp = (struct cmsghdr *)inet6_rthdr_init(scmsgp,
+ IPV6_RTHDR_TYPE_0)) == 0)
+ errx(1, "can't initialize rthdr");
+#endif /* USE_RFC2292BIS */
+
+ for (hops = 0; hops < argc - 1; hops++) {
+ struct addrinfo *iaip;
+
+ if ((error = getaddrinfo(argv[hops], NULL, &hints,
+ &iaip)))
+ errx(1, "%s", gai_strerror(error));
+ if (SIN6(iaip->ai_addr)->sin6_family != AF_INET6)
+ errx(1,
+ "bad addr family of an intermediate addr");
+
+#ifdef USE_RFC2292BIS
+ if (inet6_rth_add(rthdr,
+ &(SIN6(iaip->ai_addr))->sin6_addr))
+ errx(1, "can't add an intermediate node");
+#else /* old advanced API */
+ if (inet6_rthdr_add(scmsgp,
+ &(SIN6(iaip->ai_addr))->sin6_addr,
+ IPV6_RTHDR_LOOSE))
+ errx(1, "can't add an intermediate node");
+#endif /* USE_RFC2292BIS */
+ freeaddrinfo(iaip);
+ }
+
+#ifndef USE_RFC2292BIS
+ if (inet6_rthdr_lasthop(scmsgp, IPV6_RTHDR_LOOSE))
+ errx(1, "can't set the last flag");
+#endif
+
+ scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
+ }
+
+ if (!(options & F_SRCADDR)) {
+ /*
+ * get the source address. XXX since we revoked the root
+ * privilege, we cannot use a raw socket for this.
+ */
+ int dummy;
+ socklen_t len = sizeof(src);
+
+ if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "UDP socket");
+
+ src.sin6_family = AF_INET6;
+ src.sin6_addr = dst.sin6_addr;
+ src.sin6_port = ntohs(DUMMY_PORT);
+ src.sin6_scope_id = dst.sin6_scope_id;
+
+#ifdef USE_RFC2292BIS
+ if (pktinfo &&
+ setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTINFO,
+ (void *)pktinfo, sizeof(*pktinfo)))
+ err(1, "UDP setsockopt(IPV6_PKTINFO)");
+
+ if (hoplimit != -1 &&
+ setsockopt(dummy, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ (void *)&hoplimit, sizeof(hoplimit)))
+ err(1, "UDP setsockopt(IPV6_UNICAST_HOPS)");
+
+ if (hoplimit != -1 &&
+ setsockopt(dummy, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ (void *)&hoplimit, sizeof(hoplimit)))
+ err(1, "UDP setsockopt(IPV6_MULTICAST_HOPS)");
+
+ if (rthdr &&
+ setsockopt(dummy, IPPROTO_IPV6, IPV6_RTHDR,
+ (void *)rthdr, (rthdr->ip6r_len + 1) << 3))
+ err(1, "UDP setsockopt(IPV6_RTHDR)");
+#else /* old advanced API */
+ if (smsghdr.msg_control &&
+ setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTOPTIONS,
+ (void *)smsghdr.msg_control, smsghdr.msg_controllen))
+ err(1, "UDP setsockopt(IPV6_PKTOPTIONS)");
+#endif
+
+ if (connect(dummy, (struct sockaddr *)&src, len) < 0)
+ err(1, "UDP connect");
+
+ if (getsockname(dummy, (struct sockaddr *)&src, &len) < 0)
+ err(1, "getsockname");
+
+ close(dummy);
+ }
+
+#if defined(SO_SNDBUF) && defined(SO_RCVBUF)
+ if (sockbufsize) {
+ if (datalen > sockbufsize)
+ warnx("you need -b to increase socket buffer size");
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sockbufsize,
+ sizeof(sockbufsize)) < 0)
+ err(1, "setsockopt(SO_SNDBUF)");
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &sockbufsize,
+ sizeof(sockbufsize)) < 0)
+ err(1, "setsockopt(SO_RCVBUF)");
+ }
+ else {
+ if (datalen > 8 * 1024) /*XXX*/
+ warnx("you need -b to increase socket buffer size");
+ /*
+ * When pinging the broadcast address, you can get a lot of
+ * answers. Doing something so evil is useful if you are trying
+ * to stress the ethernet, or just want to fill the arp cache
+ * to get some stuff for /etc/ethers.
+ */
+ hold = 48 * 1024;
+ setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold,
+ sizeof(hold));
+ }
+#endif
+
+ optval = 1;
+#ifndef USE_SIN6_SCOPE_ID
+#ifdef IPV6_RECVPKTINFO
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &optval,
+ sizeof(optval)) < 0)
+ warn("setsockopt(IPV6_RECVPKTINFO)"); /* XXX err? */
+#else /* old adv. API */
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &optval,
+ sizeof(optval)) < 0)
+ warn("setsockopt(IPV6_PKTINFO)"); /* XXX err? */
+#endif
+#endif /* USE_SIN6_SCOPE_ID */
+#ifdef IPV6_RECVHOPLIMIT
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &optval,
+ sizeof(optval)) < 0)
+ warn("setsockopt(IPV6_RECVHOPLIMIT)"); /* XXX err? */
+#else /* old adv. API */
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_HOPLIMIT, &optval,
+ sizeof(optval)) < 0)
+ warn("setsockopt(IPV6_HOPLIMIT)"); /* XXX err? */
+#endif
+
+ printf("PING6(%lu=40+8+%lu bytes) ", (unsigned long)(40 + pingerlen()),
+ (unsigned long)(pingerlen() - 8));
+ printf("%s --> ", pr_addr((struct sockaddr *)&src, sizeof(src)));
+ printf("%s\n", pr_addr((struct sockaddr *)&dst, sizeof(dst)));
+
+ while (preload--) /* Fire off them quickies. */
+ (void)pinger();
+
+ (void)signal(SIGINT, onsignal);
+#ifdef SIGINFO
+ (void)signal(SIGINFO, onsignal);
+#endif
+
+ if ((options & F_FLOOD) == 0) {
+ (void)signal(SIGALRM, onsignal);
+ itimer.it_interval = interval;
+ itimer.it_value = interval;
+ (void)setitimer(ITIMER_REAL, &itimer, NULL);
+ if (ntransmitted == 0)
+ retransmit();
+ }
+
+#ifndef HAVE_POLL_H
+ fdmasks = howmany(s + 1, NFDBITS) * sizeof(fd_mask);
+ if ((fdmaskp = malloc(fdmasks)) == NULL)
+ err(1, "malloc");
+#endif
+
+ seenalrm = seenint = 0;
+#ifdef SIGINFO
+ seeninfo = 0;
+#endif
+
+ for (;;) {
+ struct msghdr m;
+ struct cmsghdr *cm;
+ u_char buf[1024];
+ struct iovec iov[2];
+
+ /* signal handling */
+ if (seenalrm) {
+ retransmit();
+ seenalrm = 0;
+ continue;
+ }
+ if (seenint) {
+ onint(SIGINT);
+ seenint = 0;
+ continue;
+ }
+#ifdef SIGINFO
+ if (seeninfo) {
+ summary();
+ seeninfo = 0;
+ continue;
+ }
+#endif
+
+ if (options & F_FLOOD) {
+ (void)pinger();
+#ifdef HAVE_POLL_H
+ timeout = 10;
+#else
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 10000;
+ tv = &timeout;
+#endif
+ } else {
+#ifdef HAVE_POLL_H
+ timeout = INFTIM;
+#else
+ tv = NULL;
+#endif
+ }
+#ifdef HAVE_POLL_H
+ fdmaskp[0].fd = s;
+ fdmaskp[0].events = POLLIN;
+ cc = poll(fdmaskp, 1, timeout);
+#else
+ memset(fdmaskp, 0, fdmasks);
+ FD_SET(s, fdmaskp);
+ cc = select(s + 1, fdmaskp, NULL, NULL, tv);
+#endif
+ if (cc < 0) {
+ if (errno != EINTR) {
+#ifdef HAVE_POLL_H
+ warn("poll");
+#else
+ warn("select");
+#endif
+ sleep(1);
+ }
+ continue;
+ } else if (cc == 0)
+ continue;
+
+ m.msg_name = (caddr_t)&from;
+ m.msg_namelen = sizeof(from);
+ memset(&iov, 0, sizeof(iov));
+ iov[0].iov_base = (caddr_t)packet;
+ iov[0].iov_len = packlen;
+ m.msg_iov = iov;
+ m.msg_iovlen = 1;
+ cm = (struct cmsghdr *)buf;
+ m.msg_control = (caddr_t)buf;
+ m.msg_controllen = sizeof(buf);
+
+ cc = recvmsg(s, &m, 0);
+ if (cc < 0) {
+ if (errno != EINTR) {
+ warn("recvmsg");
+ sleep(1);
+ }
+ continue;
+ } else if (cc == 0) {
+ int mtu;
+
+ /*
+ * receive control messages only. Process the
+ * exceptions (currently the only possiblity is
+ * a path MTU notification.)
+ */
+ if ((mtu = get_pathmtu(&m)) > 0) {
+ if ((options & F_VERBOSE) != 0) {
+ printf("new path MTU (%d) is "
+ "notified\n", mtu);
+ }
+ }
+ continue;
+ } else {
+ /*
+ * an ICMPv6 message (probably an echoreply) arrived.
+ */
+ pr_pack(packet, cc, &m);
+ }
+ if (npackets && nreceived >= npackets)
+ break;
+ }
+ summary();
+ exit(nreceived == 0);
+}
+
+void
+onsignal(sig)
+ int sig;
+{
+
+ switch (sig) {
+ case SIGALRM:
+ seenalrm++;
+ break;
+ case SIGINT:
+ seenint++;
+ break;
+#ifdef SIGINFO
+ case SIGINFO:
+ seeninfo++;
+ break;
+#endif
+ }
+}
+
+/*
+ * retransmit --
+ * This routine transmits another ping6.
+ */
+void
+retransmit()
+{
+ struct itimerval itimer;
+
+ if (pinger() == 0)
+ return;
+
+ /*
+ * If we're not transmitting any more packets, change the timer
+ * to wait two round-trip times if we've received any packets or
+ * ten seconds if we haven't.
+ */
+#define MAXWAIT 10
+ if (nreceived) {
+ itimer.it_value.tv_sec = 2 * tmax / 1000;
+ if (itimer.it_value.tv_sec == 0)
+ itimer.it_value.tv_sec = 1;
+ } else
+ itimer.it_value.tv_sec = MAXWAIT;
+ itimer.it_interval.tv_sec = 0;
+ itimer.it_interval.tv_usec = 0;
+ itimer.it_value.tv_usec = 0;
+
+ (void)signal(SIGALRM, onint);
+ (void)setitimer(ITIMER_REAL, &itimer, NULL);
+}
+
+/*
+ * pinger --
+ * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
+ * will be added on by the kernel. The ID field is our UNIX process ID,
+ * and the sequence number is an ascending integer. The first 8 bytes
+ * of the data portion are used to hold a UNIX "timeval" struct in VAX
+ * byte-order, to compute the round-trip time.
+ */
+size_t
+pingerlen()
+{
+ size_t l;
+
+ if (options & F_FQDN)
+ l = ICMP6_NIQLEN + sizeof(dst.sin6_addr);
+ else if (options & F_FQDNOLD)
+ l = ICMP6_NIQLEN;
+ else if (options & F_NODEADDR)
+ l = ICMP6_NIQLEN + sizeof(dst.sin6_addr);
+ else if (options & F_SUPTYPES)
+ l = ICMP6_NIQLEN;
+ else
+ l = ICMP6ECHOLEN + datalen;
+
+ return l;
+}
+
+int
+pinger()
+{
+ struct icmp6_hdr *icp;
+ struct iovec iov[2];
+ int i, cc;
+ struct icmp6_nodeinfo *nip;
+ int seq;
+
+ if (npackets && ntransmitted >= npackets)
+ return(-1); /* no more transmission */
+
+ icp = (struct icmp6_hdr *)outpack;
+ nip = (struct icmp6_nodeinfo *)outpack;
+ memset(icp, 0, sizeof(*icp));
+ icp->icmp6_cksum = 0;
+ seq = ntransmitted++;
+ CLR(seq % mx_dup_ck);
+
+ if (options & F_FQDN) {
+ icp->icmp6_type = ICMP6_NI_QUERY;
+ icp->icmp6_code = ICMP6_NI_SUBJ_IPV6;
+ nip->ni_qtype = htons(NI_QTYPE_FQDN);
+ nip->ni_flags = htons(0);
+
+ memcpy(nip->icmp6_ni_nonce, nonce,
+ sizeof(nip->icmp6_ni_nonce));
+ *(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq);
+
+ memcpy(&outpack[ICMP6_NIQLEN], &dst.sin6_addr,
+ sizeof(dst.sin6_addr));
+ cc = ICMP6_NIQLEN + sizeof(dst.sin6_addr);
+ datalen = 0;
+ } else if (options & F_FQDNOLD) {
+ /* packet format in 03 draft - no Subject data on queries */
+ icp->icmp6_type = ICMP6_NI_QUERY;
+ icp->icmp6_code = 0; /* code field is always 0 */
+ nip->ni_qtype = htons(NI_QTYPE_FQDN);
+ nip->ni_flags = htons(0);
+
+ memcpy(nip->icmp6_ni_nonce, nonce,
+ sizeof(nip->icmp6_ni_nonce));
+ *(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq);
+
+ cc = ICMP6_NIQLEN;
+ datalen = 0;
+ } else if (options & F_NODEADDR) {
+ icp->icmp6_type = ICMP6_NI_QUERY;
+ icp->icmp6_code = ICMP6_NI_SUBJ_IPV6;
+ nip->ni_qtype = htons(NI_QTYPE_NODEADDR);
+ nip->ni_flags = naflags;
+
+ memcpy(nip->icmp6_ni_nonce, nonce,
+ sizeof(nip->icmp6_ni_nonce));
+ *(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq);
+
+ memcpy(&outpack[ICMP6_NIQLEN], &dst.sin6_addr,
+ sizeof(dst.sin6_addr));
+ cc = ICMP6_NIQLEN + sizeof(dst.sin6_addr);
+ datalen = 0;
+ } else if (options & F_SUPTYPES) {
+ icp->icmp6_type = ICMP6_NI_QUERY;
+ icp->icmp6_code = ICMP6_NI_SUBJ_FQDN; /*empty*/
+ nip->ni_qtype = htons(NI_QTYPE_SUPTYPES);
+ /* we support compressed bitmap */
+ nip->ni_flags = NI_SUPTYPE_FLAG_COMPRESS;
+
+ memcpy(nip->icmp6_ni_nonce, nonce,
+ sizeof(nip->icmp6_ni_nonce));
+ *(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq);
+ cc = ICMP6_NIQLEN;
+ datalen = 0;
+ } else {
+ icp->icmp6_type = ICMP6_ECHO_REQUEST;
+ icp->icmp6_code = 0;
+ icp->icmp6_id = htons(ident);
+ icp->icmp6_seq = ntohs(seq);
+ if (timing) {
+ struct timeval tv;
+ struct tv32 *tv32;
+ (void)gettimeofday(&tv, NULL);
+ tv32 = (struct tv32 *)&outpack[ICMP6ECHOLEN];
+ tv32->tv32_sec = htonl(tv.tv_sec);
+ tv32->tv32_usec = htonl(tv.tv_usec);
+ }
+ cc = ICMP6ECHOLEN + datalen;
+ }
+
+#ifdef DIAGNOSTIC
+ if (pingerlen() != cc)
+ errx(1, "internal error; length mismatch");
+#endif
+
+ smsghdr.msg_name = (caddr_t)&dst;
+ smsghdr.msg_namelen = sizeof(dst);
+ memset(&iov, 0, sizeof(iov));
+ iov[0].iov_base = (caddr_t)outpack;
+ iov[0].iov_len = cc;
+ smsghdr.msg_iov = iov;
+ smsghdr.msg_iovlen = 1;
+
+ i = sendmsg(s, &smsghdr, 0);
+
+ if (i < 0 || i != cc) {
+ if (i < 0)
+ warn("sendmsg");
+ (void)printf("ping6: wrote %s %d chars, ret=%d\n",
+ hostname, cc, i);
+ }
+ if (!(options & F_QUIET) && options & F_FLOOD)
+ (void)write(STDOUT_FILENO, &DOT, 1);
+
+ return(0);
+}
+
+int
+myechoreply(icp)
+ const struct icmp6_hdr *icp;
+{
+ if (ntohs(icp->icmp6_id) == ident)
+ return 1;
+ else
+ return 0;
+}
+
+int
+mynireply(nip)
+ const struct icmp6_nodeinfo *nip;
+{
+ if (memcmp(nip->icmp6_ni_nonce + sizeof(u_int16_t),
+ nonce + sizeof(u_int16_t),
+ sizeof(nonce) - sizeof(u_int16_t)) == 0)
+ return 1;
+ else
+ return 0;
+}
+
+char *
+dnsdecode(sp, ep, base, buf, bufsiz)
+ const u_char **sp;
+ const u_char *ep;
+ const u_char *base; /*base for compressed name*/
+ char *buf;
+ size_t bufsiz;
+{
+ int i;
+ const u_char *cp;
+ char cresult[MAXDNAME + 1];
+ const u_char *comp;
+ int l;
+
+ cp = *sp;
+ *buf = '\0';
+
+ if (cp >= ep)
+ return NULL;
+ while (cp < ep) {
+ i = *cp;
+ if (i == 0 || cp != *sp) {
+ if (strlcat((char *)buf, ".", bufsiz) >= bufsiz)
+ return NULL; /*result overrun*/
+ }
+ if (i == 0)
+ break;
+ cp++;
+
+ if ((i & 0xc0) == 0xc0 && cp - base > (i & 0x3f)) {
+ /* DNS compression */
+ if (!base)
+ return NULL;
+
+ comp = base + (i & 0x3f);
+ if (dnsdecode(&comp, cp, base, cresult,
+ sizeof(cresult)) == NULL)
+ return NULL;
+ if (strlcat(buf, cresult, bufsiz) >= bufsiz)
+ return NULL; /*result overrun*/
+ break;
+ } else if ((i & 0x3f) == i) {
+ if (i > ep - cp)
+ return NULL; /*source overrun*/
+ while (i-- > 0 && cp < ep) {
+ l = snprintf(cresult, sizeof(cresult),
+ isprint(*cp) ? "%c" : "\\%03o", *cp & 0xff);
+ if (l >= sizeof(cresult) || l < 0)
+ return NULL;
+ if (strlcat(buf, cresult, bufsiz) >= bufsiz)
+ return NULL; /*result overrun*/
+ cp++;
+ }
+ } else
+ return NULL; /*invalid label*/
+ }
+ if (i != 0)
+ return NULL; /*not terminated*/
+ cp++;
+ *sp = cp;
+ return buf;
+}
+
+/*
+ * pr_pack --
+ * Print out the packet, if it came from us. This logic is necessary
+ * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
+ * which arrive ('tis only fair). This permits multiple copies of this
+ * program to be run without having intermingled output (or statistics!).
+ */
+void
+pr_pack(buf, cc, mhdr)
+ u_char *buf;
+ int cc;
+ struct msghdr *mhdr;
+{
+#define safeputc(c) printf((isprint((c)) ? "%c" : "\\%03o"), c)
+ struct icmp6_hdr *icp;
+ struct icmp6_nodeinfo *ni;
+ int i;
+ int hoplim;
+ struct sockaddr *from;
+ int fromlen;
+ u_char *cp = NULL, *dp, *end = buf + cc;
+ struct in6_pktinfo *pktinfo = NULL;
+ struct timeval tv, tp;
+ struct tv32 *tpp;
+ double triptime = 0;
+ int dupflag;
+ size_t off;
+ int oldfqdn;
+ u_int16_t seq;
+ char dnsname[MAXDNAME + 1];
+
+ (void)gettimeofday(&tv, NULL);
+
+ if (!mhdr || !mhdr->msg_name ||
+ mhdr->msg_namelen != sizeof(struct sockaddr_in6) ||
+ ((struct sockaddr *)mhdr->msg_name)->sa_family != AF_INET6) {
+ if (options & F_VERBOSE)
+ warnx("invalid peername");
+ return;
+ }
+ from = (struct sockaddr *)mhdr->msg_name;
+ fromlen = mhdr->msg_namelen;
+ if (cc < sizeof(struct icmp6_hdr)) {
+ if (options & F_VERBOSE)
+ warnx("packet too short (%d bytes) from %s", cc,
+ pr_addr(from, fromlen));
+ return;
+ }
+ icp = (struct icmp6_hdr *)buf;
+ ni = (struct icmp6_nodeinfo *)buf;
+ off = 0;
+
+ if ((hoplim = get_hoplim(mhdr)) == -1) {
+ warnx("failed to get receiving hop limit");
+ return;
+ }
+ if ((pktinfo = get_rcvpktinfo(mhdr)) == NULL) {
+ warnx("failed to get receiving packet information");
+ return;
+ }
+
+ if (icp->icmp6_type == ICMP6_ECHO_REPLY && myechoreply(icp)) {
+ seq = ntohs(icp->icmp6_seq);
+ ++nreceived;
+ if (timing) {
+ tpp = (struct tv32 *)(icp + 1);
+ tp.tv_sec = ntohl(tpp->tv32_sec);
+ tp.tv_usec = ntohl(tpp->tv32_usec);
+ tvsub(&tv, &tp);
+ triptime = ((double)tv.tv_sec) * 1000.0 +
+ ((double)tv.tv_usec) / 1000.0;
+ tsum += triptime;
+ tsumsq += triptime * triptime;
+ if (triptime < tmin)
+ tmin = triptime;
+ if (triptime > tmax)
+ tmax = triptime;
+ }
+
+ if (TST(seq % mx_dup_ck)) {
+ ++nrepeats;
+ --nreceived;
+ dupflag = 1;
+ } else {
+ SET(seq % mx_dup_ck);
+ dupflag = 0;
+ }
+
+ if (options & F_QUIET)
+ return;
+
+ if (options & F_FLOOD)
+ (void)write(STDOUT_FILENO, &BSPACE, 1);
+ else {
+ (void)printf("%d bytes from %s, icmp_seq=%u", cc,
+ pr_addr(from, fromlen), seq);
+ (void)printf(" hlim=%d", hoplim);
+ if ((options & F_VERBOSE) != 0) {
+ struct sockaddr_in6 dstsa;
+
+ memset(&dstsa, 0, sizeof(dstsa));
+ dstsa.sin6_family = AF_INET6;
+ dstsa.sin6_len = sizeof(dstsa);
+ dstsa.sin6_scope_id = pktinfo->ipi6_ifindex;
+ dstsa.sin6_addr = pktinfo->ipi6_addr;
+ (void)printf(" dst=%s",
+ pr_addr((struct sockaddr *)&dstsa,
+ sizeof(dstsa)));
+ }
+ if (timing)
+ (void)printf(" time=%.3f ms", triptime);
+ if (dupflag)
+ (void)printf("(DUP!)");
+ /* check the data */
+ cp = buf + off + ICMP6ECHOLEN + ICMP6ECHOTMLEN;
+ dp = outpack + ICMP6ECHOLEN + ICMP6ECHOTMLEN;
+ for (i = 8; cp < end; ++i, ++cp, ++dp) {
+ if (*cp != *dp) {
+ (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", i, *dp, *cp);
+ break;
+ }
+ }
+ }
+ } else if (icp->icmp6_type == ICMP6_NI_REPLY && mynireply(ni)) {
+ seq = ntohs(*(u_int16_t *)ni->icmp6_ni_nonce);
+ ++nreceived;
+ if (TST(seq % mx_dup_ck)) {
+ ++nrepeats;
+ --nreceived;
+ dupflag = 1;
+ } else {
+ SET(seq % mx_dup_ck);
+ dupflag = 0;
+ }
+
+ if (options & F_QUIET)
+ return;
+
+ (void)printf("%d bytes from %s: ", cc, pr_addr(from, fromlen));
+
+ switch (ntohs(ni->ni_code)) {
+ case ICMP6_NI_SUCCESS:
+ break;
+ case ICMP6_NI_REFUSED:
+ printf("refused, type 0x%x", ntohs(ni->ni_type));
+ goto fqdnend;
+ case ICMP6_NI_UNKNOWN:
+ printf("unknown, type 0x%x", ntohs(ni->ni_type));
+ goto fqdnend;
+ default:
+ printf("unknown code 0x%x, type 0x%x",
+ ntohs(ni->ni_code), ntohs(ni->ni_type));
+ goto fqdnend;
+ }
+
+ switch (ntohs(ni->ni_qtype)) {
+ case NI_QTYPE_NOOP:
+ printf("NodeInfo NOOP");
+ break;
+ case NI_QTYPE_SUPTYPES:
+ pr_suptypes(ni, end - (u_char *)ni);
+ break;
+ case NI_QTYPE_NODEADDR:
+ pr_nodeaddr(ni, end - (u_char *)ni);
+ break;
+ case NI_QTYPE_FQDN:
+ default: /* XXX: for backward compatibility */
+ cp = (u_char *)ni + ICMP6_NIRLEN;
+ if (buf[off + ICMP6_NIRLEN] ==
+ cc - off - ICMP6_NIRLEN - 1)
+ oldfqdn = 1;
+ else
+ oldfqdn = 0;
+ if (oldfqdn) {
+ cp++; /* skip length */
+ while (cp < end) {
+ safeputc(*cp & 0xff);
+ cp++;
+ }
+ } else {
+ i = 0;
+ while (cp < end) {
+ if (dnsdecode((const u_char **)&cp, end,
+ (const u_char *)(ni + 1), dnsname,
+ sizeof(dnsname)) == NULL) {
+ printf("???");
+ break;
+ }
+ /*
+ * name-lookup special handling for
+ * truncated name
+ */
+ if (cp + 1 <= end && !*cp &&
+ strlen(dnsname) > 0) {
+ dnsname[strlen(dnsname) - 1] = '\0';
+ cp++;
+ }
+ printf("%s%s", i > 0 ? "," : "",
+ dnsname);
+ }
+ }
+ if (options & F_VERBOSE) {
+ int32_t ttl;
+ int comma = 0;
+
+ (void)printf(" ("); /*)*/
+
+ switch (ni->ni_code) {
+ case ICMP6_NI_REFUSED:
+ (void)printf("refused");
+ comma++;
+ break;
+ case ICMP6_NI_UNKNOWN:
+ (void)printf("unknown qtype");
+ comma++;
+ break;
+ }
+
+ if ((end - (u_char *)ni) < ICMP6_NIRLEN) {
+ /* case of refusion, unknown */
+ /*(*/
+ putchar(')');
+ goto fqdnend;
+ }
+ ttl = (int32_t)ntohl(*(u_long *)&buf[off+ICMP6ECHOLEN+8]);
+ if (comma)
+ printf(",");
+ if (!(ni->ni_flags & NI_FQDN_FLAG_VALIDTTL)) {
+ (void)printf("TTL=%d:meaningless",
+ (int)ttl);
+ } else {
+ if (ttl < 0) {
+ (void)printf("TTL=%d:invalid",
+ ttl);
+ } else
+ (void)printf("TTL=%d", ttl);
+ }
+ comma++;
+
+ if (oldfqdn) {
+ if (comma)
+ printf(",");
+ printf("03 draft");
+ comma++;
+ } else {
+ cp = (u_char *)ni + ICMP6_NIRLEN;
+ if (cp == end) {
+ if (comma)
+ printf(",");
+ printf("no name");
+ comma++;
+ }
+ }
+
+ if (buf[off + ICMP6_NIRLEN] !=
+ cc - off - ICMP6_NIRLEN - 1 && oldfqdn) {
+ if (comma)
+ printf(",");
+ (void)printf("invalid namelen:%d/%lu",
+ buf[off + ICMP6_NIRLEN],
+ (u_long)cc - off - ICMP6_NIRLEN - 1);
+ comma++;
+ }
+ /*(*/
+ putchar(')');
+ }
+ fqdnend:
+ ;
+ }
+ } else {
+ /* We've got something other than an ECHOREPLY */
+ if (!(options & F_VERBOSE))
+ return;
+ (void)printf("%d bytes from %s: ", cc, pr_addr(from, fromlen));
+ pr_icmph(icp, end);
+ }
+
+ if (!(options & F_FLOOD)) {
+ (void)putchar('\n');
+ if (options & F_VERBOSE)
+ pr_exthdrs(mhdr);
+ (void)fflush(stdout);
+ }
+#undef safeputc
+}
+
+void
+pr_exthdrs(mhdr)
+ struct msghdr *mhdr;
+{
+ struct cmsghdr *cm;
+
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
+ if (cm->cmsg_level != IPPROTO_IPV6)
+ continue;
+
+ switch (cm->cmsg_type) {
+ case IPV6_HOPOPTS:
+ printf(" HbH Options: ");
+ pr_ip6opt(CMSG_DATA(cm));
+ break;
+ case IPV6_DSTOPTS:
+#ifdef IPV6_RTHDRDSTOPTS
+ case IPV6_RTHDRDSTOPTS:
+#endif
+ printf(" Dst Options: ");
+ pr_ip6opt(CMSG_DATA(cm));
+ break;
+ case IPV6_RTHDR:
+ printf(" Routing: ");
+ pr_rthdr(CMSG_DATA(cm));
+ break;
+ }
+ }
+}
+
+#ifdef USE_RFC2292BIS
+void
+pr_ip6opt(void *extbuf)
+{
+ struct ip6_hbh *ext;
+ int currentlen;
+ u_int8_t type;
+ socklen_t extlen, len;
+ void *databuf;
+ size_t offset;
+ u_int16_t value2;
+ u_int32_t value4;
+
+ ext = (struct ip6_hbh *)extbuf;
+ extlen = (ext->ip6h_len + 1) * 8;
+ printf("nxt %u, len %u (%lu bytes)\n", ext->ip6h_nxt,
+ (unsigned int)ext->ip6h_len, (unsigned long)extlen);
+
+ currentlen = 0;
+ while (1) {
+ currentlen = inet6_opt_next(extbuf, extlen, currentlen,
+ &type, &len, &databuf);
+ if (currentlen == -1)
+ break;
+ switch (type) {
+ /*
+ * Note that inet6_opt_next automatically skips any padding
+ * optins.
+ */
+ case IP6OPT_JUMBO:
+ offset = 0;
+ offset = inet6_opt_get_val(databuf, offset,
+ &value4, sizeof(value4));
+ printf(" Jumbo Payload Opt: Length %u\n",
+ (u_int32_t)ntohl(value4));
+ break;
+ case IP6OPT_ROUTER_ALERT:
+ offset = 0;
+ offset = inet6_opt_get_val(databuf, offset,
+ &value2, sizeof(value2));
+ printf(" Router Alert Opt: Type %u\n",
+ ntohs(value2));
+ break;
+ default:
+ printf(" Received Opt %u len %lu\n",
+ type, (unsigned long)len);
+ break;
+ }
+ }
+ return;
+}
+#else /* !USE_RFC2292BIS */
+/* ARGSUSED */
+void
+pr_ip6opt(void *extbuf)
+{
+ putchar('\n');
+ return;
+}
+#endif /* USE_RFC2292BIS */
+
+#ifdef USE_RFC2292BIS
+void
+pr_rthdr(void *extbuf)
+{
+ struct in6_addr *in6;
+ char ntopbuf[INET6_ADDRSTRLEN];
+ struct ip6_rthdr *rh = (struct ip6_rthdr *)extbuf;
+ int i, segments;
+
+ /* print fixed part of the header */
+ printf("nxt %u, len %u (%d bytes), type %u, ", rh->ip6r_nxt,
+ rh->ip6r_len, (rh->ip6r_len + 1) << 3, rh->ip6r_type);
+ if ((segments = inet6_rth_segments(extbuf)) >= 0)
+ printf("%d segments, ", segments);
+ else
+ printf("segments unknown, ");
+ printf("%d left\n", rh->ip6r_segleft);
+
+ for (i = 0; i < segments; i++) {
+ in6 = inet6_rth_getaddr(extbuf, i);
+ if (in6 == NULL)
+ printf(" [%d]<NULL>\n", i);
+ else {
+ if (!inet_ntop(AF_INET6, in6, ntopbuf,
+ sizeof(ntopbuf)))
+ strlcpy(ntopbuf, "?", sizeof(ntopbuf));
+ printf(" [%d]%s\n", i, ntopbuf);
+ }
+ }
+
+ return;
+
+}
+
+#else /* !USE_RFC2292BIS */
+/* ARGSUSED */
+void
+pr_rthdr(void *extbuf)
+{
+ putchar('\n');
+ return;
+}
+#endif /* USE_RFC2292BIS */
+
+int
+pr_bitrange(v, soff, ii)
+ u_int32_t v;
+ int soff;
+ int ii;
+{
+ int off;
+ int i;
+
+ off = 0;
+ while (off < 32) {
+ /* shift till we have 0x01 */
+ if ((v & 0x01) == 0) {
+ if (ii > 1)
+ printf("-%u", soff + off - 1);
+ ii = 0;
+ switch (v & 0x0f) {
+ case 0x00:
+ v >>= 4;
+ off += 4;
+ continue;
+ case 0x08:
+ v >>= 3;
+ off += 3;
+ continue;
+ case 0x04: case 0x0c:
+ v >>= 2;
+ off += 2;
+ continue;
+ default:
+ v >>= 1;
+ off += 1;
+ continue;
+ }
+ }
+
+ /* we have 0x01 with us */
+ for (i = 0; i < 32 - off; i++) {
+ if ((v & (0x01 << i)) == 0)
+ break;
+ }
+ if (!ii)
+ printf(" %u", soff + off);
+ ii += i;
+ v >>= i; off += i;
+ }
+ return ii;
+}
+
+void
+pr_suptypes(ni, nilen)
+ struct icmp6_nodeinfo *ni; /* ni->qtype must be SUPTYPES */
+ size_t nilen;
+{
+ size_t clen;
+ u_int32_t v;
+ const u_char *cp, *end;
+ u_int16_t cur;
+ struct cbit {
+ u_int16_t words; /*32bit count*/
+ u_int16_t skip;
+ } cbit;
+#define MAXQTYPES (1 << 16)
+ size_t off;
+ int b;
+
+ cp = (u_char *)(ni + 1);
+ end = ((u_char *)ni) + nilen;
+ cur = 0;
+ b = 0;
+
+ printf("NodeInfo Supported Qtypes");
+ if (options & F_VERBOSE) {
+ if (ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS)
+ printf(", compressed bitmap");
+ else
+ printf(", raw bitmap");
+ }
+
+ while (cp < end) {
+ clen = (size_t)(end - cp);
+ if ((ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS) == 0) {
+ if (clen == 0 || clen > MAXQTYPES / 8 ||
+ clen % sizeof(v)) {
+ printf("???");
+ return;
+ }
+ } else {
+ if (clen < sizeof(cbit) || clen % sizeof(v))
+ return;
+ memcpy(&cbit, cp, sizeof(cbit));
+ if (sizeof(cbit) + ntohs(cbit.words) * sizeof(v) >
+ clen)
+ return;
+ cp += sizeof(cbit);
+ clen = ntohs(cbit.words) * sizeof(v);
+ if (cur + clen * 8 + (u_long)ntohs(cbit.skip) * 32 >
+ MAXQTYPES)
+ return;
+ }
+
+ for (off = 0; off < clen; off += sizeof(v)) {
+ memcpy(&v, cp + off, sizeof(v));
+ v = (u_int32_t)ntohl(v);
+ b = pr_bitrange(v, (int)(cur + off * 8), b);
+ }
+ /* flush the remaining bits */
+ b = pr_bitrange(0, (int)(cur + off * 8), b);
+
+ cp += clen;
+ cur += clen * 8;
+ if ((ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS) != 0)
+ cur += ntohs(cbit.skip) * 32;
+ }
+}
+
+void
+pr_nodeaddr(ni, nilen)
+ struct icmp6_nodeinfo *ni; /* ni->qtype must be NODEADDR */
+ int nilen;
+{
+ u_char *cp = (u_char *)(ni + 1);
+ char ntop_buf[INET6_ADDRSTRLEN];
+ int withttl = 0;
+
+ nilen -= sizeof(struct icmp6_nodeinfo);
+
+ if (options & F_VERBOSE) {
+ switch (ni->ni_code) {
+ case ICMP6_NI_REFUSED:
+ (void)printf("refused");
+ break;
+ case ICMP6_NI_UNKNOWN:
+ (void)printf("unknown qtype");
+ break;
+ }
+ if (ni->ni_flags & NI_NODEADDR_FLAG_TRUNCATE)
+ (void)printf(" truncated");
+ }
+ putchar('\n');
+ if (nilen <= 0)
+ printf(" no address\n");
+
+ /*
+ * In icmp-name-lookups 05 and later, TTL of each returned address
+ * is contained in the resposne. We try to detect the version
+ * by the length of the data, but note that the detection algorithm
+ * is incomplete. We assume the latest draft by default.
+ */
+ if (nilen % (sizeof(u_int32_t) + sizeof(struct in6_addr)) == 0)
+ withttl = 1;
+ while (nilen > 0) {
+ u_int32_t ttl;
+
+ if (withttl) {
+ /* XXX: alignment? */
+ ttl = (u_int32_t)ntohl(*(u_int32_t *)cp);
+ cp += sizeof(u_int32_t);
+ nilen -= sizeof(u_int32_t);
+ }
+
+ if (inet_ntop(AF_INET6, cp, ntop_buf, sizeof(ntop_buf)) ==
+ NULL)
+ strlcpy(ntop_buf, "?", sizeof(ntop_buf));
+ printf(" %s", ntop_buf);
+ if (withttl) {
+ if (ttl == 0xffffffff) {
+ /*
+ * XXX: can this convention be applied to all
+ * type of TTL (i.e. non-ND TTL)?
+ */
+ printf("(TTL=infty)");
+ }
+ else
+ printf("(TTL=%u)", ttl);
+ }
+ putchar('\n');
+
+ nilen -= sizeof(struct in6_addr);
+ cp += sizeof(struct in6_addr);
+ }
+}
+
+int
+get_hoplim(mhdr)
+ struct msghdr *mhdr;
+{
+ struct cmsghdr *cm;
+
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
+ if (cm->cmsg_len == 0)
+ return(-1);
+
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ return(*(int *)CMSG_DATA(cm));
+ }
+
+ return(-1);
+}
+
+struct in6_pktinfo *
+get_rcvpktinfo(mhdr)
+ struct msghdr *mhdr;
+{
+ struct cmsghdr *cm;
+
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
+ if (cm->cmsg_len == 0)
+ return(NULL);
+
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO &&
+ cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
+ return((struct in6_pktinfo *)CMSG_DATA(cm));
+ }
+
+ return(NULL);
+}
+
+int
+get_pathmtu(mhdr)
+ struct msghdr *mhdr;
+{
+#ifdef IPV6_RECVPATHMTU
+ struct cmsghdr *cm;
+ struct ip6_mtuinfo *mtuctl = NULL;
+
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
+ if (cm->cmsg_len == 0)
+ return(0);
+
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_PATHMTU &&
+ cm->cmsg_len == CMSG_LEN(sizeof(struct ip6_mtuinfo))) {
+ mtuctl = (struct ip6_mtuinfo *)CMSG_DATA(cm);
+
+ /*
+ * If the notified destination is different from
+ * the one we are pinging, just ignore the info.
+ * We check the scope ID only when both notified value
+ * and our own value have non-0 values, because we may
+ * have used the default scope zone ID for sending,
+ * in which case the scope ID value is 0.
+ */
+ if (!IN6_ARE_ADDR_EQUAL(&mtuctl->ip6m_addr.sin6_addr,
+ &dst.sin6_addr) ||
+ (mtuctl->ip6m_addr.sin6_scope_id &&
+ dst.sin6_scope_id &&
+ mtuctl->ip6m_addr.sin6_scope_id !=
+ dst.sin6_scope_id)) {
+ if ((options & F_VERBOSE) != 0) {
+ printf("path MTU for %s is notified. "
+ "(ignored)\n",
+ pr_addr((struct sockaddr *)&mtuctl->ip6m_addr,
+ sizeof(mtuctl->ip6m_addr)));
+ }
+ return(0);
+ }
+
+ /*
+ * Ignore an invalid MTU. XXX: can we just believe
+ * the kernel check?
+ */
+ if (mtuctl->ip6m_mtu < IPV6_MMTU)
+ return(0);
+
+ /* notification for our destination. return the MTU. */
+ return((int)mtuctl->ip6m_mtu);
+ }
+ }
+#endif
+ return(0);
+}
+
+/*
+ * tvsub --
+ * Subtract 2 timeval structs: out = out - in. Out is assumed to
+ * be >= in.
+ */
+void
+tvsub(out, in)
+ struct timeval *out, *in;
+{
+ if ((out->tv_usec -= in->tv_usec) < 0) {
+ --out->tv_sec;
+ out->tv_usec += 1000000;
+ }
+ out->tv_sec -= in->tv_sec;
+}
+
+/*
+ * onint --
+ * SIGINT handler.
+ */
+/* ARGSUSED */
+void
+onint(notused)
+ int notused;
+{
+ summary();
+
+ (void)signal(SIGINT, SIG_DFL);
+ (void)kill(getpid(), SIGINT);
+
+ /* NOTREACHED */
+ exit(1);
+}
+
+/*
+ * summary --
+ * Print out statistics.
+ */
+void
+summary()
+{
+
+ (void)printf("\n--- %s ping6 statistics ---\n", hostname);
+ (void)printf("%ld packets transmitted, ", ntransmitted);
+ (void)printf("%ld packets received, ", nreceived);
+ if (nrepeats)
+ (void)printf("+%ld duplicates, ", nrepeats);
+ if (ntransmitted) {
+ if (nreceived > ntransmitted)
+ (void)printf("-- somebody's duplicating packets!");
+ else
+ (void)printf("%.1f%% packet loss",
+ ((((double)ntransmitted - nreceived) * 100.0) /
+ ntransmitted));
+ }
+ (void)putchar('\n');
+ if (nreceived && timing) {
+ /* Only display average to microseconds */
+ double num = nreceived + nrepeats;
+ double avg = tsum / num;
+ double dev = sqrt(tsumsq / num - avg * avg);
+ (void)printf(
+ "round-trip min/avg/max/std-dev = %.3f/%.3f/%.3f/%.3f ms\n",
+ tmin, avg, tmax, dev);
+ (void)fflush(stdout);
+ }
+ (void)fflush(stdout);
+}
+
+/*subject type*/
+static const char *niqcode[] = {
+ "IPv6 address",
+ "DNS label", /*or empty*/
+ "IPv4 address",
+};
+
+/*result code*/
+static const char *nircode[] = {
+ "Success", "Refused", "Unknown",
+};
+
+
+/*
+ * pr_icmph --
+ * Print a descriptive string about an ICMP header.
+ */
+void
+pr_icmph(icp, end)
+ struct icmp6_hdr *icp;
+ u_char *end;
+{
+ char ntop_buf[INET6_ADDRSTRLEN];
+ struct nd_redirect *red;
+ struct icmp6_nodeinfo *ni;
+ char dnsname[MAXDNAME + 1];
+ const u_char *cp;
+ size_t l;
+
+ switch (icp->icmp6_type) {
+ case ICMP6_DST_UNREACH:
+ switch (icp->icmp6_code) {
+ case ICMP6_DST_UNREACH_NOROUTE:
+ (void)printf("No Route to Destination\n");
+ break;
+ case ICMP6_DST_UNREACH_ADMIN:
+ (void)printf("Destination Administratively "
+ "Unreachable\n");
+ break;
+ case ICMP6_DST_UNREACH_BEYONDSCOPE:
+ (void)printf("Destination Unreachable Beyond Scope\n");
+ break;
+ case ICMP6_DST_UNREACH_ADDR:
+ (void)printf("Destination Host Unreachable\n");
+ break;
+ case ICMP6_DST_UNREACH_NOPORT:
+ (void)printf("Destination Port Unreachable\n");
+ break;
+ default:
+ (void)printf("Destination Unreachable, Bad Code: %d\n",
+ icp->icmp6_code);
+ break;
+ }
+ /* Print returned IP header information */
+ pr_retip((struct ip6_hdr *)(icp + 1), end);
+ break;
+ case ICMP6_PACKET_TOO_BIG:
+ (void)printf("Packet too big mtu = %d\n",
+ (int)ntohl(icp->icmp6_mtu));
+ pr_retip((struct ip6_hdr *)(icp + 1), end);
+ break;
+ case ICMP6_TIME_EXCEEDED:
+ switch (icp->icmp6_code) {
+ case ICMP6_TIME_EXCEED_TRANSIT:
+ (void)printf("Time to live exceeded\n");
+ break;
+ case ICMP6_TIME_EXCEED_REASSEMBLY:
+ (void)printf("Frag reassembly time exceeded\n");
+ break;
+ default:
+ (void)printf("Time exceeded, Bad Code: %d\n",
+ icp->icmp6_code);
+ break;
+ }
+ pr_retip((struct ip6_hdr *)(icp + 1), end);
+ break;
+ case ICMP6_PARAM_PROB:
+ (void)printf("Parameter problem: ");
+ switch (icp->icmp6_code) {
+ case ICMP6_PARAMPROB_HEADER:
+ (void)printf("Erroneous Header ");
+ break;
+ case ICMP6_PARAMPROB_NEXTHEADER:
+ (void)printf("Unknown Nextheader ");
+ break;
+ case ICMP6_PARAMPROB_OPTION:
+ (void)printf("Unrecognized Option ");
+ break;
+ default:
+ (void)printf("Bad code(%d) ", icp->icmp6_code);
+ break;
+ }
+ (void)printf("pointer = 0x%02x\n",
+ (u_int32_t)ntohl(icp->icmp6_pptr));
+ pr_retip((struct ip6_hdr *)(icp + 1), end);
+ break;
+ case ICMP6_ECHO_REQUEST:
+ (void)printf("Echo Request");
+ /* XXX ID + Seq + Data */
+ break;
+ case ICMP6_ECHO_REPLY:
+ (void)printf("Echo Reply");
+ /* XXX ID + Seq + Data */
+ break;
+ case ICMP6_MEMBERSHIP_QUERY:
+ (void)printf("Listener Query");
+ break;
+ case ICMP6_MEMBERSHIP_REPORT:
+ (void)printf("Listener Report");
+ break;
+ case ICMP6_MEMBERSHIP_REDUCTION:
+ (void)printf("Listener Done");
+ break;
+ case ND_ROUTER_SOLICIT:
+ (void)printf("Router Solicitation");
+ break;
+ case ND_ROUTER_ADVERT:
+ (void)printf("Router Advertisement");
+ break;
+ case ND_NEIGHBOR_SOLICIT:
+ (void)printf("Neighbor Solicitation");
+ break;
+ case ND_NEIGHBOR_ADVERT:
+ (void)printf("Neighbor Advertisement");
+ break;
+ case ND_REDIRECT:
+ red = (struct nd_redirect *)icp;
+ (void)printf("Redirect\n");
+ if (!inet_ntop(AF_INET6, &red->nd_rd_dst, ntop_buf,
+ sizeof(ntop_buf)))
+ strlcpy(ntop_buf, "?", sizeof(ntop_buf));
+ (void)printf("Destination: %s", ntop_buf);
+ if (!inet_ntop(AF_INET6, &red->nd_rd_target, ntop_buf,
+ sizeof(ntop_buf)))
+ strlcpy(ntop_buf, "?", sizeof(ntop_buf));
+ (void)printf(" New Target: %s", ntop_buf);
+ break;
+ case ICMP6_NI_QUERY:
+ (void)printf("Node Information Query");
+ /* XXX ID + Seq + Data */
+ ni = (struct icmp6_nodeinfo *)icp;
+ l = end - (u_char *)(ni + 1);
+ printf(", ");
+ switch (ntohs(ni->ni_qtype)) {
+ case NI_QTYPE_NOOP:
+ (void)printf("NOOP");
+ break;
+ case NI_QTYPE_SUPTYPES:
+ (void)printf("Supported qtypes");
+ break;
+ case NI_QTYPE_FQDN:
+ (void)printf("DNS name");
+ break;
+ case NI_QTYPE_NODEADDR:
+ (void)printf("nodeaddr");
+ break;
+ case NI_QTYPE_IPV4ADDR:
+ (void)printf("IPv4 nodeaddr");
+ break;
+ default:
+ (void)printf("unknown qtype");
+ break;
+ }
+ if (options & F_VERBOSE) {
+ switch (ni->ni_code) {
+ case ICMP6_NI_SUBJ_IPV6:
+ if (l == sizeof(struct in6_addr) &&
+ inet_ntop(AF_INET6, ni + 1, ntop_buf,
+ sizeof(ntop_buf)) != NULL) {
+ (void)printf(", subject=%s(%s)",
+ niqcode[ni->ni_code], ntop_buf);
+ } else {
+#if 1
+ /* backward compat to -W */
+ (void)printf(", oldfqdn");
+#else
+ (void)printf(", invalid");
+#endif
+ }
+ break;
+ case ICMP6_NI_SUBJ_FQDN:
+ if (end == (u_char *)(ni + 1)) {
+ (void)printf(", no subject");
+ break;
+ }
+ printf(", subject=%s", niqcode[ni->ni_code]);
+ cp = (const u_char *)(ni + 1);
+ if (dnsdecode(&cp, end, NULL, dnsname,
+ sizeof(dnsname)) != NULL)
+ printf("(%s)", dnsname);
+ else
+ printf("(invalid)");
+ break;
+ case ICMP6_NI_SUBJ_IPV4:
+ if (l == sizeof(struct in_addr) &&
+ inet_ntop(AF_INET, ni + 1, ntop_buf,
+ sizeof(ntop_buf)) != NULL) {
+ (void)printf(", subject=%s(%s)",
+ niqcode[ni->ni_code], ntop_buf);
+ } else
+ (void)printf(", invalid");
+ break;
+ default:
+ (void)printf(", invalid");
+ break;
+ }
+ }
+ break;
+ case ICMP6_NI_REPLY:
+ (void)printf("Node Information Reply");
+ /* XXX ID + Seq + Data */
+ ni = (struct icmp6_nodeinfo *)icp;
+ printf(", ");
+ switch (ntohs(ni->ni_qtype)) {
+ case NI_QTYPE_NOOP:
+ (void)printf("NOOP");
+ break;
+ case NI_QTYPE_SUPTYPES:
+ (void)printf("Supported qtypes");
+ break;
+ case NI_QTYPE_FQDN:
+ (void)printf("DNS name");
+ break;
+ case NI_QTYPE_NODEADDR:
+ (void)printf("nodeaddr");
+ break;
+ case NI_QTYPE_IPV4ADDR:
+ (void)printf("IPv4 nodeaddr");
+ break;
+ default:
+ (void)printf("unknown qtype");
+ break;
+ }
+ if (options & F_VERBOSE) {
+ if (ni->ni_code > sizeof(nircode) / sizeof(nircode[0]))
+ printf(", invalid");
+ else
+ printf(", %s", nircode[ni->ni_code]);
+ }
+ break;
+ default:
+ (void)printf("Bad ICMP type: %d", icp->icmp6_type);
+ }
+}
+
+/*
+ * pr_iph --
+ * Print an IP6 header.
+ */
+void
+pr_iph(ip6)
+ struct ip6_hdr *ip6;
+{
+ u_int32_t flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK;
+ u_int8_t tc;
+ char ntop_buf[INET6_ADDRSTRLEN];
+
+ tc = *(&ip6->ip6_vfc + 1); /* XXX */
+ tc = (tc >> 4) & 0x0f;
+ tc |= (ip6->ip6_vfc << 4);
+
+ printf("Vr TC Flow Plen Nxt Hlim\n");
+ printf(" %1x %02x %05x %04x %02x %02x\n",
+ (ip6->ip6_vfc & IPV6_VERSION_MASK) >> 4, tc, (u_int32_t)ntohl(flow),
+ ntohs(ip6->ip6_plen), ip6->ip6_nxt, ip6->ip6_hlim);
+ if (!inet_ntop(AF_INET6, &ip6->ip6_src, ntop_buf, sizeof(ntop_buf)))
+ strlcpy(ntop_buf, "?", sizeof(ntop_buf));
+ printf("%s->", ntop_buf);
+ if (!inet_ntop(AF_INET6, &ip6->ip6_dst, ntop_buf, sizeof(ntop_buf)))
+ strlcpy(ntop_buf, "?", sizeof(ntop_buf));
+ printf("%s\n", ntop_buf);
+}
+
+/*
+ * pr_addr --
+ * Return an ascii host address as a dotted quad and optionally with
+ * a hostname.
+ */
+const char *
+pr_addr(addr, addrlen)
+ struct sockaddr *addr;
+ int addrlen;
+{
+ static char buf[NI_MAXHOST];
+ int flag = 0;
+
+ if ((options & F_HOSTNAME) == 0)
+ flag |= NI_NUMERICHOST;
+
+ if (getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0, flag) == 0)
+ return (buf);
+ else
+ return "?";
+}
+
+/*
+ * pr_retip --
+ * Dump some info on a returned (via ICMPv6) IPv6 packet.
+ */
+void
+pr_retip(ip6, end)
+ struct ip6_hdr *ip6;
+ u_char *end;
+{
+ u_char *cp = (u_char *)ip6, nh;
+ int hlen;
+
+ if (end - (u_char *)ip6 < sizeof(*ip6)) {
+ printf("IP6");
+ goto trunc;
+ }
+ pr_iph(ip6);
+ hlen = sizeof(*ip6);
+
+ nh = ip6->ip6_nxt;
+ cp += hlen;
+ while (end - cp >= 8) {
+ switch (nh) {
+ case IPPROTO_HOPOPTS:
+ printf("HBH ");
+ hlen = (((struct ip6_hbh *)cp)->ip6h_len+1) << 3;
+ nh = ((struct ip6_hbh *)cp)->ip6h_nxt;
+ break;
+ case IPPROTO_DSTOPTS:
+ printf("DSTOPT ");
+ hlen = (((struct ip6_dest *)cp)->ip6d_len+1) << 3;
+ nh = ((struct ip6_dest *)cp)->ip6d_nxt;
+ break;
+ case IPPROTO_FRAGMENT:
+ printf("FRAG ");
+ hlen = sizeof(struct ip6_frag);
+ nh = ((struct ip6_frag *)cp)->ip6f_nxt;
+ break;
+ case IPPROTO_ROUTING:
+ printf("RTHDR ");
+ hlen = (((struct ip6_rthdr *)cp)->ip6r_len+1) << 3;
+ nh = ((struct ip6_rthdr *)cp)->ip6r_nxt;
+ break;
+#ifdef IPSEC
+ case IPPROTO_AH:
+ printf("AH ");
+ hlen = (((struct ah *)cp)->ah_len+2) << 2;
+ nh = ((struct ah *)cp)->ah_nxt;
+ break;
+#endif
+ case IPPROTO_ICMPV6:
+ printf("ICMP6: type = %d, code = %d\n",
+ *cp, *(cp + 1));
+ return;
+ case IPPROTO_ESP:
+ printf("ESP\n");
+ return;
+ case IPPROTO_TCP:
+ printf("TCP: from port %u, to port %u (decimal)\n",
+ (*cp * 256 + *(cp + 1)),
+ (*(cp + 2) * 256 + *(cp + 3)));
+ return;
+ case IPPROTO_UDP:
+ printf("UDP: from port %u, to port %u (decimal)\n",
+ (*cp * 256 + *(cp + 1)),
+ (*(cp + 2) * 256 + *(cp + 3)));
+ return;
+ default:
+ printf("Unknown Header(%d)\n", nh);
+ return;
+ }
+
+ if ((cp += hlen) >= end)
+ goto trunc;
+ }
+ if (end - cp < 8)
+ goto trunc;
+
+ putchar('\n');
+ return;
+
+ trunc:
+ printf("...\n");
+ return;
+}
+
+void
+fill(bp, patp)
+ char *bp, *patp;
+{
+ int ii, jj, kk;
+ int pat[16];
+ char *cp;
+
+ for (cp = patp; *cp; cp++)
+ if (!isxdigit(*cp))
+ errx(1, "patterns must be specified as hex digits");
+ ii = sscanf(patp,
+ "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
+ &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6],
+ &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12],
+ &pat[13], &pat[14], &pat[15]);
+
+/* xxx */
+ if (ii > 0)
+ for (kk = 0;
+ kk <= MAXDATALEN - (8 + sizeof(struct tv32) + ii);
+ kk += ii)
+ for (jj = 0; jj < ii; ++jj)
+ bp[jj + kk] = pat[jj];
+ if (!(options & F_QUIET)) {
+ (void)printf("PATTERN: 0x");
+ for (jj = 0; jj < ii; ++jj)
+ (void)printf("%02x", bp[jj] & 0xFF);
+ (void)printf("\n");
+ }
+}
+
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+int
+setpolicy(so, policy)
+ int so;
+ char *policy;
+{
+ char *buf;
+
+ if (policy == NULL)
+ return 0; /* ignore */
+
+ buf = ipsec_set_policy(policy, strlen(policy));
+ if (buf == NULL)
+ errx(1, "%s", ipsec_strerror());
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, buf,
+ ipsec_get_policylen(buf)) < 0)
+ warnx("Unable to set IPsec policy");
+ free(buf);
+
+ return 0;
+}
+#endif
+#endif
+
+char *
+nigroup(name)
+ char *name;
+{
+ char *p;
+ char *q;
+ MD5_CTX ctxt;
+ u_int8_t digest[16];
+ u_int8_t c;
+ size_t l;
+ char hbuf[NI_MAXHOST];
+ struct in6_addr in6;
+
+ p = strchr(name, '.');
+ if (!p)
+ p = name + strlen(name);
+ l = p - name;
+ if (l > 63 || l > sizeof(hbuf) - 1)
+ return NULL; /*label too long*/
+ strncpy(hbuf, name, l);
+ hbuf[(int)l] = '\0';
+
+ for (q = name; *q; q++) {
+ if (isupper(*(unsigned char *)q))
+ *q = tolower(*(unsigned char *)q);
+ }
+
+ /* generate 8 bytes of pseudo-random value. */
+ memset(&ctxt, 0, sizeof(ctxt));
+ MD5Init(&ctxt);
+ c = l & 0xff;
+ MD5Update(&ctxt, &c, sizeof(c));
+ MD5Update(&ctxt, (unsigned char *)name, l);
+ MD5Final(digest, &ctxt);
+
+ if (inet_pton(AF_INET6, "ff02::2:0000:0000", &in6) != 1)
+ return NULL; /*XXX*/
+ bcopy(digest, &in6.s6_addr[12], 4);
+
+ if (inet_ntop(AF_INET6, &in6, hbuf, sizeof(hbuf)) == NULL)
+ return NULL;
+
+ return strdup(hbuf);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+#if defined(IPSEC) && !defined(IPSEC_POLICY_IPSEC)
+ "A"
+#endif
+ "usage: ping6 [-"
+ "d"
+#if defined(IPSEC) && !defined(IPSEC_POLICY_IPSEC)
+ "E"
+#endif
+ "fH"
+#ifdef IPV6_USE_MIN_MTU
+ "m"
+#endif
+ "nNqtvwW] "
+ "[-a addrtype] [-b bufsiz] [-c count] [-g gateway]\n"
+ " [-h hoplimit] [-I interface] [-i wait] [-l preload]"
+#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+ " [-P policy]"
+#endif
+ "\n"
+ " [-p pattern] [-S sourceaddr] [-s packetsize] "
+ "[hops ...] host\n");
+ exit(1);
+}
diff --git a/sbin/quotacheck/Makefile b/sbin/quotacheck/Makefile
new file mode 100644
index 0000000..b54eda3
--- /dev/null
+++ b/sbin/quotacheck/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= quotacheck
+SRCS= quotacheck.c preen.c utilities.c
+WARNS?= 2
+MAN= quotacheck.8
+
+.PATH: ${.CURDIR}/../fsck_ffs
+
+.include <bsd.prog.mk>
diff --git a/sbin/quotacheck/preen.c b/sbin/quotacheck/preen.c
new file mode 100644
index 0000000..93ce142
--- /dev/null
+++ b/sbin/quotacheck/preen.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 1990, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char sccsid[] = "@(#)preen.c 8.5 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fstab.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+char *blockcheck(char *origname);
+
+
+struct part {
+ struct part *next; /* forward link of partitions on disk */
+ char *name; /* device name */
+ char *fsname; /* mounted file system name */
+ long auxdata; /* auxiliary data for application */
+} *badlist, **badnext = &badlist;
+
+struct disk {
+ char *name; /* disk base name */
+ struct disk *next; /* forward link for list of disks */
+ struct part *part; /* head of list of partitions on disk */
+ int pid; /* If != 0, pid of proc working on */
+} *disks;
+
+int nrun, ndisks;
+
+static void addpart(char *name, char *fsname, long auxdata);
+static struct disk *finddisk(char *name);
+static int startdisk(struct disk *dk,int (*checkit)(char *, char *, long, int));
+
+int
+checkfstab(int preen, int maxrun, int (*docheck)(struct fstab *),
+ int (*chkit)(char *, char *, long, int))
+{
+ struct fstab *fsp;
+ struct disk *dk, *nextdisk;
+ struct part *pt;
+ int ret, pid, retcode, passno, sumstatus, status;
+ long auxdata;
+ char *name;
+
+ sumstatus = 0;
+ for (passno = 1; passno <= 2; passno++) {
+ if (setfsent() == 0) {
+ fprintf(stderr, "Can't open checklist file: %s\n",
+ _PATH_FSTAB);
+ return (8);
+ }
+ while ((fsp = getfsent()) != 0) {
+ if ((auxdata = (*docheck)(fsp)) == 0)
+ continue;
+ if (preen == 0 ||
+ (passno == 1 && fsp->fs_passno == 1)) {
+ if ((name = blockcheck(fsp->fs_spec)) != 0) {
+ if ((sumstatus = (*chkit)(name,
+ fsp->fs_file, auxdata, 0)) != 0)
+ return (sumstatus);
+ } else if (preen)
+ return (8);
+ } else if (passno == 2 && fsp->fs_passno > 1) {
+ if ((name = blockcheck(fsp->fs_spec)) == NULL) {
+ fprintf(stderr, "BAD DISK NAME %s\n",
+ fsp->fs_spec);
+ sumstatus |= 8;
+ continue;
+ }
+ addpart(name, fsp->fs_file, auxdata);
+ }
+ }
+ if (preen == 0)
+ return (0);
+ }
+ if (preen) {
+ if (maxrun == 0)
+ maxrun = ndisks;
+ if (maxrun > ndisks)
+ maxrun = ndisks;
+ nextdisk = disks;
+ for (passno = 0; passno < maxrun; ++passno) {
+ while ((ret = startdisk(nextdisk, chkit)) && nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ nextdisk = nextdisk->next;
+ }
+ while ((pid = wait(&status)) != -1) {
+ for (dk = disks; dk; dk = dk->next)
+ if (dk->pid == pid)
+ break;
+ if (dk == 0) {
+ printf("Unknown pid %d\n", pid);
+ continue;
+ }
+ if (WIFEXITED(status))
+ retcode = WEXITSTATUS(status);
+ else
+ retcode = 0;
+ if (WIFSIGNALED(status)) {
+ printf("%s (%s): EXITED WITH SIGNAL %d\n",
+ dk->part->name, dk->part->fsname,
+ WTERMSIG(status));
+ retcode = 8;
+ }
+ if (retcode != 0) {
+ sumstatus |= retcode;
+ *badnext = dk->part;
+ badnext = &dk->part->next;
+ dk->part = dk->part->next;
+ *badnext = NULL;
+ } else
+ dk->part = dk->part->next;
+ dk->pid = 0;
+ nrun--;
+ if (dk->part == NULL)
+ ndisks--;
+
+ if (nextdisk == NULL) {
+ if (dk->part) {
+ while ((ret = startdisk(dk, chkit)) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ } else if (nrun < maxrun && nrun < ndisks) {
+ for ( ;; ) {
+ if ((nextdisk = nextdisk->next) == NULL)
+ nextdisk = disks;
+ if (nextdisk->part != NULL &&
+ nextdisk->pid == 0)
+ break;
+ }
+ while ((ret = startdisk(nextdisk, chkit)) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ }
+ }
+ if (sumstatus) {
+ if (badlist == 0)
+ return (sumstatus);
+ fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
+ badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:");
+ for (pt = badlist; pt; pt = pt->next)
+ fprintf(stderr, "%s (%s)%s", pt->name, pt->fsname,
+ pt->next ? ", " : "\n");
+ return (sumstatus);
+ }
+ (void)endfsent();
+ return (0);
+}
+
+static struct disk *
+finddisk(char *name)
+{
+ struct disk *dk, **dkp;
+ char *p;
+ size_t len;
+
+ p = strrchr(name, '/');
+ p = p == NULL ? name : p + 1;
+ while (*p != '\0' && !isdigit((u_char)*p))
+ p++;
+ while (isdigit((u_char)*p))
+ p++;
+ len = (size_t)(p - name);
+ for (dk = disks, dkp = &disks; dk; dkp = &dk->next, dk = dk->next) {
+ if (strncmp(dk->name, name, len) == 0 &&
+ dk->name[len] == 0)
+ return (dk);
+ }
+ if ((*dkp = (struct disk *)malloc(sizeof(struct disk))) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ dk = *dkp;
+ if ((dk->name = malloc(len + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strncpy(dk->name, name, len);
+ dk->name[len] = '\0';
+ dk->part = NULL;
+ dk->next = NULL;
+ dk->pid = 0;
+ ndisks++;
+ return (dk);
+}
+
+static void
+addpart(char *name, char *fsname, long auxdata)
+{
+ struct disk *dk = finddisk(name);
+ struct part *pt, **ppt = &dk->part;
+
+ for (pt = dk->part; pt; ppt = &pt->next, pt = pt->next)
+ if (strcmp(pt->name, name) == 0) {
+ printf("%s in fstab more than once!\n", name);
+ return;
+ }
+ if ((*ppt = (struct part *)malloc(sizeof(struct part))) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ pt = *ppt;
+ if ((pt->name = malloc(strlen(name) + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strcpy(pt->name, name);
+ if ((pt->fsname = malloc(strlen(fsname) + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strcpy(pt->fsname, fsname);
+ pt->next = NULL;
+ pt->auxdata = auxdata;
+}
+
+static int
+startdisk(struct disk *dk, int (*checkit)(char *, char *, long, int))
+{
+ struct part *pt = dk->part;
+
+ dk->pid = fork();
+ if (dk->pid < 0) {
+ perror("fork");
+ return (8);
+ }
+ if (dk->pid == 0)
+ exit((*checkit)(pt->name, pt->fsname, pt->auxdata, 1));
+ nrun++;
+ return (0);
+}
+
diff --git a/sbin/quotacheck/quotacheck.8 b/sbin/quotacheck/quotacheck.8
new file mode 100644
index 0000000..0eb3eb8
--- /dev/null
+++ b/sbin/quotacheck/quotacheck.8
@@ -0,0 +1,162 @@
+.\" Copyright (c) 1983, 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Robert Elz at The University of Melbourne.
+.\"
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)quotacheck.8 8.1 (Berkeley) 6/5/93
+.\" $FreeBSD$
+.\"
+.Dd June 5, 1993
+.Dt QUOTACHECK 8
+.Os
+.Sh NAME
+.Nm quotacheck
+.Nd file system quota consistency checker
+.Sh SYNOPSIS
+.Nm
+.Op Fl guv
+.Fl a
+.Nm
+.Op Fl guv
+.Ar filesystem ...
+.Sh DESCRIPTION
+The
+.Nm
+utility examines each file system,
+builds a table of current disk usage,
+and compares this table against that recorded
+in the disk quota file for the file system.
+If any inconsistencies are detected, both the
+quota file and the current system copy of the
+incorrect quotas are updated (the latter only
+occurs if an active file system is checked).
+By default both user and group quotas are checked.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+If supplied in place of any file system names,
+.Nm
+will check all the file systems indicated in
+.Pa /etc/fstab
+to be read-write with disk quotas.
+By default only the types of quotas listed in
+.Pa /etc/fstab
+are checked.
+.It Fl g
+Only group quotas listed in
+.Pa /etc/fstab
+are to be checked.
+.It Fl u
+Only user quotas listed in
+.Pa /etc/fstab
+are to be checked.
+.It Fl v
+Report discrepancies between the
+calculated and recorded disk quotas and other additional diagnostic messages.
+.El
+.Pp
+Specifying both
+.Fl g
+and
+.Fl u
+is equivalent to the default.
+Parallel passes are run on the file systems required,
+using the pass numbers in
+.Pa /etc/fstab
+in an identical fashion to
+.Xr fsck 8 .
+.Pp
+Normally,
+.Nm
+operates silently.
+.Pp
+The
+.Nm
+utility expects each file system to be checked to have a
+quota files named
+.Pa quota.user
+and
+.Pa quota.group
+which are located at the root of the associated file system.
+These defaults may be overridden in
+.Pa /etc/fstab .
+If a file is not present,
+.Nm
+will create it.
+These files should be edited with the
+.Xr edquota 8
+utility.
+.Pp
+The
+.Nm
+utility is normally run at boot time from the
+.Pa /etc/rc
+file.
+The rc startup procedure is controlled by the
+.Pa /etc/rc.conf
+variable
+.Ar check_quotas .
+Note that to enable this functionality in
+.Pa /etc/rc
+you also need to enable startup quota procedures
+with the variable
+.Ar enable_quotas
+in
+.Pa /etc/rc.conf .
+.Pp
+The
+.Nm
+utility accesses the raw device in calculating the actual
+disk usage for each user.
+Thus, the file systems
+checked should be quiescent while
+.Nm
+is running.
+.Sh FILES
+.Bl -tag -width quota.group -compact
+.It Pa quota.user
+at the file system root with user quotas
+.It Pa quota.group
+at the file system root with group quotas
+.It Pa /etc/fstab
+default file systems
+.El
+.Sh SEE ALSO
+.Xr quota 1 ,
+.Xr quotactl 2 ,
+.Xr fstab 5 ,
+.Xr edquota 8 ,
+.Xr fsck 8 ,
+.Xr quotaon 8 ,
+.Xr repquota 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
diff --git a/sbin/quotacheck/quotacheck.c b/sbin/quotacheck/quotacheck.c
new file mode 100644
index 0000000..634124e
--- /dev/null
+++ b/sbin/quotacheck/quotacheck.c
@@ -0,0 +1,691 @@
+/*
+ * Copyright (c) 1980, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)quotacheck.c 8.3 (Berkeley) 1/29/94";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Fix up / report on disk quotas & usage
+ */
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+char *qfname = QUOTAFILENAME;
+char *qfextension[] = INITQFNAMES;
+char *quotagroup = QUOTAGROUP;
+
+union {
+ struct fs sblk;
+ char dummy[MAXBSIZE];
+} sb_un;
+#define sblock sb_un.sblk
+union {
+ struct cg cgblk;
+ char dummy[MAXBSIZE];
+} cg_un;
+#define cgblk cg_un.cgblk
+long dev_bsize = 1;
+ino_t maxino;
+
+union dinode {
+ struct ufs1_dinode dp1;
+ struct ufs2_dinode dp2;
+};
+#define DIP(dp, field) \
+ ((sblock.fs_magic == FS_UFS1_MAGIC) ? \
+ (dp)->dp1.field : (dp)->dp2.field)
+
+struct quotaname {
+ long flags;
+ char grpqfname[PATH_MAX];
+ char usrqfname[PATH_MAX];
+};
+#define HASUSR 1
+#define HASGRP 2
+
+struct fileusage {
+ struct fileusage *fu_next;
+ u_long fu_curinodes;
+ u_long fu_curblocks;
+ u_long fu_id;
+ char fu_name[1];
+ /* actually bigger */
+};
+#define FUHASH 1024 /* must be power of two */
+struct fileusage *fuhead[MAXQUOTAS][FUHASH];
+
+int aflag; /* all file systems */
+int gflag; /* check group quotas */
+int uflag; /* check user quotas */
+int vflag; /* verbose */
+int fi; /* open disk file descriptor */
+u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */
+
+struct fileusage *
+ addid(u_long, int, char *);
+char *blockcheck(char *);
+void bread(ufs2_daddr_t, char *, long);
+extern int checkfstab(int, int, void * (*)(struct fstab *),
+ int (*)(char *, char *, struct quotaname *));
+int chkquota(char *, char *, struct quotaname *);
+void freeinodebuf(void);
+union dinode *
+ getnextinode(ino_t);
+int getquotagid(void);
+int hasquota(struct fstab *, int, char **);
+struct fileusage *
+ lookup(u_long, int);
+void *needchk(struct fstab *);
+int oneof(char *, char*[], int);
+void setinodebuf(ino_t);
+int update(char *, char *, int);
+void usage(void);
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct fstab *fs;
+ struct passwd *pw;
+ struct group *gr;
+ struct quotaname *auxdata;
+ int i, argnum, maxrun, errs, ch;
+ long done = 0;
+ char *name;
+
+ errs = maxrun = 0;
+ while ((ch = getopt(argc, argv, "aguvl:")) != -1) {
+ switch(ch) {
+ case 'a':
+ aflag++;
+ break;
+ case 'g':
+ gflag++;
+ break;
+ case 'u':
+ uflag++;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ case 'l':
+ maxrun = atoi(optarg);
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if ((argc == 0 && !aflag) || (argc > 0 && aflag))
+ usage();
+ if (!gflag && !uflag) {
+ gflag++;
+ uflag++;
+ }
+ if (gflag) {
+ setgrent();
+ while ((gr = getgrent()) != NULL)
+ (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
+ endgrent();
+ }
+ if (uflag) {
+ setpwent();
+ while ((pw = getpwent()) != NULL)
+ (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
+ endpwent();
+ }
+ if (aflag)
+ exit(checkfstab(1, maxrun, needchk, chkquota));
+ if (setfsent() == 0)
+ errx(1, "%s: can't open", FSTAB);
+ while ((fs = getfsent()) != NULL) {
+ if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
+ (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
+ (auxdata = needchk(fs)) &&
+ (name = blockcheck(fs->fs_spec))) {
+ done |= 1 << argnum;
+ errs += chkquota(name, fs->fs_file, auxdata);
+ }
+ }
+ endfsent();
+ for (i = 0; i < argc; i++)
+ if ((done & (1 << i)) == 0)
+ fprintf(stderr, "%s not found in %s\n",
+ argv[i], FSTAB);
+ exit(errs);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "%s\n%s\n",
+ "usage: quotacheck [-guv] -a",
+ " quotacheck [-guv] filesystem ...");
+ exit(1);
+}
+
+void *
+needchk(fs)
+ struct fstab *fs;
+{
+ struct quotaname *qnp;
+ char *qfnp;
+
+ if (strcmp(fs->fs_vfstype, "ufs") ||
+ strcmp(fs->fs_type, FSTAB_RW))
+ return (NULL);
+ if ((qnp = malloc(sizeof(*qnp))) == NULL)
+ errx(1, "malloc failed");
+ qnp->flags = 0;
+ if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
+ strcpy(qnp->grpqfname, qfnp);
+ qnp->flags |= HASGRP;
+ }
+ if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
+ strcpy(qnp->usrqfname, qfnp);
+ qnp->flags |= HASUSR;
+ }
+ if (qnp->flags)
+ return (qnp);
+ free(qnp);
+ return (NULL);
+}
+
+/*
+ * Possible superblock locations ordered from most to least likely.
+ */
+static int sblock_try[] = SBLOCKSEARCH;
+
+/*
+ * Scan the specified file system to check quota(s) present on it.
+ */
+int
+chkquota(fsname, mntpt, qnp)
+ char *fsname, *mntpt;
+ struct quotaname *qnp;
+{
+ struct fileusage *fup;
+ union dinode *dp;
+ int cg, i, mode, errs = 0;
+ ino_t ino, inosused;
+ char *cp;
+
+ if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
+ warn("%s", fsname);
+ return (1);
+ }
+ if (vflag) {
+ (void)printf("*** Checking ");
+ if (qnp->flags & HASUSR)
+ (void)printf("%s%s", qfextension[USRQUOTA],
+ (qnp->flags & HASGRP) ? " and " : "");
+ if (qnp->flags & HASGRP)
+ (void)printf("%s", qfextension[GRPQUOTA]);
+ (void)printf(" quotas for %s (%s)\n", fsname, mntpt);
+ }
+ sync();
+ dev_bsize = 1;
+ for (i = 0; sblock_try[i] != -1; i++) {
+ bread(sblock_try[i], (char *)&sblock, (long)SBLOCKSIZE);
+ if ((sblock.fs_magic == FS_UFS1_MAGIC ||
+ (sblock.fs_magic == FS_UFS2_MAGIC &&
+ sblock.fs_sblockloc == sblock_try[i])) &&
+ sblock.fs_bsize <= MAXBSIZE &&
+ sblock.fs_bsize >= sizeof(struct fs))
+ break;
+ }
+ if (sblock_try[i] == -1) {
+ warn("Cannot find file system superblock");
+ return (1);
+ }
+ dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
+ maxino = sblock.fs_ncg * sblock.fs_ipg;
+ for (cg = 0; cg < sblock.fs_ncg; cg++) {
+ ino = cg * sblock.fs_ipg;
+ setinodebuf(ino);
+ bread(fsbtodb(&sblock, cgtod(&sblock, cg)), (char *)(&cgblk),
+ sblock.fs_cgsize);
+ if (sblock.fs_magic == FS_UFS2_MAGIC)
+ inosused = cgblk.cg_initediblk;
+ else
+ inosused = sblock.fs_ipg;
+ /*
+ * If we are using soft updates, then we can trust the
+ * cylinder group inode allocation maps to tell us which
+ * inodes are allocated. We will scan the used inode map
+ * to find the inodes that are really in use, and then
+ * read only those inodes in from disk.
+ */
+ if (sblock.fs_flags & FS_DOSOFTDEP) {
+ if (!cg_chkmagic(&cgblk))
+ errx(1, "CG %d: BAD MAGIC NUMBER\n", cg);
+ cp = &cg_inosused(&cgblk)[(inosused - 1) / CHAR_BIT];
+ for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) {
+ if (*cp == 0)
+ continue;
+ for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) {
+ if (*cp & i)
+ break;
+ inosused--;
+ }
+ break;
+ }
+ if (inosused <= 0)
+ continue;
+ }
+ for (i = 0; i < inosused; i++, ino++) {
+ if ((dp = getnextinode(ino)) == NULL || ino < ROOTINO ||
+ (mode = DIP(dp, di_mode) & IFMT) == 0)
+ continue;
+ if (qnp->flags & HASGRP) {
+ fup = addid((u_long)DIP(dp, di_gid), GRPQUOTA,
+ (char *)0);
+ fup->fu_curinodes++;
+ if (mode == IFREG || mode == IFDIR ||
+ mode == IFLNK)
+ fup->fu_curblocks += DIP(dp, di_blocks);
+ }
+ if (qnp->flags & HASUSR) {
+ fup = addid((u_long)DIP(dp, di_uid), USRQUOTA,
+ (char *)0);
+ fup->fu_curinodes++;
+ if (mode == IFREG || mode == IFDIR ||
+ mode == IFLNK)
+ fup->fu_curblocks += DIP(dp, di_blocks);
+ }
+ }
+ }
+ freeinodebuf();
+ if (qnp->flags & HASUSR)
+ errs += update(mntpt, qnp->usrqfname, USRQUOTA);
+ if (qnp->flags & HASGRP)
+ errs += update(mntpt, qnp->grpqfname, GRPQUOTA);
+ close(fi);
+ return (errs);
+}
+
+/*
+ * Update a specified quota file.
+ */
+int
+update(fsname, quotafile, type)
+ char *fsname, *quotafile;
+ int type;
+{
+ struct fileusage *fup;
+ FILE *qfi, *qfo;
+ u_long id, lastid;
+ off_t offset;
+ struct dqblk dqbuf;
+ static int warned = 0;
+ static struct dqblk zerodqbuf;
+ static struct fileusage zerofileusage;
+
+ if ((qfo = fopen(quotafile, "r+")) == NULL) {
+ if (errno == ENOENT)
+ qfo = fopen(quotafile, "w+");
+ if (qfo) {
+ warnx("creating quota file %s", quotafile);
+#define MODE (S_IRUSR|S_IWUSR|S_IRGRP)
+ (void) fchown(fileno(qfo), getuid(), getquotagid());
+ (void) fchmod(fileno(qfo), MODE);
+ } else {
+ warn("%s", quotafile);
+ return (1);
+ }
+ }
+ if ((qfi = fopen(quotafile, "r")) == NULL) {
+ warn("%s", quotafile);
+ (void) fclose(qfo);
+ return (1);
+ }
+ if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 &&
+ errno == EOPNOTSUPP && !warned && vflag) {
+ warned++;
+ (void)printf("*** Warning: %s\n",
+ "Quotas are not compiled into this kernel");
+ }
+ for (lastid = highid[type], id = 0, offset = 0; id <= lastid;
+ id++, offset += sizeof(struct dqblk)) {
+ if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
+ dqbuf = zerodqbuf;
+ if ((fup = lookup(id, type)) == 0)
+ fup = &zerofileusage;
+ if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
+ dqbuf.dqb_curblocks == fup->fu_curblocks) {
+ fup->fu_curinodes = 0;
+ fup->fu_curblocks = 0;
+ continue;
+ }
+ if (vflag) {
+ if (aflag)
+ printf("%s: ", fsname);
+ printf("%-8s fixed:", fup->fu_name);
+ if (dqbuf.dqb_curinodes != fup->fu_curinodes)
+ (void)printf("\tinodes %lu -> %lu",
+ (u_long)dqbuf.dqb_curinodes,
+ (u_long)fup->fu_curinodes);
+ if (dqbuf.dqb_curblocks != fup->fu_curblocks)
+ (void)printf("\tblocks %lu -> %lu",
+ (u_long)dqbuf.dqb_curblocks,
+ (u_long)fup->fu_curblocks);
+ (void)printf("\n");
+ }
+ /*
+ * Reset time limit if have a soft limit and were
+ * previously under it, but are now over it.
+ */
+ if (dqbuf.dqb_bsoftlimit &&
+ dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
+ fup->fu_curblocks >= dqbuf.dqb_bsoftlimit)
+ dqbuf.dqb_btime = 0;
+ if (dqbuf.dqb_isoftlimit &&
+ dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit &&
+ fup->fu_curblocks >= dqbuf.dqb_isoftlimit)
+ dqbuf.dqb_itime = 0;
+ dqbuf.dqb_curinodes = fup->fu_curinodes;
+ dqbuf.dqb_curblocks = fup->fu_curblocks;
+ if (fseek(qfo, offset, SEEK_SET) < 0) {
+ warn("%s: seek failed", quotafile);
+ return(1);
+ }
+ fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
+ (void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
+ (caddr_t)&dqbuf);
+ fup->fu_curinodes = 0;
+ fup->fu_curblocks = 0;
+ }
+ fclose(qfi);
+ fflush(qfo);
+ ftruncate(fileno(qfo),
+ (((off_t)highid[type] + 1) * sizeof(struct dqblk)));
+ fclose(qfo);
+ return (0);
+}
+
+/*
+ * Check to see if target appears in list of size cnt.
+ */
+int
+oneof(target, list, cnt)
+ char *target, *list[];
+ int cnt;
+{
+ int i;
+
+ for (i = 0; i < cnt; i++)
+ if (strcmp(target, list[i]) == 0)
+ return (i);
+ return (-1);
+}
+
+/*
+ * Determine the group identifier for quota files.
+ */
+int
+getquotagid()
+{
+ struct group *gr;
+
+ if ((gr = getgrnam(quotagroup)) != NULL)
+ return (gr->gr_gid);
+ return (-1);
+}
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+int
+hasquota(fs, type, qfnamep)
+ struct fstab *fs;
+ int type;
+ char **qfnamep;
+{
+ char *opt;
+ char *cp;
+ static char initname, usrname[100], grpname[100];
+ static char buf[BUFSIZ];
+
+ if (!initname) {
+ (void)snprintf(usrname, sizeof(usrname),
+ "%s%s", qfextension[USRQUOTA], qfname);
+ (void)snprintf(grpname, sizeof(grpname),
+ "%s%s", qfextension[GRPQUOTA], qfname);
+ initname = 1;
+ }
+ strcpy(buf, fs->fs_mntops);
+ for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
+ if ((cp = index(opt, '=')) != NULL)
+ *cp++ = '\0';
+ if (type == USRQUOTA && strcmp(opt, usrname) == 0)
+ break;
+ if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
+ break;
+ }
+ if (!opt)
+ return (0);
+ if (cp)
+ *qfnamep = cp;
+ else {
+ (void)snprintf(buf, sizeof(buf),
+ "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
+ *qfnamep = buf;
+ }
+ return (1);
+}
+
+/*
+ * Routines to manage the file usage table.
+ *
+ * Lookup an id of a specific type.
+ */
+struct fileusage *
+lookup(id, type)
+ u_long id;
+ int type;
+{
+ struct fileusage *fup;
+
+ for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
+ if (fup->fu_id == id)
+ return (fup);
+ return (NULL);
+}
+
+/*
+ * Add a new file usage id if it does not already exist.
+ */
+struct fileusage *
+addid(id, type, name)
+ u_long id;
+ int type;
+ char *name;
+{
+ struct fileusage *fup, **fhp;
+ int len;
+
+ if ((fup = lookup(id, type)) != NULL)
+ return (fup);
+ if (name)
+ len = strlen(name);
+ else
+ len = 10;
+ if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
+ errx(1, "calloc failed");
+ fhp = &fuhead[type][id & (FUHASH - 1)];
+ fup->fu_next = *fhp;
+ *fhp = fup;
+ fup->fu_id = id;
+ if (id > highid[type])
+ highid[type] = id;
+ if (name)
+ bcopy(name, fup->fu_name, len + 1);
+ else {
+ (void)sprintf(fup->fu_name, "%lu", id);
+ if (vflag)
+ printf("unknown %cid: %lu\n",
+ type == USRQUOTA ? 'u' : 'g', id);
+ }
+ return (fup);
+}
+
+/*
+ * Special purpose version of ginode used to optimize pass
+ * over all the inodes in numerical order.
+ */
+static ino_t nextino, lastinum, lastvalidinum;
+static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
+static caddr_t inodebuf;
+#define INOBUFSIZE 56*1024 /* size of buffer to read inodes */
+
+union dinode *
+getnextinode(ino_t inumber)
+{
+ long size;
+ ufs2_daddr_t dblk;
+ union dinode *dp;
+ static caddr_t nextinop;
+
+ if (inumber != nextino++ || inumber > lastvalidinum)
+ errx(1, "bad inode number %d to nextinode", inumber);
+ if (inumber >= lastinum) {
+ readcnt++;
+ dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
+ if (readcnt % readpercg == 0) {
+ size = partialsize;
+ lastinum += partialcnt;
+ } else {
+ size = inobufsize;
+ lastinum += fullcnt;
+ }
+ /*
+ * If bread returns an error, it will already have zeroed
+ * out the buffer, so we do not need to do so here.
+ */
+ bread(dblk, inodebuf, size);
+ nextinop = inodebuf;
+ }
+ dp = (union dinode *)nextinop;
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ nextinop += sizeof(struct ufs1_dinode);
+ else
+ nextinop += sizeof(struct ufs2_dinode);
+ return (dp);
+}
+
+/*
+ * Prepare to scan a set of inodes.
+ */
+void
+setinodebuf(ino_t inum)
+{
+
+ if (inum % sblock.fs_ipg != 0)
+ errx(1, "bad inode number %d to setinodebuf", inum);
+ lastvalidinum = inum + sblock.fs_ipg - 1;
+ nextino = inum;
+ lastinum = inum;
+ readcnt = 0;
+ if (inodebuf != NULL)
+ return;
+ inobufsize = blkroundup(&sblock, INOBUFSIZE);
+ fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ?
+ sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
+ readpercg = sblock.fs_ipg / fullcnt;
+ partialcnt = sblock.fs_ipg % fullcnt;
+ partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ?
+ sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
+ if (partialcnt != 0) {
+ readpercg++;
+ } else {
+ partialcnt = fullcnt;
+ partialsize = inobufsize;
+ }
+ if ((inodebuf = malloc((unsigned)inobufsize)) == NULL)
+ errx(1, "cannot allocate space for inode buffer");
+}
+
+/*
+ * Free up data structures used to scan inodes.
+ */
+void
+freeinodebuf()
+{
+
+ if (inodebuf != NULL)
+ free(inodebuf);
+ inodebuf = NULL;
+}
+
+/*
+ * Read specified disk blocks.
+ */
+void
+bread(bno, buf, cnt)
+ ufs2_daddr_t bno;
+ char *buf;
+ long cnt;
+{
+
+ if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 ||
+ read(fi, buf, cnt) != cnt)
+ errx(1, "bread failed on block %ld", (long)bno);
+}
diff --git a/sbin/rcorder/Makefile b/sbin/rcorder/Makefile
new file mode 100644
index 0000000..2ca64cc
--- /dev/null
+++ b/sbin/rcorder/Makefile
@@ -0,0 +1,21 @@
+# $NetBSD: Makefile,v 1.1 1999/11/23 05:28:20 mrg Exp $
+# $FreeBSD$
+
+PROG= rcorder
+SRCS= ealloc.c hash.c rcorder.c
+MAN= rcorder.8
+
+LDADD= -lutil
+DPADD= ${LIBUTIL}
+
+WARNS?= 6
+# XXX hack for make's hash.[ch]
+CFLAGS+= -DORDER -I.
+
+SRCS+= util.h
+CLEANFILES+= util.h
+
+util.h:
+ ln -sf ${.CURDIR}/../../lib/libutil/libutil.h ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/sbin/rcorder/ealloc.c b/sbin/rcorder/ealloc.c
new file mode 100644
index 0000000..9accebe
--- /dev/null
+++ b/sbin/rcorder/ealloc.c
@@ -0,0 +1,123 @@
+/* $NetBSD: ealloc.c,v 1.1.1.1 1999/11/19 04:30:56 mrg Exp $ */
+
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * 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 the University of
+ * California, Berkeley and its contributors.
+ * 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 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/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: ealloc.c,v 1.1.1.1 1999/11/19 04:30:56 mrg Exp $");
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+
+#include "ealloc.h"
+
+static void enomem __P((void));
+
+/*
+ * enomem --
+ * die when out of memory.
+ */
+static void
+enomem()
+{
+ errx(2, "Cannot allocate memory.");
+}
+
+/*
+ * emalloc --
+ * malloc, but die on error.
+ */
+void *
+emalloc(len)
+ size_t len;
+{
+ void *p;
+
+ if ((p = malloc(len)) == NULL)
+ enomem();
+ return(p);
+}
+
+/*
+ * estrdup --
+ * strdup, but die on error.
+ */
+char *
+estrdup(str)
+ const char *str;
+{
+ char *p;
+
+ if ((p = strdup(str)) == NULL)
+ enomem();
+ return(p);
+}
+
+/*
+ * erealloc --
+ * realloc, but die on error.
+ */
+void *
+erealloc(ptr, size)
+ void *ptr;
+ size_t size;
+{
+ if ((ptr = realloc(ptr, size)) == NULL)
+ enomem();
+ return(ptr);
+}
+
+/*
+ * ecalloc --
+ * calloc, but die on error.
+ */
+void *
+ecalloc(nmemb, size)
+ size_t nmemb;
+ size_t size;
+{
+ void *ptr;
+
+ if ((ptr = calloc(nmemb, size)) == NULL)
+ enomem();
+ return(ptr);
+}
diff --git a/sbin/rcorder/ealloc.h b/sbin/rcorder/ealloc.h
new file mode 100644
index 0000000..fb753d5f
--- /dev/null
+++ b/sbin/rcorder/ealloc.h
@@ -0,0 +1,6 @@
+/* $NetBSD: ealloc.h,v 1.1.1.1 1999/11/19 04:30:56 mrg Exp $ */
+
+void *emalloc __P((size_t len));
+char *estrdup __P((const char *str));
+void *erealloc __P((void *ptr, size_t size));
+void *ecalloc __P((size_t nmemb, size_t size));
diff --git a/sbin/rcorder/hash.c b/sbin/rcorder/hash.c
new file mode 100644
index 0000000..5ec335a
--- /dev/null
+++ b/sbin/rcorder/hash.c
@@ -0,0 +1,438 @@
+/* $NetBSD: hash.c,v 1.1.1.1 1999/11/19 04:30:56 mrg Exp $ */
+
+/*
+ * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
+ * Copyright (c) 1988, 1989 by Adam de Boor
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * 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 the University of
+ * California, Berkeley and its contributors.
+ * 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 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 MAKE_BOOTSTRAP
+static char rcsid[] = "$NetBSD: hash.c,v 1.1.1.1 1999/11/19 04:30:56 mrg Exp $";
+#else
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)hash.c 8.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: hash.c,v 1.1.1.1 1999/11/19 04:30:56 mrg Exp $");
+#endif
+#endif /* not lint */
+#endif
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* hash.c --
+ *
+ * This module contains routines to manipulate a hash table.
+ * See hash.h for a definition of the structure of the hash
+ * table. Hash tables grow automatically as the amount of
+ * information increases.
+ */
+#include "sprite.h"
+#ifndef ORDER
+#include "make.h"
+#endif /* ORDER */
+#include "hash.h"
+#include "ealloc.h"
+
+/*
+ * Forward references to local procedures that are used before they're
+ * defined:
+ */
+
+static void RebuildTable __P((Hash_Table *));
+
+/*
+ * The following defines the ratio of # entries to # buckets
+ * at which we rebuild the table to make it larger.
+ */
+
+#define rebuildLimit 8
+
+/*
+ *---------------------------------------------------------
+ *
+ * Hash_InitTable --
+ *
+ * This routine just sets up the hash table.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Memory is allocated for the initial bucket area.
+ *
+ *---------------------------------------------------------
+ */
+
+void
+Hash_InitTable(t, numBuckets)
+ register Hash_Table *t; /* Structure to use to hold table. */
+ int numBuckets; /* How many buckets to create for starters.
+ * This number is rounded up to a power of
+ * two. If <= 0, a reasonable default is
+ * chosen. The table will grow in size later
+ * as needed. */
+{
+ register int i;
+ register struct Hash_Entry **hp;
+
+ /*
+ * Round up the size to a power of two.
+ */
+ if (numBuckets <= 0)
+ i = 16;
+ else {
+ for (i = 2; i < numBuckets; i <<= 1)
+ continue;
+ }
+ t->numEntries = 0;
+ t->size = i;
+ t->mask = i - 1;
+ t->bucketPtr = hp = (struct Hash_Entry **)emalloc(sizeof(*hp) * i);
+ while (--i >= 0)
+ *hp++ = NULL;
+}
+
+/*
+ *---------------------------------------------------------
+ *
+ * Hash_DeleteTable --
+ *
+ * This routine removes everything from a hash table
+ * and frees up the memory space it occupied (except for
+ * the space in the Hash_Table structure).
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Lots of memory is freed up.
+ *
+ *---------------------------------------------------------
+ */
+
+void
+Hash_DeleteTable(t)
+ Hash_Table *t;
+{
+ register struct Hash_Entry **hp, *h, *nexth = NULL;
+ register int i;
+
+ for (hp = t->bucketPtr, i = t->size; --i >= 0;) {
+ for (h = *hp++; h != NULL; h = nexth) {
+ nexth = h->next;
+ free((char *)h);
+ }
+ }
+ free((char *)t->bucketPtr);
+
+ /*
+ * Set up the hash table to cause memory faults on any future access
+ * attempts until re-initialization.
+ */
+ t->bucketPtr = NULL;
+}
+
+/*
+ *---------------------------------------------------------
+ *
+ * Hash_FindEntry --
+ *
+ * Searches a hash table for an entry corresponding to key.
+ *
+ * Results:
+ * The return value is a pointer to the entry for key,
+ * if key was present in the table. If key was not
+ * present, NULL is returned.
+ *
+ * Side Effects:
+ * None.
+ *
+ *---------------------------------------------------------
+ */
+
+Hash_Entry *
+Hash_FindEntry(t, key)
+ Hash_Table *t; /* Hash table to search. */
+ char *key; /* A hash key. */
+{
+ register Hash_Entry *e;
+ register unsigned h;
+ register char *p;
+
+ for (h = 0, p = key; *p;)
+ h = (h << 5) - h + *p++;
+ p = key;
+ for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next)
+ if (e->namehash == h && strcmp(e->name, p) == 0)
+ return (e);
+ return (NULL);
+}
+
+/*
+ *---------------------------------------------------------
+ *
+ * Hash_CreateEntry --
+ *
+ * Searches a hash table for an entry corresponding to
+ * key. If no entry is found, then one is created.
+ *
+ * Results:
+ * The return value is a pointer to the entry. If *newPtr
+ * isn't NULL, then *newPtr is filled in with TRUE if a
+ * new entry was created, and FALSE if an entry already existed
+ * with the given key.
+ *
+ * Side Effects:
+ * Memory may be allocated, and the hash buckets may be modified.
+ *---------------------------------------------------------
+ */
+
+Hash_Entry *
+Hash_CreateEntry(t, key, newPtr)
+ register Hash_Table *t; /* Hash table to search. */
+ char *key; /* A hash key. */
+ Boolean *newPtr; /* Filled in with TRUE if new entry created,
+ * FALSE otherwise. */
+{
+ register Hash_Entry *e;
+ register unsigned h;
+ register char *p;
+ int keylen;
+ struct Hash_Entry **hp;
+
+ /*
+ * Hash the key. As a side effect, save the length (strlen) of the
+ * key in case we need to create the entry.
+ */
+ for (h = 0, p = key; *p;)
+ h = (h << 5) - h + *p++;
+ keylen = p - key;
+ p = key;
+ for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) {
+ if (e->namehash == h && strcmp(e->name, p) == 0) {
+ if (newPtr != NULL)
+ *newPtr = FALSE;
+ return (e);
+ }
+ }
+
+ /*
+ * The desired entry isn't there. Before allocating a new entry,
+ * expand the table if necessary (and this changes the resulting
+ * bucket chain).
+ */
+ if (t->numEntries >= rebuildLimit * t->size)
+ RebuildTable(t);
+ e = (Hash_Entry *) emalloc(sizeof(*e) + keylen);
+ hp = &t->bucketPtr[h & t->mask];
+ e->next = *hp;
+ *hp = e;
+ e->clientData = NULL;
+ e->namehash = h;
+ (void) strcpy(e->name, p);
+ t->numEntries++;
+
+ if (newPtr != NULL)
+ *newPtr = TRUE;
+ return (e);
+}
+
+/*
+ *---------------------------------------------------------
+ *
+ * Hash_DeleteEntry --
+ *
+ * Delete the given hash table entry and free memory associated with
+ * it.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Hash chain that entry lives in is modified and memory is freed.
+ *
+ *---------------------------------------------------------
+ */
+
+void
+Hash_DeleteEntry(t, e)
+ Hash_Table *t;
+ Hash_Entry *e;
+{
+ register Hash_Entry **hp, *p;
+
+ if (e == NULL)
+ return;
+ for (hp = &t->bucketPtr[e->namehash & t->mask];
+ (p = *hp) != NULL; hp = &p->next) {
+ if (p == e) {
+ *hp = p->next;
+ free((char *)p);
+ t->numEntries--;
+ return;
+ }
+ }
+ (void)write(2, "bad call to Hash_DeleteEntry\n", 29);
+ abort();
+}
+
+/*
+ *---------------------------------------------------------
+ *
+ * Hash_EnumFirst --
+ * This procedure sets things up for a complete search
+ * of all entries recorded in the hash table.
+ *
+ * Results:
+ * The return value is the address of the first entry in
+ * the hash table, or NULL if the table is empty.
+ *
+ * Side Effects:
+ * The information in searchPtr is initialized so that successive
+ * calls to Hash_Next will return successive HashEntry's
+ * from the table.
+ *
+ *---------------------------------------------------------
+ */
+
+Hash_Entry *
+Hash_EnumFirst(t, searchPtr)
+ Hash_Table *t; /* Table to be searched. */
+ register Hash_Search *searchPtr;/* Area in which to keep state
+ * about search.*/
+{
+ searchPtr->tablePtr = t;
+ searchPtr->nextIndex = 0;
+ searchPtr->hashEntryPtr = NULL;
+ return Hash_EnumNext(searchPtr);
+}
+
+/*
+ *---------------------------------------------------------
+ *
+ * Hash_EnumNext --
+ * This procedure returns successive entries in the hash table.
+ *
+ * Results:
+ * The return value is a pointer to the next HashEntry
+ * in the table, or NULL when the end of the table is
+ * reached.
+ *
+ * Side Effects:
+ * The information in searchPtr is modified to advance to the
+ * next entry.
+ *
+ *---------------------------------------------------------
+ */
+
+Hash_Entry *
+Hash_EnumNext(searchPtr)
+ register Hash_Search *searchPtr; /* Area used to keep state about
+ search. */
+{
+ register Hash_Entry *e;
+ Hash_Table *t = searchPtr->tablePtr;
+
+ /*
+ * The hashEntryPtr field points to the most recently returned
+ * entry, or is nil if we are starting up. If not nil, we have
+ * to start at the next one in the chain.
+ */
+ e = searchPtr->hashEntryPtr;
+ if (e != NULL)
+ e = e->next;
+ /*
+ * If the chain ran out, or if we are starting up, we need to
+ * find the next nonempty chain.
+ */
+ while (e == NULL) {
+ if (searchPtr->nextIndex >= t->size)
+ return (NULL);
+ e = t->bucketPtr[searchPtr->nextIndex++];
+ }
+ searchPtr->hashEntryPtr = e;
+ return (e);
+}
+
+/*
+ *---------------------------------------------------------
+ *
+ * RebuildTable --
+ * This local routine makes a new hash table that
+ * is larger than the old one.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The entire hash table is moved, so any bucket numbers
+ * from the old table are invalid.
+ *
+ *---------------------------------------------------------
+ */
+
+static void
+RebuildTable(t)
+ register Hash_Table *t;
+{
+ register Hash_Entry *e, *next = NULL, **hp, **xp;
+ register int i, mask;
+ register Hash_Entry **oldhp;
+ int oldsize;
+
+ oldhp = t->bucketPtr;
+ oldsize = i = t->size;
+ i <<= 1;
+ t->size = i;
+ t->mask = mask = i - 1;
+ t->bucketPtr = hp = (struct Hash_Entry **) emalloc(sizeof(*hp) * i);
+ while (--i >= 0)
+ *hp++ = NULL;
+ for (hp = oldhp, i = oldsize; --i >= 0;) {
+ for (e = *hp++; e != NULL; e = next) {
+ next = e->next;
+ xp = &t->bucketPtr[e->namehash & mask];
+ e->next = *xp;
+ *xp = e;
+ }
+ }
+ free((char *)oldhp);
+}
diff --git a/sbin/rcorder/hash.h b/sbin/rcorder/hash.h
new file mode 100644
index 0000000..cf15256
--- /dev/null
+++ b/sbin/rcorder/hash.h
@@ -0,0 +1,130 @@
+/* $NetBSD: hash.h,v 1.1.1.1 1999/11/19 04:30:56 mrg Exp $ */
+
+/*
+ * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
+ * Copyright (c) 1988, 1989 by Adam de Boor
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * 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 the University of
+ * California, Berkeley and its contributors.
+ * 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 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: @(#)hash.h 8.1 (Berkeley) 6/6/93
+ */
+
+/* hash.h --
+ *
+ * This file contains definitions used by the hash module,
+ * which maintains hash tables.
+ */
+
+#ifndef _HASH
+#define _HASH
+
+/*
+ * The following defines one entry in the hash table.
+ */
+
+typedef struct Hash_Entry {
+ struct Hash_Entry *next; /* Used to link together all the
+ * entries associated with the same
+ * bucket. */
+ ClientData clientData; /* Arbitrary piece of data associated
+ * with key. */
+ unsigned namehash; /* hash value of key */
+ char name[1]; /* key string */
+} Hash_Entry;
+
+typedef struct Hash_Table {
+ struct Hash_Entry **bucketPtr;
+ /* Pointers to Hash_Entry, one
+ * for each bucket in the table. */
+ int size; /* Actual size of array. */
+ int numEntries; /* Number of entries in the table. */
+ int mask; /* Used to select bits for hashing. */
+} Hash_Table;
+
+/*
+ * The following structure is used by the searching routines
+ * to record where we are in the search.
+ */
+
+typedef struct Hash_Search {
+ Hash_Table *tablePtr; /* Table being searched. */
+ int nextIndex; /* Next bucket to check (after
+ * current). */
+ Hash_Entry *hashEntryPtr; /* Next entry to check in current
+ * bucket. */
+} Hash_Search;
+
+/*
+ * Macros.
+ */
+
+/*
+ * ClientData Hash_GetValue(h)
+ * Hash_Entry *h;
+ */
+
+#define Hash_GetValue(h) ((h)->clientData)
+
+/*
+ * Hash_SetValue(h, val);
+ * Hash_Entry *h;
+ * char *val;
+ */
+
+#define Hash_SetValue(h, val) ((h)->clientData = (ClientData) (val))
+
+#ifdef ORDER
+/*
+ * Hash_GetKey(h);
+ * Hash_Entry *h;
+ */
+
+#define Hash_GetKey(h) ((h)->name)
+#endif /* ORDER */
+
+/*
+ * Hash_Size(n) returns the number of words in an object of n bytes
+ */
+
+#define Hash_Size(n) (((n) + sizeof (int) - 1) / sizeof (int))
+
+void Hash_InitTable __P((Hash_Table *, int));
+void Hash_DeleteTable __P((Hash_Table *));
+Hash_Entry *Hash_FindEntry __P((Hash_Table *, char *));
+Hash_Entry *Hash_CreateEntry __P((Hash_Table *, char *, Boolean *));
+void Hash_DeleteEntry __P((Hash_Table *, Hash_Entry *));
+Hash_Entry *Hash_EnumFirst __P((Hash_Table *, Hash_Search *));
+Hash_Entry *Hash_EnumNext __P((Hash_Search *));
+
+#endif /* _HASH */
diff --git a/sbin/rcorder/rcorder.8 b/sbin/rcorder/rcorder.8
new file mode 100644
index 0000000..6a0c570
--- /dev/null
+++ b/sbin/rcorder/rcorder.8
@@ -0,0 +1,167 @@
+.\" $NetBSD: rcorder.8,v 1.3 2000/07/17 14:16:22 mrg Exp $
+.\"
+.\" Copyright (c) 1998
+.\" Perry E. Metzger. 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 for the NetBSD Project
+.\" by Perry E. Metzger.
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 17, 2000
+.Dt RCORDER 8
+.Os
+.Sh NAME
+.Nm rcorder
+.Nd print a dependency ordering of interdependent files
+.Sh SYNOPSIS
+.Nm
+.Op Fl k Ar keep
+.Op Fl s Ar skip
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility is designed to print out a dependency ordering of a set of
+interdependent files.
+Typically it is used to find an execution
+sequence for a set of shell scripts in which certain files must be
+executed before others.
+.Pp
+Each file passed to
+.Nm
+must be annotated with special lines (which look like comments to the
+shell) which indicate the dependencies the files have upon certain
+points in the sequence, known as
+.Dq conditions ,
+and which indicate, for each file, which
+.Dq conditions
+may be expected to be filled by that file.
+.Pp
+Within each file, a block containing a series of
+.Dq Li REQUIRE ,
+.Dq Li PROVIDE ,
+.Dq Li BEFORE
+and
+.Dq Li KEYWORD
+lines must appear.
+The format of the lines is rigid.
+Each line must begin with a single
+.Ql # ,
+followed by a single space, followed by
+.Dq Li PROVIDE: ,
+.Dq Li REQUIRE: ,
+.Dq Li BEFORE: ,
+or
+.Dq Li KEYWORD: .
+No deviation is permitted.
+Each dependency line is then followed by a series of conditions,
+separated by whitespace.
+Multiple
+.Dq Li PROVIDE ,
+.Dq Li REQUIRE ,
+.Dq Li BEFORE
+and
+.Dq Li KEYWORD
+lines may appear, but all such lines must appear in a sequence without
+any intervening lines, as once a line that does not follow the format
+is reached, parsing stops.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl k
+Add the specified keyword to the
+.Dq "keep list" .
+If any
+.Fl k
+option is given, only those files containing the matching keyword are listed.
+.It Fl s
+Add the specified keyword to the
+.Dq "skip list" .
+If any
+.Fl s
+option is given, files containing the matching keyword are not listed.
+.El
+.Pp
+An example block follows:
+.Bd -literal -offset indent
+# REQUIRE: networking syslog
+# REQUIRE: usr
+# PROVIDE: dns nscd
+.Ed
+.Pp
+This block states that the file in which it appears depends upon the
+.Dq Li networking ,
+.Dq Li syslog ,
+and
+.Dq Li usr
+conditions, and provides the
+.Dq Li dns
+and
+.Dq Li nscd
+conditions.
+.Pp
+A file may contain zero
+.Dq Li PROVIDE
+lines, in which case it provides no conditions, and may contain zero
+.Dq Li REQUIRE
+lines, in which case it has no dependencies.
+There must be at least one file with no dependencies in the set of
+arguments passed to
+.Nm
+in order for it to find a starting place in the dependency ordering.
+.Sh DIAGNOSTICS
+The
+.Nm
+utility may print one of the following error messages and exit with a non-zero
+status if it encounters an error while processing the file list.
+.Bl -diag
+.It "Requirement %s has no providers, aborting."
+No file has a
+.Dq Li PROVIDE
+line corresponding to a condition present in a
+.Dq Li REQUIRE
+line in another file.
+.It "Circular dependency on provision %s, aborting."
+A set of files has a circular dependency which was detected while
+processing the stated condition.
+.It "Circular dependency on file %s, aborting."
+A set of files has a circular dependency which was detected while
+processing the stated file.
+.El
+.Sh SEE ALSO
+.Xr rc 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Nx 1.5 .
+.Sh AUTHORS
+.An -nosplit
+Written by
+.An Perry E. Metzger Aq perry@piermont.com
+and
+.An Matthew R. Green Aq mrg@eterna.com.au .
diff --git a/sbin/rcorder/rcorder.c b/sbin/rcorder/rcorder.c
new file mode 100644
index 0000000..14a7707
--- /dev/null
+++ b/sbin/rcorder/rcorder.c
@@ -0,0 +1,828 @@
+# if 0
+/* $NetBSD: rcorder.c,v 1.7 2000/08/04 07:33:55 enami Exp $ */
+#endif
+
+/*
+ * Copyright (c) 1998, 1999 Matthew R. Green
+ * All rights reserved.
+ * Copyright (c) 1998
+ * Perry E. Metzger. 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 for the NetBSD Project
+ * by Perry E. Metzger.
+ * 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.
+ */
+
+#include <sys/types.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stat.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "ealloc.h"
+#include "sprite.h"
+#include "hash.h"
+
+#ifdef DEBUG
+int debug = 0;
+# define DPRINTF(args) if (debug) { fflush(stdout); fprintf args; }
+#else
+# define DPRINTF(args)
+#endif
+
+#define REQUIRE_STR "# REQUIRE:"
+#define REQUIRE_LEN (sizeof(REQUIRE_STR) - 1)
+#define REQUIRES_STR "# REQUIRES:"
+#define REQUIRES_LEN (sizeof(REQUIRES_STR) - 1)
+#define PROVIDE_STR "# PROVIDE:"
+#define PROVIDE_LEN (sizeof(PROVIDE_STR) - 1)
+#define PROVIDES_STR "# PROVIDES:"
+#define PROVIDES_LEN (sizeof(PROVIDES_STR) - 1)
+#define BEFORE_STR "# BEFORE:"
+#define BEFORE_LEN (sizeof(BEFORE_STR) - 1)
+#define KEYWORD_STR "# KEYWORD:"
+#define KEYWORD_LEN (sizeof(KEYWORD_STR) - 1)
+#define KEYWORDS_STR "# KEYWORDS:"
+#define KEYWORDS_LEN (sizeof(KEYWORDS_STR) - 1)
+
+int exit_code;
+int file_count;
+char **file_list;
+
+typedef int bool;
+#define TRUE 1
+#define FALSE 0
+typedef bool flag;
+#define SET TRUE
+#define RESET FALSE
+
+Hash_Table provide_hash_s, *provide_hash;
+
+typedef struct provnode provnode;
+typedef struct filenode filenode;
+typedef struct f_provnode f_provnode;
+typedef struct f_reqnode f_reqnode;
+typedef struct strnodelist strnodelist;
+
+struct provnode {
+ flag head;
+ flag in_progress;
+ filenode *fnode;
+ provnode *next, *last;
+};
+
+struct f_provnode {
+ provnode *pnode;
+ f_provnode *next;
+};
+
+struct f_reqnode {
+ Hash_Entry *entry;
+ f_reqnode *next;
+};
+
+struct strnodelist {
+ filenode *node;
+ strnodelist *next;
+ char s[1];
+};
+
+struct filenode {
+ char *filename;
+ flag in_progress;
+ filenode *next, *last;
+ f_reqnode *req_list;
+ f_provnode *prov_list;
+ strnodelist *keyword_list;
+};
+
+filenode fn_head_s, *fn_head;
+
+strnodelist *bl_list;
+strnodelist *keep_list;
+strnodelist *skip_list;
+
+void do_file __P((filenode *fnode));
+void strnode_add __P((strnodelist **, char *, filenode *));
+int skip_ok __P((filenode *fnode));
+int keep_ok __P((filenode *fnode));
+void satisfy_req __P((f_reqnode *rnode, char *filename));
+void crunch_file __P((char *));
+void parse_require __P((filenode *, char *));
+void parse_provide __P((filenode *, char *));
+void parse_before __P((filenode *, char *));
+void parse_keywords __P((filenode *, char *));
+filenode *filenode_new __P((char *));
+void add_require __P((filenode *, char *));
+void add_provide __P((filenode *, char *));
+void add_before __P((filenode *, char *));
+void add_keyword __P((filenode *, char *));
+void insert_before __P((void));
+Hash_Entry *make_fake_provision __P((filenode *));
+void crunch_all_files __P((void));
+void initialize __P((void));
+void generate_ordering __P((void));
+int main __P((int, char *[]));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+
+ while ((ch = getopt(argc, argv, "dk:s:")) != -1)
+ switch (ch) {
+ case 'd':
+#ifdef DEBUG
+ debug = 1;
+#else
+ warnx("debugging not compiled in, -d ignored");
+#endif
+ break;
+ case 'k':
+ strnode_add(&keep_list, optarg, 0);
+ break;
+ case 's':
+ strnode_add(&skip_list, optarg, 0);
+ break;
+ default:
+ /* XXX should crunch it? */
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ file_count = argc;
+ file_list = argv;
+
+ DPRINTF((stderr, "parse_args\n"));
+ initialize();
+ DPRINTF((stderr, "initialize\n"));
+ crunch_all_files();
+ DPRINTF((stderr, "crunch_all_files\n"));
+ generate_ordering();
+ DPRINTF((stderr, "generate_ordering\n"));
+
+ exit(exit_code);
+}
+
+/*
+ * initialise various variables.
+ */
+void
+initialize()
+{
+
+ fn_head = &fn_head_s;
+
+ provide_hash = &provide_hash_s;
+ Hash_InitTable(provide_hash, file_count);
+}
+
+/* generic function to insert a new strnodelist element */
+void
+strnode_add(listp, s, fnode)
+ strnodelist **listp;
+ char *s;
+ filenode *fnode;
+{
+ strnodelist *ent;
+
+ ent = emalloc(sizeof *ent + strlen(s));
+ ent->node = fnode;
+ strcpy(ent->s, s);
+ ent->next = *listp;
+ *listp = ent;
+}
+
+/*
+ * below are the functions that deal with creating the lists
+ * from the filename's given and the dependancies and provisions
+ * in each of these files. no ordering or checking is done here.
+ */
+
+/*
+ * we have a new filename, create a new filenode structure.
+ * fill in the bits, and put it in the filenode linked list
+ */
+filenode *
+filenode_new(filename)
+ char *filename;
+{
+ filenode *temp;
+
+ temp = emalloc(sizeof(*temp));
+ memset(temp, 0, sizeof(*temp));
+ temp->filename = estrdup(filename);
+ temp->req_list = NULL;
+ temp->prov_list = NULL;
+ temp->keyword_list = NULL;
+ temp->in_progress = RESET;
+ /*
+ * link the filenode into the list of filenodes.
+ * note that the double linking means we can delete a
+ * filenode without searching for where it belongs.
+ */
+ temp->next = fn_head->next;
+ if (temp->next != NULL)
+ temp->next->last = temp;
+ temp->last = fn_head;
+ fn_head->next = temp;
+ return (temp);
+}
+
+/*
+ * add a requirement to a filenode.
+ */
+void
+add_require(fnode, s)
+ filenode *fnode;
+ char *s;
+{
+ Hash_Entry *entry;
+ f_reqnode *rnode;
+ int new;
+
+ entry = Hash_CreateEntry(provide_hash, s, &new);
+ if (new)
+ Hash_SetValue(entry, NULL);
+ rnode = emalloc(sizeof(*rnode));
+ rnode->entry = entry;
+ rnode->next = fnode->req_list;
+ fnode->req_list = rnode;
+}
+
+/*
+ * add a provision to a filenode. if this provision doesn't
+ * have a head node, create one here.
+ */
+void
+add_provide(fnode, s)
+ filenode *fnode;
+ char *s;
+{
+ Hash_Entry *entry;
+ f_provnode *f_pnode;
+ provnode *pnode, *head;
+ int new;
+
+ entry = Hash_CreateEntry(provide_hash, s, &new);
+ head = Hash_GetValue(entry);
+
+ /* create a head node if necessary. */
+ if (head == NULL) {
+ head = emalloc(sizeof(*head));
+ head->head = SET;
+ head->in_progress = RESET;
+ head->fnode = NULL;
+ head->last = head->next = NULL;
+ Hash_SetValue(entry, head);
+ }
+#if 0
+ /*
+ * Don't warn about this. We want to be able to support
+ * scripts that do two complex things:
+ *
+ * - Two independent scripts which both provide the
+ * same thing. Both scripts must be executed in
+ * any order to meet the barrier. An example:
+ *
+ * Script 1:
+ *
+ * PROVIDE: mail
+ * REQUIRE: LOGIN
+ *
+ * Script 2:
+ *
+ * PROVIDE: mail
+ * REQUIRE: LOGIN
+ *
+ * - Two interdependent scripts which both provide the
+ * same thing. Both scripts must be executed in
+ * graph order to meet the barrier. An example:
+ *
+ * Script 1:
+ *
+ * PROVIDE: nameservice dnscache
+ * REQUIRE: SERVERS
+ *
+ * Script 2:
+ *
+ * PROVIDE: nameservice nscd
+ * REQUIRE: dnscache
+ */
+ else if (new == 0) {
+ warnx("file `%s' provides `%s'.", fnode->filename, s);
+ warnx("\tpreviously seen in `%s'.",
+ head->next->fnode->filename);
+ }
+#endif
+
+ pnode = emalloc(sizeof(*pnode));
+ pnode->head = RESET;
+ pnode->in_progress = RESET;
+ pnode->fnode = fnode;
+ pnode->next = head->next;
+ pnode->last = head;
+ head->next = pnode;
+ if (pnode->next != NULL)
+ pnode->next->last = pnode;
+
+ f_pnode = emalloc(sizeof(*f_pnode));
+ f_pnode->pnode = pnode;
+ f_pnode->next = fnode->prov_list;
+ fnode->prov_list = f_pnode;
+}
+
+/*
+ * put the BEFORE: lines to a list and handle them later.
+ */
+void
+add_before(fnode, s)
+ filenode *fnode;
+ char *s;
+{
+ strnodelist *bf_ent;
+
+ bf_ent = emalloc(sizeof *bf_ent + strlen(s));
+ bf_ent->node = fnode;
+ strcpy(bf_ent->s, s);
+ bf_ent->next = bl_list;
+ bl_list = bf_ent;
+}
+
+/*
+ * add a key to a filenode.
+ */
+void
+add_keyword(fnode, s)
+ filenode *fnode;
+ char *s;
+{
+
+ strnode_add(&fnode->keyword_list, s, fnode);
+}
+
+/*
+ * loop over the rest of a REQUIRE line, giving each word to
+ * add_require() to do the real work.
+ */
+void
+parse_require(node, buffer)
+ filenode *node;
+ char *buffer;
+{
+ char *s;
+
+ while ((s = strsep(&buffer, " \t\n")) != NULL)
+ if (*s != '\0')
+ add_require(node, s);
+}
+
+/*
+ * loop over the rest of a PROVIDE line, giving each word to
+ * add_provide() to do the real work.
+ */
+void
+parse_provide(node, buffer)
+ filenode *node;
+ char *buffer;
+{
+ char *s;
+
+ while ((s = strsep(&buffer, " \t\n")) != NULL)
+ if (*s != '\0')
+ add_provide(node, s);
+}
+
+/*
+ * loop over the rest of a BEFORE line, giving each word to
+ * add_before() to do the real work.
+ */
+void
+parse_before(node, buffer)
+ filenode *node;
+ char *buffer;
+{
+ char *s;
+
+ while ((s = strsep(&buffer, " \t\n")) != NULL)
+ if (*s != '\0')
+ add_before(node, s);
+}
+
+/*
+ * loop over the rest of a KEYWORD line, giving each word to
+ * add_keyword() to do the real work.
+ */
+void
+parse_keywords(node, buffer)
+ filenode *node;
+ char *buffer;
+{
+ char *s;
+
+ while ((s = strsep(&buffer, " \t\n")) != NULL)
+ if (*s != '\0')
+ add_keyword(node, s);
+}
+
+/*
+ * given a file name, create a filenode for it, read in lines looking
+ * for provision and requirement lines, building the graphs as needed.
+ */
+void
+crunch_file(filename)
+ char *filename;
+{
+ FILE *fp;
+ char *buf;
+ int require_flag, provide_flag, before_flag, keywords_flag;
+ enum { BEFORE_PARSING, PARSING, PARSING_DONE } state;
+ filenode *node;
+ char delims[3] = { '\\', '\\', '\0' };
+ struct stat st;
+
+ if ((fp = fopen(filename, "r")) == NULL) {
+ warn("could not open %s", filename);
+ return;
+ }
+
+ if (fstat(fileno(fp), &st) == -1) {
+ warn("could not stat %s", filename);
+ fclose(fp);
+ return;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+#if 0
+ warnx("%s is not a file", filename);
+#endif
+ fclose(fp);
+ return;
+ }
+
+ node = filenode_new(filename);
+
+ /*
+ * we don't care about length, line number, don't want # for comments,
+ * and have no flags.
+ */
+ for (state = BEFORE_PARSING; state != PARSING_DONE &&
+ (buf = fparseln(fp, NULL, NULL, delims, 0)) != NULL; free(buf)) {
+ require_flag = provide_flag = before_flag = keywords_flag = 0;
+ if (strncmp(REQUIRE_STR, buf, REQUIRE_LEN) == 0)
+ require_flag = REQUIRE_LEN;
+ else if (strncmp(REQUIRES_STR, buf, REQUIRES_LEN) == 0)
+ require_flag = REQUIRES_LEN;
+ else if (strncmp(PROVIDE_STR, buf, PROVIDE_LEN) == 0)
+ provide_flag = PROVIDE_LEN;
+ else if (strncmp(PROVIDES_STR, buf, PROVIDES_LEN) == 0)
+ provide_flag = PROVIDES_LEN;
+ else if (strncmp(BEFORE_STR, buf, BEFORE_LEN) == 0)
+ before_flag = BEFORE_LEN;
+ else if (strncmp(KEYWORD_STR, buf, KEYWORD_LEN) == 0)
+ keywords_flag = KEYWORD_LEN;
+ else if (strncmp(KEYWORDS_STR, buf, KEYWORDS_LEN) == 0)
+ keywords_flag = KEYWORDS_LEN;
+ else {
+ if (state == PARSING)
+ state = PARSING_DONE;
+ continue;
+ }
+
+ state = PARSING;
+ if (require_flag)
+ parse_require(node, buf + require_flag);
+ else if (provide_flag)
+ parse_provide(node, buf + provide_flag);
+ else if (before_flag)
+ parse_before(node, buf + before_flag);
+ else if (keywords_flag)
+ parse_keywords(node, buf + keywords_flag);
+ }
+ fclose(fp);
+}
+
+Hash_Entry *
+make_fake_provision(node)
+ filenode *node;
+{
+ Hash_Entry *entry;
+ f_provnode *f_pnode;
+ provnode *head, *pnode;
+ static int i = 0;
+ int new;
+ char buffer[30];
+
+ do {
+ snprintf(buffer, sizeof buffer, "fake_prov_%08d", i++);
+ entry = Hash_CreateEntry(provide_hash, buffer, &new);
+ } while (new == 0);
+ head = emalloc(sizeof(*head));
+ head->head = SET;
+ head->in_progress = RESET;
+ head->fnode = NULL;
+ head->last = head->next = NULL;
+ Hash_SetValue(entry, head);
+
+ pnode = emalloc(sizeof(*pnode));
+ pnode->head = RESET;
+ pnode->in_progress = RESET;
+ pnode->fnode = node;
+ pnode->next = head->next;
+ pnode->last = head;
+ head->next = pnode;
+ if (pnode->next != NULL)
+ pnode->next->last = pnode;
+
+ f_pnode = emalloc(sizeof(*f_pnode));
+ f_pnode->pnode = pnode;
+ f_pnode->next = node->prov_list;
+ node->prov_list = f_pnode;
+
+ return (entry);
+}
+
+/*
+ * go through the BEFORE list, inserting requirements into the graph(s)
+ * as required. in the before list, for each entry B, we have a file F
+ * and a string S. we create a "fake" provision (P) that F provides.
+ * for each entry in the provision list for S, add a requirement to
+ * that provisions filenode for P.
+ */
+void
+insert_before()
+{
+ Hash_Entry *entry, *fake_prov_entry;
+ provnode *pnode;
+ f_reqnode *rnode;
+ strnodelist *bl;
+ int new;
+
+ while (bl_list != NULL) {
+ bl = bl_list->next;
+
+ fake_prov_entry = make_fake_provision(bl_list->node);
+
+ entry = Hash_CreateEntry(provide_hash, bl_list->s, &new);
+ if (new == 1)
+ warnx("file `%s' is before unknown provision `%s'", bl_list->node->filename, bl_list->s);
+
+ for (pnode = Hash_GetValue(entry); pnode; pnode = pnode->next) {
+ if (pnode->head)
+ continue;
+
+ rnode = emalloc(sizeof(*rnode));
+ rnode->entry = fake_prov_entry;
+ rnode->next = pnode->fnode->req_list;
+ pnode->fnode->req_list = rnode;
+ }
+
+ free(bl_list);
+ bl_list = bl;
+ }
+}
+
+/*
+ * loop over all the files calling crunch_file() on them to do the
+ * real work. after we have built all the nodes, insert the BEFORE:
+ * lines into graph(s).
+ */
+void
+crunch_all_files()
+{
+ int i;
+
+ for (i = 0; i < file_count; i++)
+ crunch_file(file_list[i]);
+ insert_before();
+}
+
+/*
+ * below are the functions that traverse the graphs we have built
+ * finding out the desired ordering, printing each file in turn.
+ * if missing requirements, or cyclic graphs are detected, a
+ * warning will be issued, and we will continue on..
+ */
+
+/*
+ * given a requirement node (in a filename) we attempt to satisfy it.
+ * we do some sanity checking first, to ensure that we have providers,
+ * aren't already satisfied and aren't already being satisfied (ie,
+ * cyclic). if we pass all this, we loop over the provision list
+ * calling do_file() (enter recursion) for each filenode in this
+ * provision.
+ */
+void
+satisfy_req(rnode, filename)
+ f_reqnode *rnode;
+ char *filename;
+{
+ Hash_Entry *entry;
+ provnode *head;
+
+ entry = rnode->entry;
+ head = Hash_GetValue(entry);
+
+ if (head == NULL) {
+ warnx("requirement `%s' in file `%s' has no providers.",
+ Hash_GetKey(entry), filename);
+ exit_code = 1;
+ return;
+ }
+
+ /* return if the requirement is already satisfied. */
+ if (head->next == NULL)
+ return;
+
+ /*
+ * if list is marked as in progress,
+ * print that there is a circular dependency on it and abort
+ */
+ if (head->in_progress == SET) {
+ warnx("Circular dependency on provision `%s' in file `%s'.",
+ Hash_GetKey(entry), filename);
+ exit_code = 1;
+ return;
+ }
+
+ head->in_progress = SET;
+
+ /*
+ * while provision_list is not empty
+ * do_file(first_member_of(provision_list));
+ */
+ while (head->next != NULL)
+ do_file(head->next->fnode);
+}
+
+int
+skip_ok(fnode)
+ filenode *fnode;
+{
+ strnodelist *s;
+ strnodelist *k;
+
+ for (s = skip_list; s; s = s->next)
+ for (k = fnode->keyword_list; k; k = k->next)
+ if (strcmp(k->s, s->s) == 0)
+ return (0);
+
+ return (1);
+}
+
+int
+keep_ok(fnode)
+ filenode *fnode;
+{
+ strnodelist *s;
+ strnodelist *k;
+
+ for (s = keep_list; s; s = s->next)
+ for (k = fnode->keyword_list; k; k = k->next)
+ if (strcmp(k->s, s->s) == 0)
+ return (1);
+
+ /* an empty keep_list means every one */
+ return (!keep_list);
+}
+
+/*
+ * given a filenode, we ensure we are not a cyclic graph. if this
+ * is ok, we loop over the filenodes requirements, calling satisfy_req()
+ * for each of them.. once we have done this, remove this filenode
+ * from each provision table, as we are now done.
+ */
+void
+do_file(fnode)
+ filenode *fnode;
+{
+ f_reqnode *r, *r_tmp;
+ f_provnode *p, *p_tmp;
+ provnode *pnode;
+ int was_set;
+
+ DPRINTF((stderr, "do_file on %s.\n", fnode->filename));
+
+ /*
+ * if fnode is marked as in progress,
+ * print that fnode; is circularly depended upon and abort.
+ */
+ if (fnode->in_progress == SET) {
+ warnx("Circular dependency on file `%s'.",
+ fnode->filename);
+ was_set = exit_code = 1;
+ } else
+ was_set = 0;
+
+ /* mark fnode */
+ fnode->in_progress = SET;
+
+ /*
+ * for each requirement of fnode -> r
+ * satisfy_req(r, filename)
+ */
+ r = fnode->req_list;
+ while (r != NULL) {
+ r_tmp = r;
+ satisfy_req(r, fnode->filename);
+ r = r->next;
+ if (was_set == 0)
+ free(r_tmp);
+ }
+ fnode->req_list = NULL;
+
+ /*
+ * for each provision of fnode -> p
+ * remove fnode from provision list for p in hash table
+ */
+ p = fnode->prov_list;
+ while (p != NULL) {
+ p_tmp = p;
+ pnode = p->pnode;
+ if (pnode->next != NULL) {
+ pnode->next->last = pnode->last;
+ }
+ if (pnode->last != NULL) {
+ pnode->last->next = pnode->next;
+ }
+ free(pnode);
+ p = p->next;
+ free(p_tmp);
+ }
+ fnode->prov_list = NULL;
+
+ /* do_it(fnode) */
+ DPRINTF((stderr, "next do: "));
+
+ /* if we were already in progress, don't print again */
+ if (was_set == 0 && skip_ok(fnode) && keep_ok(fnode))
+ printf("%s\n", fnode->filename);
+
+ if (fnode->next != NULL) {
+ fnode->next->last = fnode->last;
+ }
+ if (fnode->last != NULL) {
+ fnode->last->next = fnode->next;
+ }
+
+ DPRINTF((stderr, "nuking %s\n", fnode->filename));
+ if (was_set == 0) {
+ free(fnode->filename);
+ free(fnode);
+ }
+}
+
+void
+generate_ordering()
+{
+
+ /*
+ * while there remain undone files{f},
+ * pick an arbitrary f, and do_file(f)
+ * Note that the first file in the file list is perfectly
+ * arbitrary, and easy to find, so we use that.
+ */
+
+ /*
+ * N.B.: the file nodes "self delete" after they execute, so
+ * after each iteration of the loop, the head will be pointing
+ * to something totally different. The loop ends up being
+ * executed only once for every strongly connected set of
+ * nodes.
+ */
+ while (fn_head->next != NULL) {
+ DPRINTF((stderr, "generate on %s\n", fn_head->next->filename));
+ do_file(fn_head->next);
+ }
+}
diff --git a/sbin/rcorder/sprite.h b/sbin/rcorder/sprite.h
new file mode 100644
index 0000000..5e5d7f3
--- /dev/null
+++ b/sbin/rcorder/sprite.h
@@ -0,0 +1,113 @@
+/* $NetBSD: sprite.h,v 1.1 1999/11/23 05:28:22 mrg Exp $ */
+
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * 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 the University of
+ * California, Berkeley and its contributors.
+ * 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 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: @(#)sprite.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * sprite.h --
+ *
+ * Common constants and type declarations for Sprite.
+ */
+
+#ifndef _SPRITE
+#define _SPRITE
+
+
+/*
+ * A boolean type is defined as an integer, not an enum. This allows a
+ * boolean argument to be an expression that isn't strictly 0 or 1 valued.
+ */
+
+typedef int Boolean;
+#ifndef TRUE
+#define TRUE 1
+#endif /* TRUE */
+#ifndef FALSE
+#define FALSE 0
+#endif /* FALSE */
+
+/*
+ * Functions that must return a status can return a ReturnStatus to
+ * indicate success or type of failure.
+ */
+
+typedef int ReturnStatus;
+
+/*
+ * The following statuses overlap with the first 2 generic statuses
+ * defined in status.h:
+ *
+ * SUCCESS There was no error.
+ * FAILURE There was a general error.
+ */
+
+#define SUCCESS 0x00000000
+#define FAILURE 0x00000001
+
+
+/*
+ * A nil pointer must be something that will cause an exception if
+ * referenced. There are two nils: the kernels nil and the nil used
+ * by user processes.
+ */
+
+#define NIL ~0
+#define USER_NIL 0
+#ifndef NULL
+#define NULL 0
+#endif /* NULL */
+
+/*
+ * An address is just a pointer in C. It is defined as a character pointer
+ * so that address arithmetic will work properly, a byte at a time.
+ */
+
+typedef char *Address;
+
+/*
+ * ClientData is an uninterpreted word. It is defined as an int so that
+ * kdbx will not interpret client data as a string. Unlike an "Address",
+ * client data will generally not be used in arithmetic.
+ * But we don't have kdbx anymore so we define it as void (christos)
+ */
+
+typedef void *ClientData;
+
+#endif /* _SPRITE */
diff --git a/sbin/reboot/Makefile b/sbin/reboot/Makefile
new file mode 100644
index 0000000..42f0868
--- /dev/null
+++ b/sbin/reboot/Makefile
@@ -0,0 +1,25 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= reboot
+WARNS?= 6
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+MAN= reboot.8 nextboot.8
+MLINKS= reboot.8 halt.8 reboot.8 fastboot.8 reboot.8 fasthalt.8
+
+.if exists(${.CURDIR}/boot_${MACHINE}.8)
+MAN+= boot_${MACHINE}.8
+MLINKS+= boot_${MACHINE}.8 boot.8
+.endif
+.if ${MACHINE} == "amd64"
+MAN+= boot_i386.8
+MLINKS+= boot_i386.8 boot.8
+.endif
+
+LINKS= ${BINDIR}/reboot ${BINDIR}/halt ${BINDIR}/reboot ${BINDIR}/fastboot \
+ ${BINDIR}/reboot ${BINDIR}/fasthalt
+
+SCRIPTS= nextboot.sh
+
+.include <bsd.prog.mk>
diff --git a/sbin/reboot/boot_i386.8 b/sbin/reboot/boot_i386.8
new file mode 100644
index 0000000..bc1560d
--- /dev/null
+++ b/sbin/reboot/boot_i386.8
@@ -0,0 +1,381 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software written and contributed
+.\" to Berkeley by William Jolitz.
+.\"
+.\" Almost completely rewritten for FreeBSD 2.1 by Joerg Wunsch.
+.\"
+.\" Substantially revised for FreeBSD 3.1 by Robert Nordier.
+.\"
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)boot_i386.8 8.2 (Berkeley) 4/19/94
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 18, 2005
+.Dt BOOT 8 i386
+.Os
+.Sh NAME
+.Nm boot
+.Nd system bootstrapping procedures
+.Sh DESCRIPTION
+.Sy Power fail and crash recovery .
+Normally, the system will reboot itself at power-up or after crashes.
+An automatic consistency check of the file systems will be performed,
+and unless this fails, the system will resume multi-user operations.
+.Pp
+.Sy Cold starts .
+Most i386 PCs attempt to boot first from floppy disk drive 0 (sometimes
+known as drive A:) and, failing that, from hard disk drive 0 (sometimes
+known as drive C:, or as drive 0x80 to the BIOS).
+Some BIOSes allow
+you to change this default sequence, and may also include a CD-ROM
+drive as a boot device.
+.Pp
+By default, a three-stage bootstrap is employed, and control is
+automatically passed from the boot blocks (bootstrap stages one and
+two) to a separate third-stage bootstrap program,
+.Xr loader 8 .
+This third stage provides more sophisticated control over the booting
+process than it is possible to achieve in the boot blocks, which are
+constrained by occupying limited fixed space on a given disk or slice.
+.Pp
+However, it is possible to dispense with the third stage altogether,
+either by specifying a kernel name in the boot block parameter
+file,
+.Pa /boot.config ,
+or, unless option
+.Fl n
+is set, by hitting a key during a brief pause (while one of the characters
+.Sy - ,
+.Sy \e ,
+.Sy \&| ,
+or
+.Sy /
+is displayed) before
+.Xr loader 8
+is invoked.
+Booting will also be attempted at stage two, if the
+third stage cannot be loaded.
+.Pp
+Make note of the fact that
+.Pa /boot.config
+is read only from the
+.Ql a
+partition.
+As a result, slices which are missing an
+.Ql a
+partition require user intervention during the boot process.
+.Pp
+The remainder of this subsection deals only with the boot blocks.
+The
+.Xr loader 8
+program is documented separately.
+.Pp
+After the boot blocks have been loaded,
+you should see a prompt similar to the following:
+.Bd -literal
+>> FreeBSD/i386 BOOT
+Default: 0:ad(0,a)/boot/loader
+boot:
+.Ed
+.Pp
+The automatic boot will attempt to load
+.Pa /boot/loader
+from partition
+.Ql a
+of either the floppy or the hard disk.
+This boot may be aborted by typing any character on the keyboard
+at the
+.Ql boot:
+prompt.
+At this time, the following input will be accepted:
+.Bl -tag -width indent
+.It Ic \&?
+Give a short listing of the files in the root directory of the default
+boot device, as a hint about available boot files.
+(A
+.Ic ?\&
+may also be specified as the last segment of a path, in which case
+the listing will be of the relevant subdirectory.)
+.Pp
+.It Xo
+.Sm off
+.Ar bios_drive : interface ( unit , Oo Ar slice , Oc Ar part )
+.Ar filename
+.Sm on
+.Op Fl aCcDdghmnPprsv
+.Op Fl S Ns Ar speed
+.Xc
+Specify boot file and flags.
+.Bl -tag -width indent
+.It Ar bios_drive
+The drive number as recognized by the BIOS.
+0 for the first drive, 1 for the second drive, etc.
+.It Ar interface
+The type of controller to boot from.
+Note that the controller is required
+to have BIOS support since the BIOS services are used to load the
+boot file image.
+.Pp
+The supported interfaces are:
+.Pp
+.Bl -tag -width "adXX" -compact
+.It ad
+ST506, IDE, ESDI, RLL disks on a WD100[2367] or lookalike
+controller
+.It fd
+5 1/4" or 3 1/2" High density floppies
+.It da
+SCSI disk on any supported SCSI controller
+.\".It cd
+.\"boot from CDROM
+.El
+.It Ar unit
+The unit number of the drive on the interface being used.
+0 for the first drive, 1 for the second drive, etc.
+.It Oo Ar slice , Oc Ns Ar part
+The partition letter inside the
+.Bx
+portion of the disk.
+See
+.Xr bsdlabel 8 .
+By convention, only partition
+.Ql a
+contains a bootable image.
+If sliced disks are used
+.Pq Dq fdisk partitions ,
+any
+.Ar slice
+(1 for the first slice, 2 for the second slice, etc.\&)
+can be booted from, with the default (if not specified) being the active slice
+or, otherwise, the first
+.Fx
+slice.
+If
+.Ar slice
+is specified as 0, the first
+.Fx
+slice (also known as
+.Dq compatibility
+slice) is booted from.
+.It Ar filename
+The pathname of the file to boot (relative to the root directory
+on the specified partition).
+Defaults to
+.Pa /boot/kernel/kernel .
+Symbolic links are not supported (hard links are).
+.It Xo Op Fl aCcDdghmnPpqrsv
+.Op Fl S Ns Ar speed
+.Xc
+Boot flags:
+.Pp
+.Bl -tag -width "-CXX" -compact
+.It Fl a
+during kernel initialization,
+ask for the device to mount as the root file system.
+.It Fl C
+try to mount root file system from a CD-ROM.
+.It Fl c
+this flag is currently a no-op.
+.It Fl D
+boot with the dual console configuration.
+In the single
+configuration, the console will be either the internal display
+or the serial port, depending on the state of the
+.Fl h
+option below.
+In the dual console configuration,
+both the internal display and the serial port will become the console
+at the same time, regardless of the state of the
+.Fl h
+option.
+.It Fl d
+enter the DDB kernel debugger
+(see
+.Xr ddb 4 )
+as early as possible in kernel initialization.
+.It Fl g
+use the GDB remote debugging protocol.
+.It Fl h
+force the serial console.
+For instance, if you boot from the internal console,
+you can use the
+.Fl h
+option to force the kernel to use the serial port as its
+console device.
+The serial port driver
+.Xr sio 4
+has a flag (0x20) to override this option.
+If that flag is set, the serial port will always be used as the console,
+regardless of the
+.Fl h
+option described here.
+See the man page for
+.Xr sio 4
+for more details.
+.It Fl m
+mute the console.
+.It Fl n
+ignore key press to interrupt boot before
+.Xr loader 8
+is invoked.
+.It Fl P
+probe the keyboard.
+If no keyboard is found, the
+.Fl D
+and
+.Fl h
+options are automatically set.
+.It Fl p
+pause after each attached device during the device probing phase.
+.It Fl q
+be quiet,
+do not write anything to the console unless automatic boot fails or
+is disabled.
+This option only affects second-stage bootstrap,
+to prevent next stages from writing to the console use in
+combination with the
+.Fl m
+option.
+.It Fl r
+use the statically configured default for the device containing the
+root file system
+(see
+.Xr config 8 ) .
+Normally, the root file system is on the device
+that the kernel was loaded from.
+.It Fl s
+boot into single-user mode; if the console is marked as
+.Dq insecure
+(see
+.Xr ttys 5 ) ,
+the root password must be entered.
+.It Fl S Ns Ar speed
+set the speed of the serial console to
+.Ar speed .
+The default is 9600 unless it has been overridden by setting
+.Va BOOT_COMCONSOLE_SPEED
+in
+.Xr make.conf 5
+and recompiling and reinstalling the boot blocks.
+.It Fl v
+be verbose during device probing (and later).
+.El
+.El
+.El
+.Pp
+You may put a BIOS drive number, a controller type, a unit number,
+a partition, a kernel file name, and any valid option in
+.Pa /boot.config
+to set defaults.
+Enter them in one line just as you type at the
+.Ql boot:
+prompt.
+.Sh FILES
+.Bl -tag -width /boot/loader -compact
+.It Pa /boot.config
+parameters for the boot blocks (optional)
+.It Pa /boot/boot1
+first stage bootstrap file
+.It Pa /boot/boot2
+second stage bootstrap file
+.It Pa /boot/loader
+third stage bootstrap
+.It Pa /boot/kernel/kernel
+default kernel
+.It Pa /boot/kernel.old/kernel
+typical non-default kernel (optional)
+.El
+.Sh DIAGNOSTICS
+When disk-related errors occur, these are reported by the second-stage
+bootstrap using the same error codes returned by the BIOS, for example
+.Dq Disk error 0x1 (lba=0x12345678) .
+Here is a partial list of these error codes:
+.Pp
+.Bl -tag -width "0x80" -compact
+.It 0x1
+Invalid argument
+.It 0x2
+Address mark not found
+.It 0x4
+Sector not found
+.It 0x8
+DMA overrun
+.It 0x9
+DMA attempt across 64K boundary
+.It 0xc
+Invalid media
+.It 0x10
+Uncorrectable CRC/ECC error
+.It 0x20
+Controller failure
+.It 0x40
+Seek failed
+.It 0x80
+Timeout
+.El
+.Pp
+.Sy "NOTE" :
+On older machines, or otherwise where EDD support (disk packet
+interface support) is not available, all boot-related files and
+structures (including the kernel) that need to be accessed during the
+boot phase must reside on the disk at or below cylinder 1023 (as the
+BIOS understands the geometry).
+When a
+.Dq Disk error 0x1
+is reported by the second-stage bootstrap, it generally means that this
+requirement has not been adhered to.
+.Sh SEE ALSO
+.Xr ddb 4 ,
+.Xr make.conf 5 ,
+.Xr ttys 5 ,
+.Xr boot0cfg 8 ,
+.Xr bsdlabel 8 ,
+.Xr btxld 8 ,
+.Xr config 8 ,
+.Xr halt 8 ,
+.Xr loader 8 ,
+.Xr reboot 8 ,
+.Xr shutdown 8
+.Sh BUGS
+The
+.Xr bsdlabel 5
+format used by this version of
+.Bx
+is quite
+different from that of other architectures.
+.Pp
+Due to space constraints, the keyboard probe initiated by the
+.Fl P
+option is simply a test that the BIOS has detected an
+.Dq extended
+keyboard.
+If an
+.Dq XT/AT
+keyboard (with no F11 and F12 keys, etc.) is attached, the probe will
+fail.
diff --git a/sbin/reboot/nextboot.8 b/sbin/reboot/nextboot.8
new file mode 100644
index 0000000..765fa79
--- /dev/null
+++ b/sbin/reboot/nextboot.8
@@ -0,0 +1,125 @@
+.\" Copyright (c) 2002 Gordon Tetlow
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 4, 2002
+.Dt NEXTBOOT 8
+.Os
+.Sh NAME
+.Nm nextboot
+.Nd "specify an alternate kernel and boot flags for the next reboot"
+.Sh SYNOPSIS
+.Nm
+.Op Fl f
+.Op Fl o Ar options
+.Fl k Ar kernel
+.Nm
+.Fl D
+.Sh DESCRIPTION
+The
+.Nm
+utility allows specifying an alternate kernel and/or boot flags for the
+next time the machine is booted.
+Once the
+.Xr loader 8
+loads in the new kernel
+information, it is deleted so in case the new kernel hangs the machine,
+once it is rebooted, the machine will automatically revert to its previous
+configuration.
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl o Ar options"
+.It Fl D
+Invoking
+.Nm
+with this
+option removes an existing
+.Nm
+configuration.
+.It Fl f
+This
+option disables the sanity checking which checks if the kernel really exists
+before writing the
+.Nm
+configuration.
+.It Fl k Ar kernel
+This option specifies a kernel directory relative to
+.Pa /boot
+to load the kernel and any modules from.
+.It Fl o Ar options
+This option
+allows the passing of kernel flags for the next boot.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /boot/nextboot.conf" -compact
+.It Pa /boot/nextboot.conf
+The configuration file that the
+.Nm
+configuration is written into.
+.El
+.Sh EXAMPLES
+To boot the
+.Pa GENERIC
+kernel with the
+.Nm
+command:
+.Pp
+.Dl "nextboot -k GENERIC"
+.Pp
+To enable into single user mode with the normal kernel:
+.Pp
+.Dl "nextboot -o ""-s"" -k kernel"
+.Pp
+To remove an existing nextboot configuration:
+.Pp
+.Dl "nextboot -D"
+.Sh SEE ALSO
+.Xr boot 8 ,
+.Xr loader 8
+.Sh HISTORY
+The original
+.Nm
+manual page first appeared in
+.Fx 2.2 .
+It used a very different interface to achieve similar results.
+.Pp
+The current incarnation of
+.Nm
+appeared in
+.Fx 5.0 .
+.Sh AUTHORS
+This manual page was written by
+.An Gordon Tetlow Aq gordon@FreeBSD.org .
+.Sh BUGS
+The
+.Nm
+code is implemented in the
+.Xr loader 8 .
+It is not the most throughly tested code.
+It is also my first attempt to write in Forth.
+.Pp
+Finally, it does some evil things like writing to the file system before it
+has been checked.
+If it scrambles your file system, do not blame me.
diff --git a/sbin/reboot/nextboot.sh b/sbin/reboot/nextboot.sh
new file mode 100644
index 0000000..56e53f6
--- /dev/null
+++ b/sbin/reboot/nextboot.sh
@@ -0,0 +1,57 @@
+#! /bin/sh
+#
+# Copyright 2002. Gordon Tetlow.
+# gordon@FreeBSD.org
+#
+# $FreeBSD$
+
+delete="NO"
+force="NO"
+nextboot_file="/boot/nextboot.conf"
+
+display_usage() {
+ echo "Usage: nextboot [-f] [-o options] -k kernel"
+ echo " nextboot -D"
+}
+
+while getopts "Dfk:o:" argument ; do
+ case "${argument}" in
+ D)
+ delete="YES"
+ ;;
+ f)
+ force="YES"
+ ;;
+ k)
+ kernel="${OPTARG}"
+ ;;
+ o)
+ kernel_options="${OPTARG}"
+ ;;
+ *)
+ display_usage
+ exit 1
+ ;;
+ esac
+done
+
+if [ ${delete} = "YES" ]; then
+ rm -f ${nextboot_file}
+ exit 0
+fi
+
+if [ "xxx${kernel}" = "xxx" ]; then
+ display_usage
+ exit 1
+fi
+
+if [ ${force} = "NO" -a ! -d /boot/${kernel} ]; then
+ echo "Error: /boot/${kernel} doesn't exist. Use -f to override."
+ exit 1
+fi
+
+cat > ${nextboot_file} << EOF
+nextboot_enable="YES"
+kernel="${kernel}"
+kernel_options="${kernel_options}"
+EOF
diff --git a/sbin/reboot/reboot.8 b/sbin/reboot/reboot.8
new file mode 100644
index 0000000..39022ab
--- /dev/null
+++ b/sbin/reboot/reboot.8
@@ -0,0 +1,143 @@
+.\" Copyright (c) 1990, 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.
+.\" 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 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.
+.\"
+.\" @(#)reboot.8 8.1 (Berkeley) 6/9/93
+.\" $FreeBSD$
+.\"
+.Dd June 9, 1993
+.Dt REBOOT 8
+.Os
+.Sh NAME
+.Nm reboot ,
+.Nm halt ,
+.Nm fastboot ,
+.Nm fasthalt
+.Nd stopping and restarting the system
+.Sh SYNOPSIS
+.Nm halt
+.Op Fl lnpq
+.Op Fl k Ar kernel
+.Nm
+.Op Fl dlnpq
+.Op Fl k Ar kernel
+.Nm fasthalt
+.Op Fl lnpq
+.Op Fl k Ar kernel
+.Nm fastboot
+.Op Fl dlnpq
+.Op Fl k Ar kernel
+.Sh DESCRIPTION
+The
+.Nm halt
+and
+.Nm
+utilities flush the file system cache to disk, send all running processes
+a
+.Dv SIGTERM
+(and subsequently a
+.Dv SIGKILL )
+and, respectively, halt or restart the system.
+The action is logged, including entering a shutdown record into the
+.Xr wtmp 5
+file.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl d
+The system is requested to create a crash dump.
+This option is
+supported only when rebooting, and it has no effect unless a dump
+device has previously been specified with
+.Xr dumpon 8 .
+.It Fl k Ar kernel
+Boot the specified
+.Ar kernel
+on the next system boot.
+If the kernel boots successfully, the
+.Em default
+kernel will be booted on successive boots, this is a one-shot option.
+If the boot fails, the system will continue attempting to boot
+.Ar kernel
+until the boot process is interrupted and a valid kernel booted.
+This may change in the future.
+.It Fl l
+The halt or reboot is
+.Em not
+logged to the system log.
+This option is intended for applications such as
+.Xr shutdown 8 ,
+that call
+.Nm
+or
+.Nm halt
+and log this themselves.
+.It Fl n
+The file system cache is not flushed.
+This option should probably not be used.
+.It Fl p
+The system will turn off the power if it can.
+If the power down action fails, the system
+will halt or reboot normally, depending on whether
+.Nm halt
+or
+.Nm
+was called.
+.It Fl q
+The system is halted or restarted quickly and ungracefully, and only
+the flushing of the file system cache is performed (if the
+.Fl n
+option is not specified).
+This option should probably not be used.
+.El
+.Pp
+The
+.Nm fasthalt
+and
+.Nm fastboot
+utilities are nothing more than aliases for the
+.Nm halt
+and
+.Nm
+utilities.
+.Pp
+Normally, the
+.Xr shutdown 8
+utility is used when the system needs to be halted or restarted, giving
+users advance warning of their impending doom and cleanly terminating
+specific programs.
+.Sh SEE ALSO
+.Xr wtmp 5 ,
+.Xr boot 8 ,
+.Xr dumpon 8 ,
+.Xr savecore 8 ,
+.Xr shutdown 8 ,
+.Xr sync 8
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.At v6 .
diff --git a/sbin/reboot/reboot.c b/sbin/reboot/reboot.c
new file mode 100644
index 0000000..0a52ba4
--- /dev/null
+++ b/sbin/reboot/reboot.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 1980, 1986, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1986, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)reboot.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/reboot.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <signal.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void usage(void);
+static u_int get_pageins(void);
+
+int dohalt;
+
+int
+main(int argc, char *argv[])
+{
+ const struct passwd *pw;
+ int ch, howto, i, fd, lflag, nflag, qflag, pflag, sverrno;
+ u_int pageins;
+ const char *p, *user, *kernel = NULL;
+
+ if (strstr((p = rindex(*argv, '/')) ? p + 1 : *argv, "halt")) {
+ dohalt = 1;
+ howto = RB_HALT;
+ } else
+ howto = 0;
+ lflag = nflag = qflag = 0;
+ while ((ch = getopt(argc, argv, "dk:lnpq")) != -1)
+ switch(ch) {
+ case 'd':
+ howto |= RB_DUMP;
+ break;
+ case 'k':
+ kernel = optarg;
+ break;
+ case 'l':
+ lflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ howto |= RB_NOSYNC;
+ break;
+ case 'p':
+ pflag = 1;
+ howto |= RB_POWEROFF;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT))
+ errx(1, "cannot dump (-d) when halting; must reboot instead");
+ if (geteuid()) {
+ errno = EPERM;
+ err(1, NULL);
+ }
+
+ if (qflag) {
+ reboot(howto);
+ err(1, NULL);
+ }
+
+ if (kernel != NULL) {
+ fd = open("/boot/nextboot.conf", O_WRONLY | O_CREAT | O_TRUNC,
+ 0444);
+ if (fd > -1) {
+ (void)write(fd, "nextboot_enable=\"YES\"\n", 22);
+ (void)write(fd, "kernel=\"", 8L);
+ (void)write(fd, kernel, strlen(kernel));
+ (void)write(fd, "\"\n", 2);
+ close(fd);
+ }
+ }
+
+ /* Log the reboot. */
+ if (!lflag) {
+ if ((user = getlogin()) == NULL)
+ user = (pw = getpwuid(getuid())) ?
+ pw->pw_name : "???";
+ if (dohalt) {
+ openlog("halt", 0, LOG_AUTH | LOG_CONS);
+ syslog(LOG_CRIT, "halted by %s", user);
+ } else {
+ openlog("reboot", 0, LOG_AUTH | LOG_CONS);
+ syslog(LOG_CRIT, "rebooted by %s", user);
+ }
+ }
+ logwtmp("~", "shutdown", "");
+
+ /*
+ * Do a sync early on, so disks start transfers while we're off
+ * killing processes. Don't worry about writes done before the
+ * processes die, the reboot system call syncs the disks.
+ */
+ if (!nflag)
+ sync();
+
+ /* Just stop init -- if we fail, we'll restart it. */
+ if (kill(1, SIGTSTP) == -1)
+ err(1, "SIGTSTP init");
+
+ /* Ignore the SIGHUP we get when our parent shell dies. */
+ (void)signal(SIGHUP, SIG_IGN);
+
+ /* Send a SIGTERM first, a chance to save the buffers. */
+ if (kill(-1, SIGTERM) == -1 && errno != ESRCH)
+ err(1, "SIGTERM processes");
+
+ /*
+ * After the processes receive the signal, start the rest of the
+ * buffers on their way. Wait 5 seconds between the SIGTERM and
+ * the SIGKILL to give everybody a chance. If there is a lot of
+ * paging activity then wait longer, up to a maximum of approx
+ * 60 seconds.
+ */
+ sleep(2);
+ for (i = 0; i < 20; i++) {
+ pageins = get_pageins();
+ if (!nflag)
+ sync();
+ sleep(3);
+ if (get_pageins() == pageins)
+ break;
+ }
+
+ for (i = 1;; ++i) {
+ if (kill(-1, SIGKILL) == -1) {
+ if (errno == ESRCH)
+ break;
+ goto restart;
+ }
+ if (i > 5) {
+ (void)fprintf(stderr,
+ "WARNING: some process(es) wouldn't die\n");
+ break;
+ }
+ (void)sleep(2 * i);
+ }
+
+ reboot(howto);
+ /* FALLTHROUGH */
+
+restart:
+ sverrno = errno;
+ errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "",
+ strerror(sverrno));
+ /* NOTREACHED */
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: %s [-%slnpq] [-k kernel]\n",
+ getprogname(), dohalt ? "" : "d");
+ exit(1);
+}
+
+static u_int
+get_pageins()
+{
+ u_int pageins;
+ size_t len;
+
+ len = sizeof(pageins);
+ if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0)
+ != 0) {
+ warnx("v_swappgsin");
+ return (0);
+ }
+ return pageins;
+}
diff --git a/sbin/recoverdisk/Makefile b/sbin/recoverdisk/Makefile
new file mode 100644
index 0000000..e86fbb2
--- /dev/null
+++ b/sbin/recoverdisk/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= recoverdisk
+
+NO_MAN=
+
+WARNS?= 5
+
+.include <bsd.prog.mk>
+
+test: ${PROG}
+ ./${PROG} /dev/ad0
diff --git a/sbin/recoverdisk/recoverdisk.c b/sbin/recoverdisk/recoverdisk.c
new file mode 100644
index 0000000..d30055a
--- /dev/null
+++ b/sbin/recoverdisk/recoverdisk.c
@@ -0,0 +1,160 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/queue.h>
+#include <sys/disk.h>
+#include <sys/stat.h>
+
+#define BIGSIZE (1024 * 1024)
+#define MEDIUMSIZE (64 * 1024)
+#define MINSIZE (512)
+
+struct lump {
+ off_t start;
+ off_t len;
+ int state;
+ TAILQ_ENTRY(lump) list;
+};
+
+static TAILQ_HEAD(, lump) lumps = TAILQ_HEAD_INITIALIZER(lumps);
+
+
+static void
+new_lump(off_t start, off_t len, int state)
+{
+ struct lump *lp;
+
+ lp = malloc(sizeof *lp);
+ if (lp == NULL)
+ err(1, "Malloc failed");
+ lp->start = start;
+ lp->len = len;
+ lp->state = state;
+ TAILQ_INSERT_TAIL(&lumps, lp, list);
+}
+
+int
+main(int argc, const char **argv)
+{
+ int fdr, fdw;
+ struct lump *lp;
+ off_t t, d;
+ size_t i, j;
+ int error, flags;
+ u_char *buf;
+ u_int sectorsize, minsize;
+ time_t t1, t2;
+ struct stat sb;
+
+
+ if (argc < 2)
+ errx(1, "Usage: %s source-drive [destination]", argv[0]);
+
+ buf = malloc(BIGSIZE);
+ if (buf == NULL)
+ err(1, "Cannot allocate %d bytes buffer", BIGSIZE);
+ fdr = open(argv[1], O_RDONLY);
+ if (fdr < 0)
+ err(1, "Cannot open read descriptor %s", argv[1]);
+
+ error = fstat(fdr, &sb);
+ if (error < 0)
+ err(1, "fstat failed");
+ flags = O_WRONLY;
+ if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
+ error = ioctl(fdr, DIOCGSECTORSIZE, &sectorsize);
+ if (error < 0)
+ err(1, "DIOCGSECTORSIZE failed");
+ minsize = sectorsize;
+
+ error = ioctl(fdr, DIOCGMEDIASIZE, &t);
+ if (error < 0)
+ err(1, "DIOCGMEDIASIZE failed");
+ } else {
+ sectorsize = 1;
+ t = sb.st_size;
+ minsize = MINSIZE;
+ flags |= O_CREAT | O_TRUNC;
+ }
+
+ if (argc > 2) {
+ fdw = open(argv[2], flags, DEFFILEMODE);
+ if (fdw < 0)
+ err(1, "Cannot open write descriptor %s", argv[2]);
+ } else {
+ fdw = -1;
+ }
+
+ new_lump(0, t, 0);
+ d = 0;
+
+ t1 = 0;
+ for (;;) {
+ lp = TAILQ_FIRST(&lumps);
+ if (lp == NULL)
+ break;
+ TAILQ_REMOVE(&lumps, lp, list);
+ while (lp->len > 0) {
+ i = BIGSIZE;
+ if (lp->len < BIGSIZE)
+ i = lp->len;
+ if (lp->state == 1)
+ i = MEDIUMSIZE;
+ if (lp->state > 1)
+ i = minsize;
+ time(&t2);
+ if (t1 != t2 || lp->len < BIGSIZE) {
+ printf("\r%13jd %7zu %13jd %3d %13jd %13jd %.8f",
+ (intmax_t)lp->start,
+ i,
+ (intmax_t)lp->len,
+ lp->state,
+ (intmax_t)d,
+ (intmax_t)(t - d),
+ (double)d/(double)t);
+ t1 = t2;
+ }
+ if (i == 0) {
+ errx(1, "BOGUS i %10jd", (intmax_t)i);
+ }
+ fflush(stdout);
+ j = pread(fdr, buf, i, lp->start);
+ if (j == i) {
+ d += i;
+ if (fdw >= 0)
+ j = pwrite(fdw, buf, i, lp->start);
+ else
+ j = i;
+ if (j != i)
+ printf("\nWrite error at %jd/%zu\n",
+ lp->start, i);
+ lp->start += i;
+ lp->len -= i;
+ continue;
+ }
+ printf("\n%jd %zu failed %d\n", lp->start, i, errno);
+ new_lump(lp->start, i, lp->state + 1);
+ lp->start += i;
+ lp->len -= i;
+ }
+ free(lp);
+ }
+ printf("\nCompleted\n");
+ exit (0);
+}
+
diff --git a/sbin/restore/Makefile b/sbin/restore/Makefile
new file mode 100644
index 0000000..c06a3db
--- /dev/null
+++ b/sbin/restore/Makefile
@@ -0,0 +1,15 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../dump
+
+PROG= restore
+LINKS= ${BINDIR}/restore ${BINDIR}/rrestore
+CFLAGS+=-DRRESTORE
+WARNS?= 0
+SRCS= main.c interactive.c restore.c dirs.c symtab.c tape.c utilities.c \
+ dumprmt.c
+MAN= restore.8
+MLINKS= restore.8 rrestore.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/restore/dirs.c b/sbin/restore/dirs.c
new file mode 100644
index 0000000..f12f932
--- /dev/null
+++ b/sbin/restore/dirs.c
@@ -0,0 +1,722 @@
+/*
+ * Copyright (c) 1983, 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.
+ *
+ * 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.
+ * 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 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 lint
+#if 0
+static char sccsid[] = "@(#)dirs.c 8.7 (Berkeley) 5/1/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <protocols/dumprestore.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "restore.h"
+#include "extern.h"
+
+/*
+ * Symbol table of directories read from tape.
+ */
+#define HASHSIZE 1000
+#define INOHASH(val) (val % HASHSIZE)
+struct inotab {
+ struct inotab *t_next;
+ ino_t t_ino;
+ int32_t t_seekpt;
+ int32_t t_size;
+};
+static struct inotab *inotab[HASHSIZE];
+
+/*
+ * Information retained about directories.
+ */
+struct modeinfo {
+ ino_t ino;
+ struct timeval ctimep[2];
+ struct timeval mtimep[2];
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ int flags;
+};
+
+/*
+ * Definitions for library routines operating on directories.
+ */
+#undef DIRBLKSIZ
+#define DIRBLKSIZ 1024
+struct rstdirdesc {
+ int dd_fd;
+ int32_t dd_loc;
+ int32_t dd_size;
+ char dd_buf[DIRBLKSIZ];
+};
+
+/*
+ * Global variables for this file.
+ */
+static long seekpt;
+static FILE *df, *mf;
+static RST_DIR *dirp;
+static char dirfile[MAXPATHLEN] = "#"; /* No file */
+static char modefile[MAXPATHLEN] = "#"; /* No file */
+static char dot[2] = "."; /* So it can be modified */
+
+static struct inotab *allocinotab(struct context *, long);
+static void flushent(void);
+static struct inotab *inotablookup(ino_t);
+static RST_DIR *opendirfile(const char *);
+static void putdir(char *, long);
+static void putent(struct direct *);
+static void rst_seekdir(RST_DIR *, long, long);
+static long rst_telldir(RST_DIR *);
+static struct direct *searchdir(ino_t, char *);
+
+/*
+ * Extract directory contents, building up a directory structure
+ * on disk for extraction by name.
+ * If genmode is requested, save mode, owner, and times for all
+ * directories on the tape.
+ */
+void
+extractdirs(int genmode)
+{
+ struct inotab *itp;
+ struct direct nulldir;
+ int i, fd;
+ const char *tmpdir;
+
+ vprintf(stdout, "Extract directories from tape\n");
+ if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0')
+ tmpdir = _PATH_TMP;
+ (void) sprintf(dirfile, "%s/rstdir%d", tmpdir, dumpdate);
+ if (command != 'r' && command != 'R') {
+ (void *) strcat(dirfile, "-XXXXXX");
+ fd = mkstemp(dirfile);
+ } else
+ fd = open(dirfile, O_RDWR|O_CREAT|O_EXCL, 0666);
+ if (fd == -1 || (df = fdopen(fd, "w")) == NULL) {
+ if (fd != -1)
+ close(fd);
+ warn("%s - cannot create directory temporary\nfopen", dirfile);
+ done(1);
+ }
+ if (genmode != 0) {
+ (void) sprintf(modefile, "%s/rstmode%d", tmpdir, dumpdate);
+ if (command != 'r' && command != 'R') {
+ (void *) strcat(modefile, "-XXXXXX");
+ fd = mkstemp(modefile);
+ } else
+ fd = open(modefile, O_RDWR|O_CREAT|O_EXCL, 0666);
+ if (fd == -1 || (mf = fdopen(fd, "w")) == NULL) {
+ if (fd != -1)
+ close(fd);
+ warn("%s - cannot create modefile\nfopen", modefile);
+ done(1);
+ }
+ }
+ nulldir.d_ino = 0;
+ nulldir.d_type = DT_DIR;
+ nulldir.d_namlen = 1;
+ (void) strcpy(nulldir.d_name, "/");
+ nulldir.d_reclen = DIRSIZ(0, &nulldir);
+ for (;;) {
+ curfile.name = "<directory file - name unknown>";
+ curfile.action = USING;
+ if (curfile.mode == 0 || (curfile.mode & IFMT) != IFDIR) {
+ (void) fclose(df);
+ dirp = opendirfile(dirfile);
+ if (dirp == NULL)
+ fprintf(stderr, "opendirfile: %s\n",
+ strerror(errno));
+ if (mf != NULL)
+ (void) fclose(mf);
+ i = dirlookup(dot);
+ if (i == 0)
+ panic("Root directory is not on tape\n");
+ return;
+ }
+ itp = allocinotab(&curfile, seekpt);
+ getfile(putdir, xtrnull);
+ putent(&nulldir);
+ flushent();
+ itp->t_size = seekpt - itp->t_seekpt;
+ }
+}
+
+/*
+ * skip over all the directories on the tape
+ */
+void
+skipdirs(void)
+{
+
+ while (curfile.ino && (curfile.mode & IFMT) == IFDIR) {
+ skipfile();
+ }
+}
+
+/*
+ * Recursively find names and inumbers of all files in subtree
+ * pname and pass them off to be processed.
+ */
+void
+treescan(char *pname, ino_t ino, long (*todo)(char *, ino_t, int))
+{
+ struct inotab *itp;
+ struct direct *dp;
+ int namelen;
+ long bpt;
+ char locname[MAXPATHLEN + 1];
+
+ itp = inotablookup(ino);
+ if (itp == NULL) {
+ /*
+ * Pname is name of a simple file or an unchanged directory.
+ */
+ (void) (*todo)(pname, ino, LEAF);
+ return;
+ }
+ /*
+ * Pname is a dumped directory name.
+ */
+ if ((*todo)(pname, ino, NODE) == FAIL)
+ return;
+ /*
+ * begin search through the directory
+ * skipping over "." and ".."
+ */
+ (void) strncpy(locname, pname, sizeof(locname) - 1);
+ locname[sizeof(locname) - 1] = '\0';
+ (void) strncat(locname, "/", sizeof(locname) - strlen(locname));
+ namelen = strlen(locname);
+ rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
+ dp = rst_readdir(dirp); /* "." */
+ if (dp != NULL && strcmp(dp->d_name, ".") == 0)
+ dp = rst_readdir(dirp); /* ".." */
+ else
+ fprintf(stderr, "Warning: `.' missing from directory %s\n",
+ pname);
+ if (dp != NULL && strcmp(dp->d_name, "..") == 0)
+ dp = rst_readdir(dirp); /* first real entry */
+ else
+ fprintf(stderr, "Warning: `..' missing from directory %s\n",
+ pname);
+ bpt = rst_telldir(dirp);
+ /*
+ * a zero inode signals end of directory
+ */
+ while (dp != NULL) {
+ locname[namelen] = '\0';
+ if (namelen + dp->d_namlen >= sizeof(locname)) {
+ fprintf(stderr, "%s%s: name exceeds %d char\n",
+ locname, dp->d_name, sizeof(locname) - 1);
+ } else {
+ (void) strncat(locname, dp->d_name, (int)dp->d_namlen);
+ treescan(locname, dp->d_ino, todo);
+ rst_seekdir(dirp, bpt, itp->t_seekpt);
+ }
+ dp = rst_readdir(dirp);
+ bpt = rst_telldir(dirp);
+ }
+}
+
+/*
+ * Lookup a pathname which is always assumed to start from the ROOTINO.
+ */
+struct direct *
+pathsearch(const char *pathname)
+{
+ ino_t ino;
+ struct direct *dp;
+ char *path, *name, buffer[MAXPATHLEN];
+
+ strcpy(buffer, pathname);
+ path = buffer;
+ ino = ROOTINO;
+ while (*path == '/')
+ path++;
+ dp = NULL;
+ while ((name = strsep(&path, "/")) != NULL && *name != '\0') {
+ if ((dp = searchdir(ino, name)) == NULL)
+ return (NULL);
+ ino = dp->d_ino;
+ }
+ return (dp);
+}
+
+/*
+ * Lookup the requested name in directory inum.
+ * Return its inode number if found, zero if it does not exist.
+ */
+static struct direct *
+searchdir(ino_t inum, char *name)
+{
+ struct direct *dp;
+ struct inotab *itp;
+ int len;
+
+ itp = inotablookup(inum);
+ if (itp == NULL)
+ return (NULL);
+ rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
+ len = strlen(name);
+ do {
+ dp = rst_readdir(dirp);
+ if (dp == NULL)
+ return (NULL);
+ } while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0);
+ return (dp);
+}
+
+/*
+ * Put the directory entries in the directory file
+ */
+static void
+putdir(char *buf, long size)
+{
+ struct direct *dp;
+ long loc, i;
+
+ for (loc = 0; loc < size; ) {
+ dp = (struct direct *)(buf + loc);
+ if (Bcvt)
+ swabst((u_char *)"ls", (u_char *) dp);
+ if (oldinofmt && dp->d_ino != 0) {
+#if BYTE_ORDER == BIG_ENDIAN
+ if (Bcvt)
+ dp->d_namlen = dp->d_type;
+#else
+ if (!Bcvt && dp->d_namlen == 0)
+ dp->d_namlen = dp->d_type;
+#endif
+ dp->d_type = DT_UNKNOWN;
+ }
+ i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
+ if ((dp->d_reclen & 0x3) != 0 ||
+ dp->d_reclen > i ||
+ dp->d_reclen < DIRSIZ(0, dp)
+#if NAME_MAX < 255
+ || dp->d_namlen > NAME_MAX
+#endif
+ ) {
+ vprintf(stdout, "Mangled directory: ");
+ if ((dp->d_reclen & 0x3) != 0)
+ vprintf(stdout,
+ "reclen not multiple of 4 ");
+ if (dp->d_reclen < DIRSIZ(0, dp))
+ vprintf(stdout,
+ "reclen less than DIRSIZ (%d < %d) ",
+ dp->d_reclen, DIRSIZ(0, dp));
+#if NAME_MAX < 255
+ if (dp->d_namlen > NAME_MAX)
+ vprintf(stdout,
+ "reclen name too big (%d > %d) ",
+ dp->d_namlen, NAME_MAX);
+#endif
+ vprintf(stdout, "\n");
+ loc += i;
+ continue;
+ }
+ loc += dp->d_reclen;
+ if (dp->d_ino != 0) {
+ putent(dp);
+ }
+ }
+}
+
+/*
+ * These variables are "local" to the following two functions.
+ */
+char dirbuf[DIRBLKSIZ];
+long dirloc = 0;
+long prev = 0;
+
+/*
+ * add a new directory entry to a file.
+ */
+static void
+putent(struct direct *dp)
+{
+ dp->d_reclen = DIRSIZ(0, dp);
+ if (dirloc + dp->d_reclen > DIRBLKSIZ) {
+ ((struct direct *)(dirbuf + prev))->d_reclen =
+ DIRBLKSIZ - prev;
+ (void) fwrite(dirbuf, 1, DIRBLKSIZ, df);
+ dirloc = 0;
+ }
+ memmove(dirbuf + dirloc, dp, (long)dp->d_reclen);
+ prev = dirloc;
+ dirloc += dp->d_reclen;
+}
+
+/*
+ * flush out a directory that is finished.
+ */
+static void
+flushent(void)
+{
+ ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
+ (void) fwrite(dirbuf, (int)dirloc, 1, df);
+ seekpt = ftell(df);
+ dirloc = 0;
+}
+
+/*
+ * Seek to an entry in a directory.
+ * Only values returned by rst_telldir should be passed to rst_seekdir.
+ * This routine handles many directories in a single file.
+ * It takes the base of the directory in the file, plus
+ * the desired seek offset into it.
+ */
+static void
+rst_seekdir(RST_DIR *dirp, long loc, long base)
+{
+
+ if (loc == rst_telldir(dirp))
+ return;
+ loc -= base;
+ if (loc < 0)
+ fprintf(stderr, "bad seek pointer to rst_seekdir %ld\n", loc);
+ (void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET);
+ dirp->dd_loc = loc & (DIRBLKSIZ - 1);
+ if (dirp->dd_loc != 0)
+ dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ);
+}
+
+/*
+ * get next entry in a directory.
+ */
+struct direct *
+rst_readdir(RST_DIR *dirp)
+{
+ struct direct *dp;
+
+ for (;;) {
+ if (dirp->dd_loc == 0) {
+ dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
+ DIRBLKSIZ);
+ if (dirp->dd_size <= 0) {
+ dprintf(stderr, "error reading directory\n");
+ return (NULL);
+ }
+ }
+ if (dirp->dd_loc >= dirp->dd_size) {
+ dirp->dd_loc = 0;
+ continue;
+ }
+ dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc);
+ if (dp->d_reclen == 0 ||
+ dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) {
+ dprintf(stderr, "corrupted directory: bad reclen %d\n",
+ dp->d_reclen);
+ return (NULL);
+ }
+ dirp->dd_loc += dp->d_reclen;
+ if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0)
+ return (NULL);
+ if (dp->d_ino >= maxino) {
+ dprintf(stderr, "corrupted directory: bad inum %d\n",
+ dp->d_ino);
+ continue;
+ }
+ return (dp);
+ }
+}
+
+/*
+ * Simulate the opening of a directory
+ */
+void *
+rst_opendir(const char *name)
+{
+ struct inotab *itp;
+ RST_DIR *dirp;
+ ino_t ino;
+
+ if ((ino = dirlookup(name)) > 0 &&
+ (itp = inotablookup(ino)) != NULL) {
+ dirp = opendirfile(dirfile);
+ rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
+ return (dirp);
+ }
+ return (NULL);
+}
+
+/*
+ * In our case, there is nothing to do when closing a directory.
+ */
+void
+rst_closedir(void *arg)
+{
+ RST_DIR *dirp;
+
+ dirp = arg;
+ (void)close(dirp->dd_fd);
+ free(dirp);
+ return;
+}
+
+/*
+ * Simulate finding the current offset in the directory.
+ */
+static long
+rst_telldir(RST_DIR *dirp)
+{
+ return ((long)lseek(dirp->dd_fd,
+ (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc);
+}
+
+/*
+ * Open a directory file.
+ */
+static RST_DIR *
+opendirfile(const char *name)
+{
+ RST_DIR *dirp;
+ int fd;
+
+ if ((fd = open(name, O_RDONLY)) == -1)
+ return (NULL);
+ if ((dirp = malloc(sizeof(RST_DIR))) == NULL) {
+ (void)close(fd);
+ return (NULL);
+ }
+ dirp->dd_fd = fd;
+ dirp->dd_loc = 0;
+ return (dirp);
+}
+
+/*
+ * Set the mode, owner, and times for all new or changed directories
+ */
+void
+setdirmodes(int flags)
+{
+ FILE *mf;
+ struct modeinfo node;
+ struct entry *ep;
+ char *cp;
+ const char *tmpdir;
+
+ vprintf(stdout, "Set directory mode, owner, and times.\n");
+ if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0')
+ tmpdir = _PATH_TMP;
+ if (command == 'r' || command == 'R')
+ (void) sprintf(modefile, "%s/rstmode%d", tmpdir, dumpdate);
+ if (modefile[0] == '#') {
+ panic("modefile not defined\n");
+ fprintf(stderr, "directory mode, owner, and times not set\n");
+ return;
+ }
+ mf = fopen(modefile, "r");
+ if (mf == NULL) {
+ fprintf(stderr, "fopen: %s\n", strerror(errno));
+ fprintf(stderr, "cannot open mode file %s\n", modefile);
+ fprintf(stderr, "directory mode, owner, and times not set\n");
+ return;
+ }
+ clearerr(mf);
+ for (;;) {
+ (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
+ if (feof(mf))
+ break;
+ ep = lookupino(node.ino);
+ if (command == 'i' || command == 'x') {
+ if (ep == NULL)
+ continue;
+ if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) {
+ ep->e_flags &= ~NEW;
+ continue;
+ }
+ if (node.ino == ROOTINO &&
+ reply("set owner/mode for '.'") == FAIL)
+ continue;
+ }
+ if (ep == NULL) {
+ panic("cannot find directory inode %d\n", node.ino);
+ } else {
+ cp = myname(ep);
+ if (!Nflag) {
+ (void) chown(cp, node.uid, node.gid);
+ (void) chmod(cp, node.mode);
+ utimes(cp, node.ctimep);
+ utimes(cp, node.mtimep);
+ (void) chflags(cp, node.flags);
+ }
+ ep->e_flags &= ~NEW;
+ }
+ }
+ if (ferror(mf))
+ panic("error setting directory modes\n");
+ (void) fclose(mf);
+}
+
+/*
+ * Generate a literal copy of a directory.
+ */
+int
+genliteraldir(char *name, ino_t ino)
+{
+ struct inotab *itp;
+ int ofile, dp, i, size;
+ char buf[BUFSIZ];
+
+ itp = inotablookup(ino);
+ if (itp == NULL)
+ panic("Cannot find directory inode %d named %s\n", ino, name);
+ if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
+ fprintf(stderr, "%s: ", name);
+ (void) fflush(stderr);
+ fprintf(stderr, "cannot create file: %s\n", strerror(errno));
+ return (FAIL);
+ }
+ rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
+ dp = dup(dirp->dd_fd);
+ for (i = itp->t_size; i > 0; i -= BUFSIZ) {
+ size = i < BUFSIZ ? i : BUFSIZ;
+ if (read(dp, buf, (int) size) == -1) {
+ fprintf(stderr,
+ "write error extracting inode %d, name %s\n",
+ curfile.ino, curfile.name);
+ fprintf(stderr, "read: %s\n", strerror(errno));
+ done(1);
+ }
+ if (!Nflag && write(ofile, buf, (int) size) == -1) {
+ fprintf(stderr,
+ "write error extracting inode %d, name %s\n",
+ curfile.ino, curfile.name);
+ fprintf(stderr, "write: %s\n", strerror(errno));
+ done(1);
+ }
+ }
+ (void) close(dp);
+ (void) close(ofile);
+ return (GOOD);
+}
+
+/*
+ * Determine the type of an inode
+ */
+int
+inodetype(ino_t ino)
+{
+ struct inotab *itp;
+
+ itp = inotablookup(ino);
+ if (itp == NULL)
+ return (LEAF);
+ return (NODE);
+}
+
+/*
+ * Allocate and initialize a directory inode entry.
+ * If requested, save its pertinent mode, owner, and time info.
+ */
+static struct inotab *
+allocinotab(struct context *ctxp, long seekpt)
+{
+ struct inotab *itp;
+ struct modeinfo node;
+
+ itp = calloc(1, sizeof(struct inotab));
+ if (itp == NULL)
+ panic("no memory directory table\n");
+ itp->t_next = inotab[INOHASH(ctxp->ino)];
+ inotab[INOHASH(ctxp->ino)] = itp;
+ itp->t_ino = ctxp->ino;
+ itp->t_seekpt = seekpt;
+ if (mf == NULL)
+ return (itp);
+ node.ino = ctxp->ino;
+ node.mtimep[0].tv_sec = ctxp->atime_sec;
+ node.mtimep[0].tv_usec = ctxp->atime_nsec / 1000;
+ node.mtimep[1].tv_sec = ctxp->mtime_sec;
+ node.mtimep[1].tv_usec = ctxp->mtime_nsec / 1000;
+ node.ctimep[0].tv_sec = ctxp->atime_sec;
+ node.ctimep[0].tv_usec = ctxp->atime_nsec / 1000;
+ node.ctimep[1].tv_sec = ctxp->birthtime_sec;
+ node.ctimep[1].tv_usec = ctxp->birthtime_nsec / 1000;
+ node.mode = ctxp->mode;
+ node.flags = ctxp->file_flags;
+ node.uid = ctxp->uid;
+ node.gid = ctxp->gid;
+ (void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf);
+ return (itp);
+}
+
+/*
+ * Look up an inode in the table of directories
+ */
+static struct inotab *
+inotablookup(ino_t ino)
+{
+ struct inotab *itp;
+
+ for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
+ if (itp->t_ino == ino)
+ return (itp);
+ return (NULL);
+}
+
+/*
+ * Clean up and exit
+ */
+void
+done(int exitcode)
+{
+
+ closemt();
+ if (modefile[0] != '#')
+ (void) unlink(modefile);
+ if (dirfile[0] != '#')
+ (void) unlink(dirfile);
+ exit(exitcode);
+}
diff --git a/sbin/restore/extern.h b/sbin/restore/extern.h
new file mode 100644
index 0000000..603a561
--- /dev/null
+++ b/sbin/restore/extern.h
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 1992, 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.
+ * 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 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.h 8.2 (Berkeley) 1/7/94
+ * $FreeBSD$
+ */
+
+struct entry *addentry(char *, ino_t, int);
+long addfile(char *, ino_t, int);
+int addwhiteout(char *);
+void badentry(struct entry *, char *);
+void canon(char *, char *, int);
+void checkrestore(void);
+void closemt(void);
+void createfiles(void);
+void createleaves(char *);
+void createlinks(void);
+long deletefile(char *, ino_t, int);
+void deleteino(ino_t);
+void delwhiteout(struct entry *);
+ino_t dirlookup(const char *);
+void done(int) __dead2;
+void dumpsymtable(char *, long);
+void extractdirs(int);
+int extractfile(char *);
+void findunreflinks(void);
+char *flagvalues(struct entry *);
+void freeentry(struct entry *);
+void freename(char *);
+int genliteraldir(char *, ino_t);
+char *gentempname(struct entry *);
+void getfile(void (*)(char *, long), void (*)(char *, long));
+void getvol(long);
+void initsymtable(char *);
+int inodetype(ino_t);
+int linkit(char *, char *, int);
+struct entry *lookupino(ino_t);
+struct entry *lookupname(char *);
+long listfile(char *, ino_t, int);
+ino_t lowerbnd(ino_t);
+void mktempname(struct entry *);
+void moveentry(struct entry *, char *);
+void msg(const char *, ...) __printflike(1, 2);
+char *myname(struct entry *);
+void newnode(struct entry *);
+void newtapebuf(long);
+long nodeupdates(char *, ino_t, int);
+void onintr(int);
+void panic(const char *, ...) __printflike(1, 2);
+void pathcheck(char *);
+struct direct *pathsearch(const char *);
+void printdumpinfo(void);
+void removeleaf(struct entry *);
+void removenode(struct entry *);
+void removeoldleaves(void);
+void removeoldnodes(void);
+void renameit(char *, char *);
+int reply(char *);
+void *rst_opendir(const char *);
+struct direct *rst_readdir(RST_DIR *);
+void rst_closedir(void *);
+void runcmdshell(void);
+char *savename(char *);
+void setdirmodes(int);
+void setinput(char *, int);
+void setup(void);
+void skipdirs(void);
+void skipfile(void);
+void skipmaps(void);
+void swabst(u_char *, u_char *);
+void treescan(char *, ino_t, long (*)(char *, ino_t, int));
+ino_t upperbnd(ino_t);
+long verifyfile(char *, ino_t, int);
+void xtrnull(char *, long);
+
+/* From ../dump/dumprmt.c */
+void rmtclose(void);
+int rmthost(char *);
+int rmtioctl(int, int);
+int rmtopen(char *, int);
+int rmtread(char *, int);
+int rmtseek(int, int);
diff --git a/sbin/restore/interactive.c b/sbin/restore/interactive.c
new file mode 100644
index 0000000..e3391d0
--- /dev/null
+++ b/sbin/restore/interactive.c
@@ -0,0 +1,769 @@
+/*
+ * Copyright (c) 1985, 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.
+ * 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 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 lint
+#if 0
+static char sccsid[] = "@(#)interactive.c 8.5 (Berkeley) 5/1/95";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <protocols/dumprestore.h>
+
+#include <ctype.h>
+#include <glob.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "restore.h"
+#include "extern.h"
+
+#define round(a, b) (((a) + (b) - 1) / (b) * (b))
+
+/*
+ * Things to handle interruptions.
+ */
+static int runshell;
+static jmp_buf reset;
+static char *nextarg = NULL;
+
+/*
+ * Structure and routines associated with listing directories.
+ */
+struct afile {
+ ino_t fnum; /* inode number of file */
+ char *fname; /* file name */
+ short len; /* name length */
+ char prefix; /* prefix character */
+ char postfix; /* postfix character */
+};
+struct arglist {
+ int freeglob; /* glob structure needs to be freed */
+ int argcnt; /* next globbed argument to return */
+ glob_t glob; /* globbing information */
+ char *cmd; /* the current command */
+};
+
+static char *copynext(char *, char *);
+static int fcmp(const void *, const void *);
+static void formatf(struct afile *, int);
+static void getcmd(char *, char *, char *, int, struct arglist *);
+struct dirent *glob_readdir(void *);
+static int glob_stat(const char *, struct stat *);
+static void mkentry(char *, struct direct *, struct afile *);
+static void printlist(char *, char *);
+
+/*
+ * Read and execute commands from the terminal.
+ */
+void
+runcmdshell(void)
+{
+ struct entry *np;
+ ino_t ino;
+ struct arglist arglist;
+ char curdir[MAXPATHLEN];
+ char name[MAXPATHLEN];
+ char cmd[BUFSIZ];
+
+ arglist.freeglob = 0;
+ arglist.argcnt = 0;
+ arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
+ arglist.glob.gl_opendir = rst_opendir;
+ arglist.glob.gl_readdir = glob_readdir;
+ arglist.glob.gl_closedir = rst_closedir;
+ arglist.glob.gl_lstat = glob_stat;
+ arglist.glob.gl_stat = glob_stat;
+ canon("/", curdir, sizeof(curdir));
+loop:
+ if (setjmp(reset) != 0) {
+ if (arglist.freeglob != 0) {
+ arglist.freeglob = 0;
+ arglist.argcnt = 0;
+ globfree(&arglist.glob);
+ }
+ nextarg = NULL;
+ volno = 0;
+ }
+ runshell = 1;
+ getcmd(curdir, cmd, name, sizeof(name), &arglist);
+ switch (cmd[0]) {
+ /*
+ * Add elements to the extraction list.
+ */
+ case 'a':
+ if (strncmp(cmd, "add", strlen(cmd)) != 0)
+ goto bad;
+ ino = dirlookup(name);
+ if (ino == 0)
+ break;
+ if (mflag)
+ pathcheck(name);
+ treescan(name, ino, addfile);
+ break;
+ /*
+ * Change working directory.
+ */
+ case 'c':
+ if (strncmp(cmd, "cd", strlen(cmd)) != 0)
+ goto bad;
+ ino = dirlookup(name);
+ if (ino == 0)
+ break;
+ if (inodetype(ino) == LEAF) {
+ fprintf(stderr, "%s: not a directory\n", name);
+ break;
+ }
+ (void) strcpy(curdir, name);
+ break;
+ /*
+ * Delete elements from the extraction list.
+ */
+ case 'd':
+ if (strncmp(cmd, "delete", strlen(cmd)) != 0)
+ goto bad;
+ np = lookupname(name);
+ if (np == NULL || (np->e_flags & NEW) == 0) {
+ fprintf(stderr, "%s: not on extraction list\n", name);
+ break;
+ }
+ treescan(name, np->e_ino, deletefile);
+ break;
+ /*
+ * Extract the requested list.
+ */
+ case 'e':
+ if (strncmp(cmd, "extract", strlen(cmd)) != 0)
+ goto bad;
+ createfiles();
+ createlinks();
+ setdirmodes(0);
+ if (dflag)
+ checkrestore();
+ volno = 0;
+ break;
+ /*
+ * List available commands.
+ */
+ case 'h':
+ if (strncmp(cmd, "help", strlen(cmd)) != 0)
+ goto bad;
+ case '?':
+ fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ "Available commands are:\n",
+ "\tls [arg] - list directory\n",
+ "\tcd arg - change directory\n",
+ "\tpwd - print current directory\n",
+ "\tadd [arg] - add `arg' to list of",
+ " files to be extracted\n",
+ "\tdelete [arg] - delete `arg' from",
+ " list of files to be extracted\n",
+ "\textract - extract requested files\n",
+ "\tsetmodes - set modes of requested directories\n",
+ "\tquit - immediately exit program\n",
+ "\twhat - list dump header information\n",
+ "\tverbose - toggle verbose flag",
+ " (useful with ``ls'')\n",
+ "\thelp or `?' - print this list\n",
+ "If no `arg' is supplied, the current",
+ " directory is used\n");
+ break;
+ /*
+ * List a directory.
+ */
+ case 'l':
+ if (strncmp(cmd, "ls", strlen(cmd)) != 0)
+ goto bad;
+ printlist(name, curdir);
+ break;
+ /*
+ * Print current directory.
+ */
+ case 'p':
+ if (strncmp(cmd, "pwd", strlen(cmd)) != 0)
+ goto bad;
+ if (curdir[1] == '\0')
+ fprintf(stderr, "/\n");
+ else
+ fprintf(stderr, "%s\n", &curdir[1]);
+ break;
+ /*
+ * Quit.
+ */
+ case 'q':
+ if (strncmp(cmd, "quit", strlen(cmd)) != 0)
+ goto bad;
+ return;
+ case 'x':
+ if (strncmp(cmd, "xit", strlen(cmd)) != 0)
+ goto bad;
+ return;
+ /*
+ * Toggle verbose mode.
+ */
+ case 'v':
+ if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
+ goto bad;
+ if (vflag) {
+ fprintf(stderr, "verbose mode off\n");
+ vflag = 0;
+ break;
+ }
+ fprintf(stderr, "verbose mode on\n");
+ vflag++;
+ break;
+ /*
+ * Just restore requested directory modes.
+ */
+ case 's':
+ if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
+ goto bad;
+ setdirmodes(FORCE);
+ break;
+ /*
+ * Print out dump header information.
+ */
+ case 'w':
+ if (strncmp(cmd, "what", strlen(cmd)) != 0)
+ goto bad;
+ printdumpinfo();
+ break;
+ /*
+ * Turn on debugging.
+ */
+ case 'D':
+ if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
+ goto bad;
+ if (dflag) {
+ fprintf(stderr, "debugging mode off\n");
+ dflag = 0;
+ break;
+ }
+ fprintf(stderr, "debugging mode on\n");
+ dflag++;
+ break;
+ /*
+ * Unknown command.
+ */
+ default:
+ bad:
+ fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
+ break;
+ }
+ goto loop;
+}
+
+/*
+ * Read and parse an interactive command.
+ * The first word on the line is assigned to "cmd". If
+ * there are no arguments on the command line, then "curdir"
+ * is returned as the argument. If there are arguments
+ * on the line they are returned one at a time on each
+ * successive call to getcmd. Each argument is first assigned
+ * to "name". If it does not start with "/" the pathname in
+ * "curdir" is prepended to it. Finally "canon" is called to
+ * eliminate any embedded ".." components.
+ */
+static void
+getcmd(char *curdir, char *cmd, char *name, int size, struct arglist *ap)
+{
+ char *cp;
+ static char input[BUFSIZ];
+ char output[BUFSIZ];
+# define rawname input /* save space by reusing input buffer */
+
+ /*
+ * Check to see if still processing arguments.
+ */
+ if (ap->argcnt > 0)
+ goto retnext;
+ if (nextarg != NULL)
+ goto getnext;
+ /*
+ * Read a command line and trim off trailing white space.
+ */
+ do {
+ fprintf(stderr, "restore > ");
+ (void) fflush(stderr);
+ if (fgets(input, BUFSIZ, terminal) == NULL) {
+ strcpy(cmd, "quit");
+ return;
+ }
+ } while (input[0] == '\n');
+ for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
+ /* trim off trailing white space and newline */;
+ *++cp = '\0';
+ /*
+ * Copy the command into "cmd".
+ */
+ cp = copynext(input, cmd);
+ ap->cmd = cmd;
+ /*
+ * If no argument, use curdir as the default.
+ */
+ if (*cp == '\0') {
+ (void) strncpy(name, curdir, size);
+ name[size - 1] = '\0';
+ return;
+ }
+ nextarg = cp;
+ /*
+ * Find the next argument.
+ */
+getnext:
+ cp = copynext(nextarg, rawname);
+ if (*cp == '\0')
+ nextarg = NULL;
+ else
+ nextarg = cp;
+ /*
+ * If it is an absolute pathname, canonicalize it and return it.
+ */
+ if (rawname[0] == '/') {
+ canon(rawname, name, size);
+ } else {
+ /*
+ * For relative pathnames, prepend the current directory to
+ * it then canonicalize and return it.
+ */
+ snprintf(output, sizeof(output), "%s/%s", curdir, rawname);
+ canon(output, name, size);
+ }
+ switch (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob)) {
+ case GLOB_NOSPACE:
+ fprintf(stderr, "%s: out of memory\n", ap->cmd);
+ break;
+ case GLOB_NOMATCH:
+ fprintf(stderr, "%s %s: no such file or directory\n", ap->cmd, name);
+ break;
+ }
+ if (ap->glob.gl_pathc == 0)
+ return;
+ ap->freeglob = 1;
+ ap->argcnt = ap->glob.gl_pathc;
+
+retnext:
+ strncpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt], size);
+ name[size - 1] = '\0';
+ if (--ap->argcnt == 0) {
+ ap->freeglob = 0;
+ globfree(&ap->glob);
+ }
+# undef rawname
+}
+
+/*
+ * Strip off the next token of the input.
+ */
+static char *
+copynext(char *input, char *output)
+{
+ char *cp, *bp;
+ char quote;
+
+ for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
+ /* skip to argument */;
+ bp = output;
+ while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
+ /*
+ * Handle back slashes.
+ */
+ if (*cp == '\\') {
+ if (*++cp == '\0') {
+ fprintf(stderr,
+ "command lines cannot be continued\n");
+ continue;
+ }
+ *bp++ = *cp++;
+ continue;
+ }
+ /*
+ * The usual unquoted case.
+ */
+ if (*cp != '\'' && *cp != '"') {
+ *bp++ = *cp++;
+ continue;
+ }
+ /*
+ * Handle single and double quotes.
+ */
+ quote = *cp++;
+ while (*cp != quote && *cp != '\0')
+ *bp++ = *cp++;
+ if (*cp++ == '\0') {
+ fprintf(stderr, "missing %c\n", quote);
+ cp--;
+ continue;
+ }
+ }
+ *bp = '\0';
+ return (cp);
+}
+
+/*
+ * Canonicalize file names to always start with ``./'' and
+ * remove any embedded "." and ".." components.
+ */
+void
+canon(char *rawname, char *canonname, int len)
+{
+ char *cp, *np;
+
+ if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
+ (void) strcpy(canonname, "");
+ else if (rawname[0] == '/')
+ (void) strcpy(canonname, ".");
+ else
+ (void) strcpy(canonname, "./");
+ if (strlen(canonname) + strlen(rawname) >= len) {
+ fprintf(stderr, "canonname: not enough buffer space\n");
+ done(1);
+ }
+
+ (void) strcat(canonname, rawname);
+ /*
+ * Eliminate multiple and trailing '/'s
+ */
+ for (cp = np = canonname; *np != '\0'; cp++) {
+ *cp = *np++;
+ while (*cp == '/' && *np == '/')
+ np++;
+ }
+ *cp = '\0';
+ if (*--cp == '/')
+ *cp = '\0';
+ /*
+ * Eliminate extraneous "." and ".." from pathnames.
+ */
+ for (np = canonname; *np != '\0'; ) {
+ np++;
+ cp = np;
+ while (*np != '/' && *np != '\0')
+ np++;
+ if (np - cp == 1 && *cp == '.') {
+ cp--;
+ (void) strcpy(cp, np);
+ np = cp;
+ }
+ if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
+ cp--;
+ while (cp > &canonname[1] && *--cp != '/')
+ /* find beginning of name */;
+ (void) strcpy(cp, np);
+ np = cp;
+ }
+ }
+}
+
+/*
+ * Do an "ls" style listing of a directory
+ */
+static void
+printlist(char *name, char *basename)
+{
+ struct afile *fp, *list, *listp;
+ struct direct *dp;
+ struct afile single;
+ RST_DIR *dirp;
+ int entries, len, namelen;
+ char locname[MAXPATHLEN + 1];
+
+ dp = pathsearch(name);
+ if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
+ (!vflag && dp->d_ino == WINO))
+ return;
+ if ((dirp = rst_opendir(name)) == NULL) {
+ entries = 1;
+ list = &single;
+ mkentry(name, dp, list);
+ len = strlen(basename) + 1;
+ if (strlen(name) - len > single.len) {
+ freename(single.fname);
+ single.fname = savename(&name[len]);
+ single.len = strlen(single.fname);
+ }
+ } else {
+ entries = 0;
+ while ((dp = rst_readdir(dirp)))
+ entries++;
+ rst_closedir(dirp);
+ list = (struct afile *)malloc(entries * sizeof(struct afile));
+ if (list == NULL) {
+ fprintf(stderr, "ls: out of memory\n");
+ return;
+ }
+ if ((dirp = rst_opendir(name)) == NULL)
+ panic("directory reopen failed\n");
+ fprintf(stderr, "%s:\n", name);
+ entries = 0;
+ listp = list;
+ (void) strncpy(locname, name, MAXPATHLEN);
+ (void) strncat(locname, "/", MAXPATHLEN);
+ namelen = strlen(locname);
+ while ((dp = rst_readdir(dirp))) {
+ if (dp == NULL)
+ break;
+ if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
+ continue;
+ if (!vflag && (dp->d_ino == WINO ||
+ strcmp(dp->d_name, ".") == 0 ||
+ strcmp(dp->d_name, "..") == 0))
+ continue;
+ locname[namelen] = '\0';
+ if (namelen + dp->d_namlen >= MAXPATHLEN) {
+ fprintf(stderr, "%s%s: name exceeds %d char\n",
+ locname, dp->d_name, MAXPATHLEN);
+ } else {
+ (void) strncat(locname, dp->d_name,
+ (int)dp->d_namlen);
+ mkentry(locname, dp, listp++);
+ entries++;
+ }
+ }
+ rst_closedir(dirp);
+ if (entries == 0) {
+ fprintf(stderr, "\n");
+ free(list);
+ return;
+ }
+ qsort((char *)list, entries, sizeof(struct afile), fcmp);
+ }
+ formatf(list, entries);
+ if (dirp != NULL) {
+ for (fp = listp - 1; fp >= list; fp--)
+ freename(fp->fname);
+ fprintf(stderr, "\n");
+ free(list);
+ }
+}
+
+/*
+ * Read the contents of a directory.
+ */
+static void
+mkentry(char *name, struct direct *dp, struct afile *fp)
+{
+ char *cp;
+ struct entry *np;
+
+ fp->fnum = dp->d_ino;
+ fp->fname = savename(dp->d_name);
+ for (cp = fp->fname; *cp; cp++)
+ if (!vflag && !isprint((unsigned char)*cp))
+ *cp = '?';
+ fp->len = cp - fp->fname;
+ if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
+ fp->prefix = '^';
+ else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
+ fp->prefix = '*';
+ else
+ fp->prefix = ' ';
+ switch(dp->d_type) {
+
+ default:
+ fprintf(stderr, "Warning: undefined file type %d\n",
+ dp->d_type);
+ /* FALLTHROUGH */
+ case DT_REG:
+ fp->postfix = ' ';
+ break;
+
+ case DT_LNK:
+ fp->postfix = '@';
+ break;
+
+ case DT_FIFO:
+ case DT_SOCK:
+ fp->postfix = '=';
+ break;
+
+ case DT_CHR:
+ case DT_BLK:
+ fp->postfix = '#';
+ break;
+
+ case DT_WHT:
+ fp->postfix = '%';
+ break;
+
+ case DT_UNKNOWN:
+ case DT_DIR:
+ if (inodetype(dp->d_ino) == NODE)
+ fp->postfix = '/';
+ else
+ fp->postfix = ' ';
+ break;
+ }
+ return;
+}
+
+/*
+ * Print out a pretty listing of a directory
+ */
+static void
+formatf(struct afile *list, int nentry)
+{
+ struct afile *fp, *endlist;
+ int width, bigino, haveprefix, havepostfix;
+ int i, j, w, precision, columns, lines;
+
+ width = 0;
+ haveprefix = 0;
+ havepostfix = 0;
+ bigino = ROOTINO;
+ endlist = &list[nentry];
+ for (fp = &list[0]; fp < endlist; fp++) {
+ if (bigino < fp->fnum)
+ bigino = fp->fnum;
+ if (width < fp->len)
+ width = fp->len;
+ if (fp->prefix != ' ')
+ haveprefix = 1;
+ if (fp->postfix != ' ')
+ havepostfix = 1;
+ }
+ if (haveprefix)
+ width++;
+ if (havepostfix)
+ width++;
+ if (vflag) {
+ for (precision = 0, i = bigino; i > 0; i /= 10)
+ precision++;
+ width += precision + 1;
+ }
+ width++;
+ columns = 81 / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (nentry + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < columns; j++) {
+ fp = &list[j * lines + i];
+ if (vflag) {
+ fprintf(stderr, "%*d ", precision, fp->fnum);
+ fp->len += precision + 1;
+ }
+ if (haveprefix) {
+ putc(fp->prefix, stderr);
+ fp->len++;
+ }
+ fprintf(stderr, "%s", fp->fname);
+ if (havepostfix) {
+ putc(fp->postfix, stderr);
+ fp->len++;
+ }
+ if (fp + lines >= endlist) {
+ fprintf(stderr, "\n");
+ break;
+ }
+ for (w = fp->len; w < width; w++)
+ putc(' ', stderr);
+ }
+ }
+}
+
+/*
+ * Skip over directory entries that are not on the tape
+ *
+ * First have to get definition of a dirent.
+ */
+#undef DIRBLKSIZ
+#include <dirent.h>
+#undef d_ino
+
+struct dirent *
+glob_readdir(void *dirp)
+{
+ struct direct *dp;
+ static struct dirent adirent;
+
+ while ((dp = rst_readdir(dirp)) != NULL) {
+ if (!vflag && dp->d_ino == WINO)
+ continue;
+ if (dflag || TSTINO(dp->d_ino, dumpmap))
+ break;
+ }
+ if (dp == NULL)
+ return (NULL);
+ adirent.d_fileno = dp->d_ino;
+ adirent.d_namlen = dp->d_namlen;
+ memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
+ return (&adirent);
+}
+
+/*
+ * Return st_mode information in response to stat or lstat calls
+ */
+static int
+glob_stat(const char *name, struct stat *stp)
+{
+ struct direct *dp;
+
+ dp = pathsearch(name);
+ if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
+ (!vflag && dp->d_ino == WINO))
+ return (-1);
+ if (inodetype(dp->d_ino) == NODE)
+ stp->st_mode = IFDIR;
+ else
+ stp->st_mode = IFREG;
+ return (0);
+}
+
+/*
+ * Comparison routine for qsort.
+ */
+static int
+fcmp(const void *f1, const void *f2)
+{
+ return (strcoll(((struct afile *)f1)->fname,
+ ((struct afile *)f2)->fname));
+}
+
+/*
+ * respond to interrupts
+ */
+void
+onintr(int signo __unused)
+{
+ if (command == 'i' && runshell)
+ longjmp(reset, 1);
+ if (reply("restore interrupted, continue") == FAIL)
+ done(1);
+}
diff --git a/sbin/restore/main.c b/sbin/restore/main.c
new file mode 100644
index 0000000..63fb4ef
--- /dev/null
+++ b/sbin/restore/main.c
@@ -0,0 +1,373 @@
+/*
+ * 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/4/95";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/dinode.h>
+#include <protocols/dumprestore.h>
+
+#include <err.h>
+#include <limits.h>
+#include <locale.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "restore.h"
+#include "extern.h"
+
+int bflag = 0, cvtflag = 0, dflag = 0, vflag = 0, yflag = 0;
+int hflag = 1, mflag = 1, Nflag = 0;
+int uflag = 0;
+int pipecmd = 0;
+char command = '\0';
+long dumpnum = 1;
+long volno = 0;
+long ntrec;
+char *dumpmap;
+char *usedinomap;
+ino_t maxino;
+time_t dumptime;
+time_t dumpdate;
+FILE *terminal;
+
+static void obsolete(int *, char **[]);
+static void usage(void) __dead2;
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ ino_t ino;
+ char *inputdev;
+ char *symtbl = "./restoresymtable";
+ char *p, name[MAXPATHLEN];
+
+ /* Temp files should *not* be readable. We set permissions later. */
+ (void) umask(077);
+
+ if (argc < 2)
+ usage();
+
+ (void)setlocale(LC_ALL, "");
+
+ inputdev = NULL;
+ obsolete(&argc, &argv);
+ while ((ch = getopt(argc, argv, "b:df:himNP:Rrs:tuvxy")) != -1)
+ switch(ch) {
+ case 'b':
+ /* Change default tape blocksize. */
+ bflag = 1;
+ ntrec = strtol(optarg, &p, 10);
+ if (*p)
+ errx(1, "illegal blocksize -- %s", optarg);
+ if (ntrec <= 0)
+ errx(1, "block size must be greater than 0");
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'f':
+ if (pipecmd)
+ errx(1,
+ "-P and -f options are mutually exclusive");
+ inputdev = optarg;
+ break;
+ case 'P':
+ if (!pipecmd && inputdev)
+ errx(1,
+ "-P and -f options are mutually exclusive");
+ inputdev = optarg;
+ pipecmd = 1;
+ break;
+ case 'h':
+ hflag = 0;
+ break;
+ case 'i':
+ case 'R':
+ case 'r':
+ case 't':
+ case 'x':
+ if (command != '\0')
+ errx(1,
+ "%c and %c options are mutually exclusive",
+ ch, command);
+ command = ch;
+ break;
+ case 'm':
+ mflag = 0;
+ break;
+ case 'N':
+ Nflag = 1;
+ break;
+ case 's':
+ /* Dumpnum (skip to) for multifile dump tapes. */
+ dumpnum = strtol(optarg, &p, 10);
+ if (*p)
+ errx(1, "illegal dump number -- %s", optarg);
+ if (dumpnum <= 0)
+ errx(1, "dump number must be greater than 0");
+ break;
+ case 'u':
+ uflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ case 'y':
+ yflag = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (command == '\0')
+ errx(1, "none of i, R, r, t or x options specified");
+
+ if (signal(SIGINT, onintr) == SIG_IGN)
+ (void) signal(SIGINT, SIG_IGN);
+ if (signal(SIGTERM, onintr) == SIG_IGN)
+ (void) signal(SIGTERM, SIG_IGN);
+ setlinebuf(stderr);
+
+ if (inputdev == NULL && (inputdev = getenv("TAPE")) == NULL)
+ inputdev = _PATH_DEFTAPE;
+ setinput(inputdev, pipecmd);
+
+ if (argc == 0) {
+ argc = 1;
+ *--argv = ".";
+ }
+
+ switch (command) {
+ /*
+ * Interactive mode.
+ */
+ case 'i':
+ setup();
+ extractdirs(1);
+ initsymtable(NULL);
+ runcmdshell();
+ break;
+ /*
+ * Incremental restoration of a file system.
+ */
+ case 'r':
+ setup();
+ if (dumptime > 0) {
+ /*
+ * This is an incremental dump tape.
+ */
+ vprintf(stdout, "Begin incremental restore\n");
+ initsymtable(symtbl);
+ extractdirs(1);
+ removeoldleaves();
+ vprintf(stdout, "Calculate node updates.\n");
+ treescan(".", ROOTINO, nodeupdates);
+ findunreflinks();
+ removeoldnodes();
+ } else {
+ /*
+ * This is a level zero dump tape.
+ */
+ vprintf(stdout, "Begin level 0 restore\n");
+ initsymtable((char *)0);
+ extractdirs(1);
+ vprintf(stdout, "Calculate extraction list.\n");
+ treescan(".", ROOTINO, nodeupdates);
+ }
+ createleaves(symtbl);
+ createlinks();
+ setdirmodes(FORCE);
+ checkrestore();
+ if (dflag) {
+ vprintf(stdout, "Verify the directory structure\n");
+ treescan(".", ROOTINO, verifyfile);
+ }
+ dumpsymtable(symtbl, (long)1);
+ break;
+ /*
+ * Resume an incremental file system restoration.
+ */
+ case 'R':
+ initsymtable(symtbl);
+ skipmaps();
+ skipdirs();
+ createleaves(symtbl);
+ createlinks();
+ setdirmodes(FORCE);
+ checkrestore();
+ dumpsymtable(symtbl, (long)1);
+ break;
+ /*
+ * List contents of tape.
+ */
+ case 't':
+ setup();
+ extractdirs(0);
+ initsymtable((char *)0);
+ while (argc--) {
+ canon(*argv++, name, sizeof(name));
+ ino = dirlookup(name);
+ if (ino == 0)
+ continue;
+ treescan(name, ino, listfile);
+ }
+ break;
+ /*
+ * Batch extraction of tape contents.
+ */
+ case 'x':
+ setup();
+ extractdirs(1);
+ initsymtable((char *)0);
+ while (argc--) {
+ canon(*argv++, name, sizeof(name));
+ ino = dirlookup(name);
+ if (ino == 0)
+ continue;
+ if (mflag)
+ pathcheck(name);
+ treescan(name, ino, addfile);
+ }
+ createfiles();
+ createlinks();
+ setdirmodes(0);
+ if (dflag)
+ checkrestore();
+ break;
+ }
+ done(0);
+ /* NOTREACHED */
+}
+
+static void
+usage()
+{
+ const char *const common =
+ "[-b blocksize] [-f file | -P pipecommand] [-s fileno]";
+ const char *const fileell = "[file ...]";
+
+ (void)fprintf(stderr, "usage:\t%s %s\n\t%s %s\n\t%s %s\n"
+ "\t%s %s %s\n\t%s %s %s\n",
+ "restore -i [-dhmNuvy]", common,
+ "restore -R [-dNuvy]", common,
+ "restore -r [-dNuvy]", common,
+ "restore -t [-dhNuvy]", common, fileell,
+ "restore -x [-dhmNuvy]", common, fileell);
+ done(1);
+}
+
+/*
+ * obsolete --
+ * Change set of key letters and ordered arguments into something
+ * getopt(3) will like.
+ */
+static void
+obsolete(int *argcp, char **argvp[])
+{
+ int argc, flags;
+ char *ap, **argv, *flagsp, **nargv, *p;
+
+ /* Setup. */
+ argv = *argvp;
+ argc = *argcp;
+
+ /* Return if no arguments or first argument has leading dash. */
+ ap = argv[1];
+ if (argc == 1 || *ap == '-')
+ return;
+
+ /* Allocate space for new arguments. */
+ if ((*argvp = nargv = malloc((argc + 1) * sizeof(char *))) == NULL ||
+ (p = flagsp = malloc(strlen(ap) + 2)) == NULL)
+ err(1, NULL);
+
+ *nargv++ = *argv;
+ argv += 2, argc -= 2;
+
+ for (flags = 0; *ap; ++ap) {
+ switch (*ap) {
+ case 'b':
+ case 'f':
+ case 's':
+ if (*argv == NULL) {
+ warnx("option requires an argument -- %c", *ap);
+ usage();
+ }
+ if ((nargv[0] = malloc(strlen(*argv) + 2 + 1)) == NULL)
+ err(1, NULL);
+ nargv[0][0] = '-';
+ nargv[0][1] = *ap;
+ (void)strcpy(&nargv[0][2], *argv);
+ ++argv;
+ ++nargv;
+ break;
+ default:
+ if (!flags) {
+ *p++ = '-';
+ flags = 1;
+ }
+ *p++ = *ap;
+ break;
+ }
+ }
+
+ /* Terminate flags. */
+ if (flags) {
+ *p = '\0';
+ *nargv++ = flagsp;
+ }
+
+ /* Copy remaining arguments. */
+ while ((*nargv++ = *argv++));
+
+ /* Update argument count. */
+ *argcp = nargv - *argvp - 1;
+}
diff --git a/sbin/restore/restore.8 b/sbin/restore/restore.8
new file mode 100644
index 0000000..1efea75
--- /dev/null
+++ b/sbin/restore/restore.8
@@ -0,0 +1,497 @@
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)restore.8 8.4 (Berkeley) 5/1/95
+.\" $FreeBSD$
+.\"
+.Dd May 1, 1995
+.Dt RESTORE 8
+.Os
+.Sh NAME
+.Nm restore ,
+.Nm rrestore
+.Nd "restore files or file systems from backups made with dump"
+.Sh SYNOPSIS
+.Nm
+.Fl i
+.Op Fl dhmNuvy
+.Op Fl b Ar blocksize
+.Op Fl f Ar file | Fl P Ar pipecommand
+.Op Fl s Ar fileno
+.Nm
+.Fl R
+.Op Fl dNuvy
+.Op Fl b Ar blocksize
+.Op Fl f Ar file | Fl P Ar pipecommand
+.Op Fl s Ar fileno
+.Nm
+.Fl r
+.Op Fl dNuvy
+.Op Fl b Ar blocksize
+.Op Fl f Ar file | Fl P Ar pipecommand
+.Op Fl s Ar fileno
+.Nm
+.Fl t
+.Op Fl dhNuvy
+.Op Fl b Ar blocksize
+.Op Fl f Ar file | Fl P Ar pipecommand
+.Op Fl s Ar fileno
+.Op Ar
+.Nm
+.Fl x
+.Op Fl dhmNuvy
+.Op Fl b Ar blocksize
+.Op Fl f Ar file | Fl P Ar pipecommand
+.Op Fl s Ar fileno
+.Op Ar
+.Pp
+.Nm rrestore
+is an alternate name for
+.Nm .
+.Pp
+.in \" XXX
+(The
+.Bx 4.3
+option syntax is implemented for backward compatibility, but
+is not documented here.)
+.Sh DESCRIPTION
+The
+.Nm
+utility performs the inverse function of
+.Xr dump 8 .
+A full backup of a file system may be restored and
+subsequent incremental backups layered on top of it.
+Single files and
+directory subtrees may be restored from full or partial
+backups.
+The
+.Nm
+utility works across a network;
+to do this see the
+.Fl f
+and
+.Fl P
+flags described below.
+Other arguments to the command are file or directory
+names specifying the files that are to be restored.
+Unless the
+.Fl h
+flag is specified (see below),
+the appearance of a directory name refers to
+the files and (recursively) subdirectories of that directory.
+.Pp
+Exactly one of the following flags is required:
+.Bl -tag -width Ds
+.It Fl i
+This mode allows interactive restoration of files from a dump.
+After reading in the directory information from the dump,
+.Nm
+provides a shell like interface that allows the user to move
+around the directory tree selecting files to be extracted.
+The available commands are given below;
+for those commands that require an argument,
+the default is the current directory.
+.Bl -tag -width Fl
+.It Ic add Op Ar arg
+The current directory or specified argument is added to the list of
+files to be extracted.
+If a directory is specified, then it and all its descendents are
+added to the extraction list
+(unless the
+.Fl h
+flag is specified on the command line).
+Files that are on the extraction list are prepended with a ``*''
+when they are listed by
+.Ic ls .
+.It Ic \&cd Ar arg
+Change the current working directory to the specified argument.
+.It Ic delete Op Ar arg
+The current directory or specified argument is deleted from the list of
+files to be extracted.
+If a directory is specified, then it and all its descendents are
+deleted from the extraction list
+(unless the
+.Fl h
+flag is specified on the command line).
+The most expedient way to extract most of the files from a directory
+is to add the directory to the extraction list and then delete
+those files that are not needed.
+.It Ic extract
+All the files that are on the extraction list are extracted
+from the dump.
+The
+.Nm
+utility will ask which volume the user wishes to mount.
+The fastest way to extract a few files is to
+start with the last volume, and work towards the first volume.
+.It Ic help
+List a summary of the available commands.
+.It Ic \&ls Op Ar arg
+List the current or specified directory.
+Entries that are directories are appended with a ``/''.
+Entries that have been marked for extraction are prepended with a ``*''.
+If the verbose
+flag is set the inode number of each entry is also listed.
+.It Ic pwd
+Print the full pathname of the current working directory.
+.It Ic quit
+Exit immediately,
+even if the extraction list is not empty.
+.It Ic setmodes
+All the directories that have been added to the extraction list
+have their owner, modes, and times set;
+nothing is extracted from the dump.
+This is useful for cleaning up after a restore has been prematurely aborted.
+.It Ic verbose
+The sense of the
+.Fl v
+flag is toggled.
+When set, the verbose flag causes the
+.Ic ls
+command to list the inode numbers of all entries.
+It also causes
+.Nm
+to print out information about each file as it is extracted.
+.It Ic what
+Display dump header information, which includes: date,
+level, label, and the file system and host dump was made
+from.
+.El
+.It Fl R
+Request a particular tape of a multi volume set on which to restart
+a full restore
+(see the
+.Fl r
+flag below).
+This is useful if the restore has been interrupted.
+.It Fl r
+Restore (rebuild a file system).
+The target file system should be made pristine with
+.Xr newfs 8 ,
+mounted and the user
+.Xr cd 1 Ns 'd
+into the pristine file system
+before starting the restoration of the initial level 0 backup.
+If the
+level 0 restores successfully, the
+.Fl r
+flag may be used to restore
+any necessary incremental backups on top of the level 0.
+The
+.Fl r
+flag precludes an interactive file extraction and can be
+detrimental to one's health if not used carefully (not to mention
+the disk).
+An example:
+.Bd -literal -offset indent
+newfs /dev/da0s1a
+mount /dev/da0s1a /mnt
+cd /mnt
+
+restore rf /dev/sa0
+.Ed
+.Pp
+Note that
+.Nm
+leaves a file
+.Pa restoresymtable
+in the root directory to pass information between incremental
+restore passes.
+This file should be removed when the last incremental has been
+restored.
+.Pp
+The
+.Nm
+utility ,
+in conjunction with
+.Xr newfs 8
+and
+.Xr dump 8 ,
+may be used to modify file system parameters
+such as size or block size.
+.It Fl t
+The names of the specified files are listed if they occur
+on the backup.
+If no file argument is given,
+then the root directory is listed,
+which results in the entire content of the
+backup being listed,
+unless the
+.Fl h
+flag has been specified.
+Note that the
+.Fl t
+flag replaces the function of the old
+.Xr dumpdir 8
+program.
+.It Fl x
+The named files are read from the given media.
+If a named file matches a directory whose contents
+are on the backup
+and the
+.Fl h
+flag is not specified,
+the directory is recursively extracted.
+The owner, modification time,
+and mode are restored (if possible).
+If no file argument is given,
+then the root directory is extracted,
+which results in the entire content of the
+backup being extracted,
+unless the
+.Fl h
+flag has been specified.
+.El
+.Pp
+The following additional options may be specified:
+.Bl -tag -width Ds
+.It Fl b Ar blocksize
+The number of kilobytes per dump record.
+If the
+.Fl b
+option is not specified,
+.Nm
+tries to determine the media block size dynamically.
+.It Fl d
+Sends verbose debugging output to the standard error.
+.It Fl f Ar file
+Read the backup from
+.Ar file ;
+.Ar file
+may be a special device file
+like
+.Pa /dev/sa0
+(a tape drive),
+.Pa /dev/da1c
+(a disk drive),
+an ordinary file,
+or
+.Sq Fl
+(the standard input).
+If the name of the file is of the form
+.Dq host:file ,
+or
+.Dq user@host:file ,
+.Nm
+reads from the named file on the remote host using
+.Xr rmt 8 .
+.It Fl P Ar pipecommand
+Use
+.Xr popen 3
+to execute the
+.Xr sh 1
+script string defined by
+.Ar pipecommand
+as the input for every volume in the backup.
+This child pipeline's
+.Dv stdout
+.Pq Pa /dev/fd/1
+is redirected to the
+.Nm
+input stream, and the environment variable
+.Ev RESTORE_VOLUME
+is set to the current volume number being read.
+The
+.Ar pipecommand
+script is started each time a volume is loaded, as if it were a tape drive.
+.It Fl h
+Extract the actual directory,
+rather than the files that it references.
+This prevents hierarchical restoration of complete subtrees
+from the dump.
+.It Fl m
+Extract by inode numbers rather than by file name.
+This is useful if only a few files are being extracted,
+and one wants to avoid regenerating the complete pathname
+to the file.
+.It Fl N
+Do the extraction normally, but do not actually write any changes
+to disk.
+This can be used to check the integrity of dump media
+or other test purposes.
+.It Fl s Ar fileno
+Read from the specified
+.Ar fileno
+on a multi-file tape.
+File numbering starts at 1.
+.It Fl u
+When creating certain types of files, restore may generate a warning
+diagnostic if they already exist in the target directory.
+To prevent this, the
+.Fl u
+(unlink) flag causes restore to remove old entries before attempting
+to create new ones.
+.It Fl v
+Normally
+.Nm
+does its work silently.
+The
+.Fl v
+(verbose)
+flag causes it to type the name of each file it treats
+preceded by its file type.
+.It Fl y
+Do not ask the user whether to abort the restore in the event of an error.
+Always try to skip over the bad block(s) and continue.
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width ".Ev TMPDIR"
+.It Ev TAPE
+Device from which to read backup.
+.It Ev TMPDIR
+Name of directory where temporary files are to be created.
+.El
+.Sh FILES
+.Bl -tag -width "./restoresymtable" -compact
+.It Pa /dev/sa0
+the default tape drive
+.It Pa /tmp/rstdir*
+file containing directories on the tape.
+.It Pa /tmp/rstmode*
+owner, mode, and time stamps for directories.
+.It Pa \&./restoresymtable
+information passed between incremental restores.
+.El
+.Sh DIAGNOSTICS
+The
+.Nm
+utility complains if it gets a read error.
+If
+.Fl y
+has been specified, or the user responds
+.Ql y ,
+.Nm
+will attempt to continue the restore.
+.Pp
+If a backup was made using more than one tape volume,
+.Nm
+will notify the user when it is time to mount the next volume.
+If the
+.Fl x
+or
+.Fl i
+flag has been specified,
+.Nm
+will also ask which volume the user wishes to mount.
+The fastest way to extract a few files is to
+start with the last volume, and work towards the first volume.
+.Pp
+There are numerous consistency checks that can be listed by
+.Nm .
+Most checks are self-explanatory or can ``never happen''.
+Common errors are given below.
+.Pp
+.Bl -tag -width Ds -compact
+.Pp
+.It <filename>: not found on tape
+The specified file name was listed in the tape directory,
+but was not found on the tape.
+This is caused by tape read errors while looking for the file,
+and from using a dump tape created on an active file system.
+.Pp
+.It expected next file <inumber>, got <inumber>
+A file that was not listed in the directory showed up.
+This can occur when using a dump created on an active file system.
+.Pp
+.It Incremental dump too low
+When doing incremental restore,
+a dump that was written before the previous incremental dump,
+or that has too low an incremental level has been loaded.
+.Pp
+.It Incremental dump too high
+When doing incremental restore,
+a dump that does not begin its coverage where the previous incremental
+dump left off,
+or that has too high an incremental level has been loaded.
+.Pp
+.It Tape read error while restoring <filename>
+.It Tape read error while skipping over inode <inumber>
+.It Tape read error while trying to resynchronize
+A tape (or other media) read error has occurred.
+If a file name is specified,
+then its contents are probably partially wrong.
+If an inode is being skipped or the tape is trying to resynchronize,
+then no extracted files have been corrupted,
+though files may not be found on the tape.
+.Pp
+.It resync restore, skipped <num> blocks
+After a dump read error,
+.Nm
+may have to resynchronize itself.
+This message lists the number of blocks that were skipped over.
+.El
+.Sh SEE ALSO
+.Xr dump 8 ,
+.Xr mount 8 ,
+.Xr newfs 8 ,
+.Xr rmt 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
+.Sh BUGS
+The
+.Nm
+utility can get confused when doing incremental restores from
+dumps that were made on active file systems.
+.Pp
+A level zero dump must be done after a full restore.
+Because restore runs in user code,
+it has no control over inode allocation;
+thus a full dump must be done to get a new set of directories
+reflecting the new inode numbering,
+even though the contents of the files is unchanged.
+.Pp
+To do a network restore, you have to run restore as root.
+This is due
+to the previous security history of dump and restore.
+(restore is
+written to be setuid root, but we are not certain all bugs are gone
+from the restore code - run setuid at your own risk.)
+.Pp
+The temporary files
+.Pa /tmp/rstdir*
+and
+.Pa /tmp/rstmode*
+are generated with a unique name based on the date of the dump
+and the process ID (see
+.Xr mktemp 3 ) ,
+except for when
+.Fl r
+or
+.Fl R
+is used.
+Because
+.Fl R
+allows you to restart a
+.Fl r
+operation that may have been interrupted, the temporary files should
+be the same across different processes.
+In all other cases, the files are unique because it is possible to
+have two different dumps started at the same time, and separate
+operations should not conflict with each other.
diff --git a/sbin/restore/restore.c b/sbin/restore/restore.c
new file mode 100644
index 0000000..046c0d2
--- /dev/null
+++ b/sbin/restore/restore.c
@@ -0,0 +1,847 @@
+/*
+ * 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.
+ * 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 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 lint
+#if 0
+static char sccsid[] = "@(#)restore.c 8.3 (Berkeley) 9/13/94";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <ufs/ufs/dinode.h>
+
+#include "restore.h"
+#include "extern.h"
+
+static char *keyval(int);
+
+/*
+ * This implements the 't' option.
+ * List entries on the tape.
+ */
+long
+listfile(char *name, ino_t ino, int type)
+{
+ long descend = hflag ? GOOD : FAIL;
+
+ if (TSTINO(ino, dumpmap) == 0)
+ return (descend);
+ vprintf(stdout, "%s", type == LEAF ? "leaf" : "dir ");
+ fprintf(stdout, "%10d\t%s\n", ino, name);
+ return (descend);
+}
+
+/*
+ * This implements the 'x' option.
+ * Request that new entries be extracted.
+ */
+long
+addfile(char *name, ino_t ino, int type)
+{
+ struct entry *ep;
+ long descend = hflag ? GOOD : FAIL;
+ char buf[100];
+
+ if (TSTINO(ino, dumpmap) == 0) {
+ dprintf(stdout, "%s: not on the tape\n", name);
+ return (descend);
+ }
+ if (ino == WINO && command == 'i' && !vflag)
+ return (descend);
+ if (!mflag) {
+ (void) sprintf(buf, "./%u", ino);
+ name = buf;
+ if (type == NODE) {
+ (void) genliteraldir(name, ino);
+ return (descend);
+ }
+ }
+ ep = lookupino(ino);
+ if (ep != NULL) {
+ if (strcmp(name, myname(ep)) == 0) {
+ ep->e_flags |= NEW;
+ return (descend);
+ }
+ type |= LINK;
+ }
+ ep = addentry(name, ino, type);
+ if (type == NODE)
+ newnode(ep);
+ ep->e_flags |= NEW;
+ return (descend);
+}
+
+/*
+ * This is used by the 'i' option to undo previous requests made by addfile.
+ * Delete entries from the request queue.
+ */
+/* ARGSUSED */
+long
+deletefile(char *name, ino_t ino, int type)
+{
+ long descend = hflag ? GOOD : FAIL;
+ struct entry *ep;
+
+ if (TSTINO(ino, dumpmap) == 0)
+ return (descend);
+ ep = lookupname(name);
+ if (ep != NULL) {
+ ep->e_flags &= ~NEW;
+ ep->e_flags |= REMOVED;
+ if (ep->e_type != NODE)
+ freeentry(ep);
+ }
+ return (descend);
+}
+
+/*
+ * The following four routines implement the incremental
+ * restore algorithm. The first removes old entries, the second
+ * does renames and calculates the extraction list, the third
+ * cleans up link names missed by the first two, and the final
+ * one deletes old directories.
+ *
+ * Directories cannot be immediately deleted, as they may have
+ * other files in them which need to be moved out first. As
+ * directories to be deleted are found, they are put on the
+ * following deletion list. After all deletions and renames
+ * are done, this list is actually deleted.
+ */
+static struct entry *removelist;
+
+/*
+ * Remove invalid whiteouts from the old tree.
+ * Remove unneeded leaves from the old tree.
+ * Remove directories from the lookup chains.
+ */
+void
+removeoldleaves(void)
+{
+ struct entry *ep, *nextep;
+ ino_t i, mydirino;
+
+ vprintf(stdout, "Mark entries to be removed.\n");
+ if ((ep = lookupino(WINO))) {
+ vprintf(stdout, "Delete whiteouts\n");
+ for ( ; ep != NULL; ep = nextep) {
+ nextep = ep->e_links;
+ mydirino = ep->e_parent->e_ino;
+ /*
+ * We remove all whiteouts that are in directories
+ * that have been removed or that have been dumped.
+ */
+ if (TSTINO(mydirino, usedinomap) &&
+ !TSTINO(mydirino, dumpmap))
+ continue;
+ delwhiteout(ep);
+ freeentry(ep);
+ }
+ }
+ for (i = ROOTINO + 1; i < maxino; i++) {
+ ep = lookupino(i);
+ if (ep == NULL)
+ continue;
+ if (TSTINO(i, usedinomap))
+ continue;
+ for ( ; ep != NULL; ep = ep->e_links) {
+ dprintf(stdout, "%s: REMOVE\n", myname(ep));
+ if (ep->e_type == LEAF) {
+ removeleaf(ep);
+ freeentry(ep);
+ } else {
+ mktempname(ep);
+ deleteino(ep->e_ino);
+ ep->e_next = removelist;
+ removelist = ep;
+ }
+ }
+ }
+}
+
+/*
+ * For each directory entry on the incremental tape, determine which
+ * category it falls into as follows:
+ * KEEP - entries that are to be left alone.
+ * NEW - new entries to be added.
+ * EXTRACT - files that must be updated with new contents.
+ * LINK - new links to be added.
+ * Renames are done at the same time.
+ */
+long
+nodeupdates(char *name, ino_t ino, int type)
+{
+ struct entry *ep, *np, *ip;
+ long descend = GOOD;
+ int lookuptype = 0;
+ int key = 0;
+ /* key values */
+# define ONTAPE 0x1 /* inode is on the tape */
+# define INOFND 0x2 /* inode already exists */
+# define NAMEFND 0x4 /* name already exists */
+# define MODECHG 0x8 /* mode of inode changed */
+
+ /*
+ * This routine is called once for each element in the
+ * directory hierarchy, with a full path name.
+ * The "type" value is incorrectly specified as LEAF for
+ * directories that are not on the dump tape.
+ *
+ * Check to see if the file is on the tape.
+ */
+ if (TSTINO(ino, dumpmap))
+ key |= ONTAPE;
+ /*
+ * Check to see if the name exists, and if the name is a link.
+ */
+ np = lookupname(name);
+ if (np != NULL) {
+ key |= NAMEFND;
+ ip = lookupino(np->e_ino);
+ if (ip == NULL)
+ panic("corrupted symbol table\n");
+ if (ip != np)
+ lookuptype = LINK;
+ }
+ /*
+ * Check to see if the inode exists, and if one of its links
+ * corresponds to the name (if one was found).
+ */
+ ip = lookupino(ino);
+ if (ip != NULL) {
+ key |= INOFND;
+ for (ep = ip->e_links; ep != NULL; ep = ep->e_links) {
+ if (ep == np) {
+ ip = ep;
+ break;
+ }
+ }
+ }
+ /*
+ * If both a name and an inode are found, but they do not
+ * correspond to the same file, then both the inode that has
+ * been found and the inode corresponding to the name that
+ * has been found need to be renamed. The current pathname
+ * is the new name for the inode that has been found. Since
+ * all files to be deleted have already been removed, the
+ * named file is either a now unneeded link, or it must live
+ * under a new name in this dump level. If it is a link, it
+ * can be removed. If it is not a link, it is given a
+ * temporary name in anticipation that it will be renamed
+ * when it is later found by inode number.
+ */
+ if (((key & (INOFND|NAMEFND)) == (INOFND|NAMEFND)) && ip != np) {
+ if (lookuptype == LINK) {
+ removeleaf(np);
+ freeentry(np);
+ } else {
+ dprintf(stdout, "name/inode conflict, mktempname %s\n",
+ myname(np));
+ mktempname(np);
+ }
+ np = NULL;
+ key &= ~NAMEFND;
+ }
+ if ((key & ONTAPE) &&
+ (((key & INOFND) && ip->e_type != type) ||
+ ((key & NAMEFND) && np->e_type != type)))
+ key |= MODECHG;
+
+ /*
+ * Decide on the disposition of the file based on its flags.
+ * Note that we have already handled the case in which
+ * a name and inode are found that correspond to different files.
+ * Thus if both NAMEFND and INOFND are set then ip == np.
+ */
+ switch (key) {
+
+ /*
+ * A previously existing file has been found.
+ * Mark it as KEEP so that other links to the inode can be
+ * detected, and so that it will not be reclaimed by the search
+ * for unreferenced names.
+ */
+ case INOFND|NAMEFND:
+ ip->e_flags |= KEEP;
+ dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+ flagvalues(ip));
+ break;
+
+ /*
+ * A file on the tape has a name which is the same as a name
+ * corresponding to a different file in the previous dump.
+ * Since all files to be deleted have already been removed,
+ * this file is either a now unneeded link, or it must live
+ * under a new name in this dump level. If it is a link, it
+ * can simply be removed. If it is not a link, it is given a
+ * temporary name in anticipation that it will be renamed
+ * when it is later found by inode number (see INOFND case
+ * below). The entry is then treated as a new file.
+ */
+ case ONTAPE|NAMEFND:
+ case ONTAPE|NAMEFND|MODECHG:
+ if (lookuptype == LINK) {
+ removeleaf(np);
+ freeentry(np);
+ } else {
+ mktempname(np);
+ }
+ /* FALLTHROUGH */
+
+ /*
+ * A previously non-existent file.
+ * Add it to the file system, and request its extraction.
+ * If it is a directory, create it immediately.
+ * (Since the name is unused there can be no conflict)
+ */
+ case ONTAPE:
+ ep = addentry(name, ino, type);
+ if (type == NODE)
+ newnode(ep);
+ ep->e_flags |= NEW|KEEP;
+ dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+ flagvalues(ep));
+ break;
+
+ /*
+ * A file with the same inode number, but a different
+ * name has been found. If the other name has not already
+ * been found (indicated by the KEEP flag, see above) then
+ * this must be a new name for the file, and it is renamed.
+ * If the other name has been found then this must be a
+ * link to the file. Hard links to directories are not
+ * permitted, and are either deleted or converted to
+ * symbolic links. Finally, if the file is on the tape,
+ * a request is made to extract it.
+ */
+ case ONTAPE|INOFND:
+ if (type == LEAF && (ip->e_flags & KEEP) == 0)
+ ip->e_flags |= EXTRACT;
+ /* FALLTHROUGH */
+ case INOFND:
+ if ((ip->e_flags & KEEP) == 0) {
+ renameit(myname(ip), name);
+ moveentry(ip, name);
+ ip->e_flags |= KEEP;
+ dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+ flagvalues(ip));
+ break;
+ }
+ if (ip->e_type == NODE) {
+ descend = FAIL;
+ fprintf(stderr,
+ "deleted hard link %s to directory %s\n",
+ name, myname(ip));
+ break;
+ }
+ ep = addentry(name, ino, type|LINK);
+ ep->e_flags |= NEW;
+ dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
+ flagvalues(ep));
+ break;
+
+ /*
+ * A previously known file which is to be updated. If it is a link,
+ * then all names referring to the previous file must be removed
+ * so that the subset of them that remain can be recreated.
+ */
+ case ONTAPE|INOFND|NAMEFND:
+ if (lookuptype == LINK) {
+ removeleaf(np);
+ freeentry(np);
+ ep = addentry(name, ino, type|LINK);
+ if (type == NODE)
+ newnode(ep);
+ ep->e_flags |= NEW|KEEP;
+ dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
+ flagvalues(ep));
+ break;
+ }
+ if (type == LEAF && lookuptype != LINK)
+ np->e_flags |= EXTRACT;
+ np->e_flags |= KEEP;
+ dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+ flagvalues(np));
+ break;
+
+ /*
+ * An inode is being reused in a completely different way.
+ * Normally an extract can simply do an "unlink" followed
+ * by a "creat". Here we must do effectively the same
+ * thing. The complications arise because we cannot really
+ * delete a directory since it may still contain files
+ * that we need to rename, so we delete it from the symbol
+ * table, and put it on the list to be deleted eventually.
+ * Conversely if a directory is to be created, it must be
+ * done immediately, rather than waiting until the
+ * extraction phase.
+ */
+ case ONTAPE|INOFND|MODECHG:
+ case ONTAPE|INOFND|NAMEFND|MODECHG:
+ if (ip->e_flags & KEEP) {
+ badentry(ip, "cannot KEEP and change modes");
+ break;
+ }
+ if (ip->e_type == LEAF) {
+ /* changing from leaf to node */
+ for (ip = lookupino(ino); ip != NULL; ip = ip->e_links) {
+ if (ip->e_type != LEAF)
+ badentry(ip, "NODE and LEAF links to same inode");
+ removeleaf(ip);
+ freeentry(ip);
+ }
+ ip = addentry(name, ino, type);
+ newnode(ip);
+ } else {
+ /* changing from node to leaf */
+ if ((ip->e_flags & TMPNAME) == 0)
+ mktempname(ip);
+ deleteino(ip->e_ino);
+ ip->e_next = removelist;
+ removelist = ip;
+ ip = addentry(name, ino, type);
+ }
+ ip->e_flags |= NEW|KEEP;
+ dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+ flagvalues(ip));
+ break;
+
+ /*
+ * A hard link to a directory that has been removed.
+ * Ignore it.
+ */
+ case NAMEFND:
+ dprintf(stdout, "[%s] %s: Extraneous name\n", keyval(key),
+ name);
+ descend = FAIL;
+ break;
+
+ /*
+ * If we find a directory entry for a file that is not on
+ * the tape, then we must have found a file that was created
+ * while the dump was in progress. Since we have no contents
+ * for it, we discard the name knowing that it will be on the
+ * next incremental tape.
+ */
+ case 0:
+ fprintf(stderr, "%s: (inode %d) not found on tape\n",
+ name, ino);
+ break;
+
+ /*
+ * If any of these arise, something is grievously wrong with
+ * the current state of the symbol table.
+ */
+ case INOFND|NAMEFND|MODECHG:
+ case NAMEFND|MODECHG:
+ case INOFND|MODECHG:
+ fprintf(stderr, "[%s] %s: inconsistent state\n", keyval(key),
+ name);
+ break;
+
+ /*
+ * These states "cannot" arise for any state of the symbol table.
+ */
+ case ONTAPE|MODECHG:
+ case MODECHG:
+ default:
+ panic("[%s] %s: impossible state\n", keyval(key), name);
+ break;
+ }
+ return (descend);
+}
+
+/*
+ * Calculate the active flags in a key.
+ */
+static char *
+keyval(int key)
+{
+ static char keybuf[32];
+
+ (void) strcpy(keybuf, "|NIL");
+ keybuf[0] = '\0';
+ if (key & ONTAPE)
+ (void) strcat(keybuf, "|ONTAPE");
+ if (key & INOFND)
+ (void) strcat(keybuf, "|INOFND");
+ if (key & NAMEFND)
+ (void) strcat(keybuf, "|NAMEFND");
+ if (key & MODECHG)
+ (void) strcat(keybuf, "|MODECHG");
+ return (&keybuf[1]);
+}
+
+/*
+ * Find unreferenced link names.
+ */
+void
+findunreflinks(void)
+{
+ struct entry *ep, *np;
+ ino_t i;
+
+ vprintf(stdout, "Find unreferenced names.\n");
+ for (i = ROOTINO; i < maxino; i++) {
+ ep = lookupino(i);
+ if (ep == NULL || ep->e_type == LEAF || TSTINO(i, dumpmap) == 0)
+ continue;
+ for (np = ep->e_entries; np != NULL; np = np->e_sibling) {
+ if (np->e_flags == 0) {
+ dprintf(stdout,
+ "%s: remove unreferenced name\n",
+ myname(np));
+ removeleaf(np);
+ freeentry(np);
+ }
+ }
+ }
+ /*
+ * Any leaves remaining in removed directories is unreferenced.
+ */
+ for (ep = removelist; ep != NULL; ep = ep->e_next) {
+ for (np = ep->e_entries; np != NULL; np = np->e_sibling) {
+ if (np->e_type == LEAF) {
+ if (np->e_flags != 0)
+ badentry(np, "unreferenced with flags");
+ dprintf(stdout,
+ "%s: remove unreferenced name\n",
+ myname(np));
+ removeleaf(np);
+ freeentry(np);
+ }
+ }
+ }
+}
+
+/*
+ * Remove old nodes (directories).
+ * Note that this routine runs in O(N*D) where:
+ * N is the number of directory entries to be removed.
+ * D is the maximum depth of the tree.
+ * If N == D this can be quite slow. If the list were
+ * topologically sorted, the deletion could be done in
+ * time O(N).
+ */
+void
+removeoldnodes(void)
+{
+ struct entry *ep, **prev;
+ long change;
+
+ vprintf(stdout, "Remove old nodes (directories).\n");
+ do {
+ change = 0;
+ prev = &removelist;
+ for (ep = removelist; ep != NULL; ep = *prev) {
+ if (ep->e_entries != NULL) {
+ prev = &ep->e_next;
+ continue;
+ }
+ *prev = ep->e_next;
+ removenode(ep);
+ freeentry(ep);
+ change++;
+ }
+ } while (change);
+ for (ep = removelist; ep != NULL; ep = ep->e_next)
+ badentry(ep, "cannot remove, non-empty");
+}
+
+/*
+ * This is the routine used to extract files for the 'r' command.
+ * Extract new leaves.
+ */
+void
+createleaves(char *symtabfile)
+{
+ struct entry *ep;
+ ino_t first;
+ long curvol;
+
+ if (command == 'R') {
+ vprintf(stdout, "Continue extraction of new leaves\n");
+ } else {
+ vprintf(stdout, "Extract new leaves.\n");
+ dumpsymtable(symtabfile, volno);
+ }
+ first = lowerbnd(ROOTINO);
+ curvol = volno;
+ while (curfile.ino < maxino) {
+ first = lowerbnd(first);
+ /*
+ * If the next available file is not the one which we
+ * expect then we have missed one or more files. Since
+ * we do not request files that were not on the tape,
+ * the lost files must have been due to a tape read error,
+ * or a file that was removed while the dump was in progress.
+ */
+ while (first < curfile.ino) {
+ ep = lookupino(first);
+ if (ep == NULL)
+ panic("%d: bad first\n", first);
+ fprintf(stderr, "%s: not found on tape\n", myname(ep));
+ ep->e_flags &= ~(NEW|EXTRACT);
+ first = lowerbnd(first);
+ }
+ /*
+ * If we find files on the tape that have no corresponding
+ * directory entries, then we must have found a file that
+ * was created while the dump was in progress. Since we have
+ * no name for it, we discard it knowing that it will be
+ * on the next incremental tape.
+ */
+ if (first != curfile.ino) {
+ fprintf(stderr, "expected next file %d, got %d\n",
+ first, curfile.ino);
+ skipfile();
+ goto next;
+ }
+ ep = lookupino(curfile.ino);
+ if (ep == NULL)
+ panic("unknown file on tape\n");
+ if ((ep->e_flags & (NEW|EXTRACT)) == 0)
+ badentry(ep, "unexpected file on tape");
+ /*
+ * If the file is to be extracted, then the old file must
+ * be removed since its type may change from one leaf type
+ * to another (e.g. "file" to "character special").
+ */
+ if ((ep->e_flags & EXTRACT) != 0) {
+ removeleaf(ep);
+ ep->e_flags &= ~REMOVED;
+ }
+ (void) extractfile(myname(ep));
+ ep->e_flags &= ~(NEW|EXTRACT);
+ /*
+ * We checkpoint the restore after every tape reel, so
+ * as to simplify the amount of work required by the
+ * 'R' command.
+ */
+ next:
+ if (curvol != volno) {
+ dumpsymtable(symtabfile, volno);
+ skipmaps();
+ curvol = volno;
+ }
+ }
+}
+
+/*
+ * This is the routine used to extract files for the 'x' and 'i' commands.
+ * Efficiently extract a subset of the files on a tape.
+ */
+void
+createfiles(void)
+{
+ ino_t first, next, last;
+ struct entry *ep;
+ long curvol;
+
+ vprintf(stdout, "Extract requested files\n");
+ curfile.action = SKIP;
+ getvol((long)1);
+ skipmaps();
+ skipdirs();
+ first = lowerbnd(ROOTINO);
+ last = upperbnd(maxino - 1);
+ for (;;) {
+ curvol = volno;
+ first = lowerbnd(first);
+ last = upperbnd(last);
+ /*
+ * Check to see if any files remain to be extracted
+ */
+ if (first > last)
+ return;
+ /*
+ * Reject any volumes with inodes greater than the last
+ * one needed, so that we can quickly skip backwards to
+ * a volume containing useful inodes. We can't do this
+ * if there are no further volumes available (curfile.ino
+ * >= maxino) or if we are already at the first tape.
+ */
+ if (curfile.ino > last && curfile.ino < maxino && volno > 1) {
+ curfile.action = SKIP;
+ getvol((long)0);
+ skipmaps();
+ skipdirs();
+ continue;
+ }
+ /*
+ * Decide on the next inode needed.
+ * Skip across the inodes until it is found
+ * or a volume change is encountered
+ */
+ if (curfile.ino < maxino) {
+ next = lowerbnd(curfile.ino);
+ while (next > curfile.ino && volno == curvol)
+ skipfile();
+ if (volno != curvol) {
+ skipmaps();
+ skipdirs();
+ continue;
+ }
+ } else {
+ /*
+ * No further volumes or inodes available. Set
+ * `next' to the first inode, so that a warning
+ * is emitted below for each missing file.
+ */
+ next = first;
+ }
+ /*
+ * If the current inode is greater than the one we were
+ * looking for then we missed the one we were looking for.
+ * Since we only attempt to extract files listed in the
+ * dump map, the lost files must have been due to a tape
+ * read error, or a file that was removed while the dump
+ * was in progress. Thus we report all requested files
+ * between the one we were looking for, and the one we
+ * found as missing, and delete their request flags.
+ */
+ while (next < curfile.ino) {
+ ep = lookupino(next);
+ if (ep == NULL)
+ panic("corrupted symbol table\n");
+ fprintf(stderr, "%s: not found on tape\n", myname(ep));
+ ep->e_flags &= ~NEW;
+ next = lowerbnd(next);
+ }
+ /*
+ * The current inode is the one that we are looking for,
+ * so extract it per its requested name.
+ */
+ if (next == curfile.ino && next <= last) {
+ ep = lookupino(next);
+ if (ep == NULL)
+ panic("corrupted symbol table\n");
+ (void) extractfile(myname(ep));
+ ep->e_flags &= ~NEW;
+ if (volno != curvol)
+ skipmaps();
+ }
+ }
+}
+
+/*
+ * Add links.
+ */
+void
+createlinks(void)
+{
+ struct entry *np, *ep;
+ ino_t i;
+ char name[BUFSIZ];
+
+ if ((ep = lookupino(WINO))) {
+ vprintf(stdout, "Add whiteouts\n");
+ for ( ; ep != NULL; ep = ep->e_links) {
+ if ((ep->e_flags & NEW) == 0)
+ continue;
+ (void) addwhiteout(myname(ep));
+ ep->e_flags &= ~NEW;
+ }
+ }
+ vprintf(stdout, "Add links\n");
+ for (i = ROOTINO; i < maxino; i++) {
+ ep = lookupino(i);
+ if (ep == NULL)
+ continue;
+ for (np = ep->e_links; np != NULL; np = np->e_links) {
+ if ((np->e_flags & NEW) == 0)
+ continue;
+ (void) strcpy(name, myname(ep));
+ if (ep->e_type == NODE) {
+ (void) linkit(name, myname(np), SYMLINK);
+ } else {
+ (void) linkit(name, myname(np), HARDLINK);
+ }
+ np->e_flags &= ~NEW;
+ }
+ }
+}
+
+/*
+ * Check the symbol table.
+ * We do this to insure that all the requested work was done, and
+ * that no temporary names remain.
+ */
+void
+checkrestore(void)
+{
+ struct entry *ep;
+ ino_t i;
+
+ vprintf(stdout, "Check the symbol table.\n");
+ for (i = WINO; i < maxino; i++) {
+ for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
+ ep->e_flags &= ~KEEP;
+ if (ep->e_type == NODE)
+ ep->e_flags &= ~(NEW|EXISTED);
+ if (ep->e_flags != 0)
+ badentry(ep, "incomplete operations");
+ }
+ }
+}
+
+/*
+ * Compare with the directory structure on the tape
+ * A paranoid check that things are as they should be.
+ */
+long
+verifyfile(char *name, ino_t ino, int type)
+{
+ struct entry *np, *ep;
+ long descend = GOOD;
+
+ ep = lookupname(name);
+ if (ep == NULL) {
+ fprintf(stderr, "Warning: missing name %s\n", name);
+ return (FAIL);
+ }
+ np = lookupino(ino);
+ if (np != ep)
+ descend = FAIL;
+ for ( ; np != NULL; np = np->e_links)
+ if (np == ep)
+ break;
+ if (np == NULL)
+ panic("missing inumber %d\n", ino);
+ if (ep->e_type == LEAF && type != LEAF)
+ badentry(ep, "type should be LEAF");
+ return (descend);
+}
diff --git a/sbin/restore/restore.h b/sbin/restore/restore.h
new file mode 100644
index 0000000..04048dc
--- /dev/null
+++ b/sbin/restore/restore.h
@@ -0,0 +1,151 @@
+/*-
+ * Copyright (c) 1983, 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.
+ *
+ * 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.
+ * 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 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.
+ *
+ * @(#)restore.h 8.3 (Berkeley) 9/13/94
+ * $FreeBSD$
+ */
+
+/*
+ * Flags
+ */
+extern int bflag; /* set input block size */
+extern int dflag; /* print out debugging info */
+extern int hflag; /* restore heirarchies */
+extern int mflag; /* restore by name instead of inode number */
+extern int Nflag; /* do not write the disk */
+extern int uflag; /* unlink symlink targets */
+extern int vflag; /* print out actions taken */
+extern int yflag; /* always try to recover from tape errors */
+/*
+ * Global variables
+ */
+extern char *dumpmap; /* map of inodes on this dump tape */
+extern char *usedinomap; /* map of inodes that are in use on this fs */
+extern ino_t maxino; /* highest numbered inode in this file system */
+extern long dumpnum; /* location of the dump on this tape */
+extern long volno; /* current volume being read */
+extern long ntrec; /* number of TP_BSIZE records per tape block */
+extern time_t dumptime; /* time that this dump begins */
+extern time_t dumpdate; /* time that this dump was made */
+extern char command; /* opration being performed */
+extern FILE *terminal; /* file descriptor for the terminal input */
+extern int Bcvt; /* need byte swapping on inodes and dirs */
+extern int oldinofmt; /* reading tape with FreeBSD 1 format inodes */
+
+/*
+ * Each file in the file system is described by one of these entries
+ */
+struct entry {
+ char *e_name; /* the current name of this entry */
+ u_char e_namlen; /* length of this name */
+ char e_type; /* type of this entry, see below */
+ short e_flags; /* status flags, see below */
+ ino_t e_ino; /* inode number in previous file sys */
+ long e_index; /* unique index (for dumpped table) */
+ struct entry *e_parent; /* pointer to parent directory (..) */
+ struct entry *e_sibling; /* next element in this directory (.) */
+ struct entry *e_links; /* hard links to this inode */
+ struct entry *e_entries; /* for directories, their entries */
+ struct entry *e_next; /* hash chain list */
+};
+/* types */
+#define LEAF 1 /* non-directory entry */
+#define NODE 2 /* directory entry */
+#define LINK 4 /* synthesized type, stripped by addentry */
+/* flags */
+#define EXTRACT 0x0001 /* entry is to be replaced from the tape */
+#define NEW 0x0002 /* a new entry to be extracted */
+#define KEEP 0x0004 /* entry is not to change */
+#define REMOVED 0x0010 /* entry has been removed */
+#define TMPNAME 0x0020 /* entry has been given a temporary name */
+#define EXISTED 0x0040 /* directory already existed during extract */
+
+/*
+ * Constants associated with entry structs
+ */
+#define HARDLINK 1
+#define SYMLINK 2
+#define TMPHDR "RSTTMP"
+
+/*
+ * The entry describes the next file available on the tape
+ */
+struct context {
+ short action; /* action being taken on this file */
+ mode_t mode; /* mode of file */
+ ino_t ino; /* inumber of file */
+ uid_t uid; /* file owner */
+ gid_t gid; /* file group */
+ int file_flags; /* status flags (chflags) */
+ int rdev; /* device number of file */
+ time_t atime_sec; /* access time seconds */
+ time_t mtime_sec; /* modified time seconds */
+ time_t birthtime_sec; /* creation time seconds */
+ int atime_nsec; /* access time nanoseconds */
+ int mtime_nsec; /* modified time nanoseconds */
+ int birthtime_nsec; /* creation time nanoseconds */
+ off_t size; /* size of file */
+ char *name; /* name of file */
+} curfile;
+/* actions */
+#define USING 1 /* extracting from the tape */
+#define SKIP 2 /* skipping */
+#define UNKNOWN 3 /* disposition or starting point is unknown */
+
+/*
+ * Definitions for library routines operating on directories.
+ */
+typedef struct rstdirdesc RST_DIR;
+
+/*
+ * Flags to setdirmodes.
+ */
+#define FORCE 0x0001
+
+/*
+ * Useful macros
+ */
+#define TSTINO(ino, map) \
+ (map[(u_int)((ino) - 1) / CHAR_BIT] & \
+ (1 << ((u_int)((ino) - 1) % CHAR_BIT)))
+#define SETINO(ino, map) \
+ map[(u_int)((ino) - 1) / CHAR_BIT] |= \
+ 1 << ((u_int)((ino) - 1) % CHAR_BIT)
+
+#define dprintf if (dflag) fprintf
+#define vprintf if (vflag) fprintf
+
+#define GOOD 1
+#define FAIL 0
+
+#define NFS_DR_NEWINODEFMT 0x2 /* Tape uses 4.4 BSD inode format */
diff --git a/sbin/restore/symtab.c b/sbin/restore/symtab.c
new file mode 100644
index 0000000..b7eff26
--- /dev/null
+++ b/sbin/restore/symtab.c
@@ -0,0 +1,614 @@
+/*
+ * 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.
+ * 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 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 lint
+#if 0
+static char sccsid[] = "@(#)symtab.c 8.3 (Berkeley) 4/28/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * These routines maintain the symbol table which tracks the state
+ * of the file system being restored. They provide lookup by either
+ * name or inode number. They also provide for creation, deletion,
+ * and renaming of entries. Because of the dynamic nature of pathnames,
+ * names should not be saved, but always constructed just before they
+ * are needed, by calling "myname".
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/dinode.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "restore.h"
+#include "extern.h"
+
+/*
+ * The following variables define the inode symbol table.
+ * The primary hash table is dynamically allocated based on
+ * the number of inodes in the file system (maxino), scaled by
+ * HASHFACTOR. The variable "entry" points to the hash table;
+ * the variable "entrytblsize" indicates its size (in entries).
+ */
+#define HASHFACTOR 5
+static struct entry **entry;
+static long entrytblsize;
+
+static void addino(ino_t, struct entry *);
+static struct entry *lookupparent(char *);
+static void removeentry(struct entry *);
+
+/*
+ * Look up an entry by inode number
+ */
+struct entry *
+lookupino(ino_t inum)
+{
+ struct entry *ep;
+
+ if (inum < WINO || inum >= maxino)
+ return (NULL);
+ for (ep = entry[inum % entrytblsize]; ep != NULL; ep = ep->e_next)
+ if (ep->e_ino == inum)
+ return (ep);
+ return (NULL);
+}
+
+/*
+ * Add an entry into the entry table
+ */
+static void
+addino(ino_t inum, struct entry *np)
+{
+ struct entry **epp;
+
+ if (inum < WINO || inum >= maxino)
+ panic("addino: out of range %d\n", inum);
+ epp = &entry[inum % entrytblsize];
+ np->e_ino = inum;
+ np->e_next = *epp;
+ *epp = np;
+ if (dflag)
+ for (np = np->e_next; np != NULL; np = np->e_next)
+ if (np->e_ino == inum)
+ badentry(np, "duplicate inum");
+}
+
+/*
+ * Delete an entry from the entry table
+ */
+void
+deleteino(ino_t inum)
+{
+ struct entry *next;
+ struct entry **prev;
+
+ if (inum < WINO || inum >= maxino)
+ panic("deleteino: out of range %d\n", inum);
+ prev = &entry[inum % entrytblsize];
+ for (next = *prev; next != NULL; next = next->e_next) {
+ if (next->e_ino == inum) {
+ next->e_ino = 0;
+ *prev = next->e_next;
+ return;
+ }
+ prev = &next->e_next;
+ }
+ panic("deleteino: %d not found\n", inum);
+}
+
+/*
+ * Look up an entry by name
+ */
+struct entry *
+lookupname(char *name)
+{
+ struct entry *ep;
+ char *np, *cp;
+ char buf[MAXPATHLEN];
+
+ cp = name;
+ for (ep = lookupino(ROOTINO); ep != NULL; ep = ep->e_entries) {
+ for (np = buf; *cp != '/' && *cp != '\0' &&
+ np < &buf[sizeof(buf)]; )
+ *np++ = *cp++;
+ if (np == &buf[sizeof(buf)])
+ break;
+ *np = '\0';
+ for ( ; ep != NULL; ep = ep->e_sibling)
+ if (strcmp(ep->e_name, buf) == 0)
+ break;
+ if (ep == NULL)
+ break;
+ if (*cp++ == '\0')
+ return (ep);
+ }
+ return (NULL);
+}
+
+/*
+ * Look up the parent of a pathname
+ */
+static struct entry *
+lookupparent(char *name)
+{
+ struct entry *ep;
+ char *tailindex;
+
+ tailindex = strrchr(name, '/');
+ if (tailindex == NULL)
+ return (NULL);
+ *tailindex = '\0';
+ ep = lookupname(name);
+ *tailindex = '/';
+ if (ep == NULL)
+ return (NULL);
+ if (ep->e_type != NODE)
+ panic("%s is not a directory\n", name);
+ return (ep);
+}
+
+/*
+ * Determine the current pathname of a node or leaf
+ */
+char *
+myname(struct entry *ep)
+{
+ char *cp;
+ static char namebuf[MAXPATHLEN];
+
+ for (cp = &namebuf[MAXPATHLEN - 2]; cp > &namebuf[ep->e_namlen]; ) {
+ cp -= ep->e_namlen;
+ memmove(cp, ep->e_name, (long)ep->e_namlen);
+ if (ep == lookupino(ROOTINO))
+ return (cp);
+ *(--cp) = '/';
+ ep = ep->e_parent;
+ }
+ panic("%s: pathname too long\n", cp);
+ return(cp);
+}
+
+/*
+ * Unused symbol table entries are linked together on a free list
+ * headed by the following pointer.
+ */
+static struct entry *freelist = NULL;
+
+/*
+ * add an entry to the symbol table
+ */
+struct entry *
+addentry(char *name, ino_t inum, int type)
+{
+ struct entry *np, *ep;
+
+ if (freelist != NULL) {
+ np = freelist;
+ freelist = np->e_next;
+ memset(np, 0, (long)sizeof(struct entry));
+ } else {
+ np = (struct entry *)calloc(1, sizeof(struct entry));
+ if (np == NULL)
+ panic("no memory to extend symbol table\n");
+ }
+ np->e_type = type & ~LINK;
+ ep = lookupparent(name);
+ if (ep == NULL) {
+ if (inum != ROOTINO || lookupino(ROOTINO) != NULL)
+ panic("bad name to addentry %s\n", name);
+ np->e_name = savename(name);
+ np->e_namlen = strlen(name);
+ np->e_parent = np;
+ addino(ROOTINO, np);
+ return (np);
+ }
+ np->e_name = savename(strrchr(name, '/') + 1);
+ np->e_namlen = strlen(np->e_name);
+ np->e_parent = ep;
+ np->e_sibling = ep->e_entries;
+ ep->e_entries = np;
+ if (type & LINK) {
+ ep = lookupino(inum);
+ if (ep == NULL)
+ panic("link to non-existent name\n");
+ np->e_ino = inum;
+ np->e_links = ep->e_links;
+ ep->e_links = np;
+ } else if (inum != 0) {
+ if (lookupino(inum) != NULL)
+ panic("duplicate entry\n");
+ addino(inum, np);
+ }
+ return (np);
+}
+
+/*
+ * delete an entry from the symbol table
+ */
+void
+freeentry(struct entry *ep)
+{
+ struct entry *np;
+ ino_t inum;
+
+ if (ep->e_flags != REMOVED)
+ badentry(ep, "not marked REMOVED");
+ if (ep->e_type == NODE) {
+ if (ep->e_links != NULL)
+ badentry(ep, "freeing referenced directory");
+ if (ep->e_entries != NULL)
+ badentry(ep, "freeing non-empty directory");
+ }
+ if (ep->e_ino != 0) {
+ np = lookupino(ep->e_ino);
+ if (np == NULL)
+ badentry(ep, "lookupino failed");
+ if (np == ep) {
+ inum = ep->e_ino;
+ deleteino(inum);
+ if (ep->e_links != NULL)
+ addino(inum, ep->e_links);
+ } else {
+ for (; np != NULL; np = np->e_links) {
+ if (np->e_links == ep) {
+ np->e_links = ep->e_links;
+ break;
+ }
+ }
+ if (np == NULL)
+ badentry(ep, "link not found");
+ }
+ }
+ removeentry(ep);
+ freename(ep->e_name);
+ ep->e_next = freelist;
+ freelist = ep;
+}
+
+/*
+ * Relocate an entry in the tree structure
+ */
+void
+moveentry(struct entry *ep, char *newname)
+{
+ struct entry *np;
+ char *cp;
+
+ np = lookupparent(newname);
+ if (np == NULL)
+ badentry(ep, "cannot move ROOT");
+ if (np != ep->e_parent) {
+ removeentry(ep);
+ ep->e_parent = np;
+ ep->e_sibling = np->e_entries;
+ np->e_entries = ep;
+ }
+ cp = strrchr(newname, '/') + 1;
+ freename(ep->e_name);
+ ep->e_name = savename(cp);
+ ep->e_namlen = strlen(cp);
+ if (strcmp(gentempname(ep), ep->e_name) == 0)
+ ep->e_flags |= TMPNAME;
+ else
+ ep->e_flags &= ~TMPNAME;
+}
+
+/*
+ * Remove an entry in the tree structure
+ */
+static void
+removeentry(struct entry *ep)
+{
+ struct entry *np;
+
+ np = ep->e_parent;
+ if (np->e_entries == ep) {
+ np->e_entries = ep->e_sibling;
+ } else {
+ for (np = np->e_entries; np != NULL; np = np->e_sibling) {
+ if (np->e_sibling == ep) {
+ np->e_sibling = ep->e_sibling;
+ break;
+ }
+ }
+ if (np == NULL)
+ badentry(ep, "cannot find entry in parent list");
+ }
+}
+
+/*
+ * Table of unused string entries, sorted by length.
+ *
+ * Entries are allocated in STRTBLINCR sized pieces so that names
+ * of similar lengths can use the same entry. The value of STRTBLINCR
+ * is chosen so that every entry has at least enough space to hold
+ * a "struct strtbl" header. Thus every entry can be linked onto an
+ * appropriate free list.
+ *
+ * NB. The macro "allocsize" below assumes that "struct strhdr"
+ * has a size that is a power of two.
+ */
+struct strhdr {
+ struct strhdr *next;
+};
+
+#define STRTBLINCR (sizeof(struct strhdr))
+#define allocsize(size) (((size) + 1 + STRTBLINCR - 1) & ~(STRTBLINCR - 1))
+
+static struct strhdr strtblhdr[allocsize(NAME_MAX) / STRTBLINCR];
+
+/*
+ * Allocate space for a name. It first looks to see if it already
+ * has an appropriate sized entry, and if not allocates a new one.
+ */
+char *
+savename(char *name)
+{
+ struct strhdr *np;
+ long len;
+ char *cp;
+
+ if (name == NULL)
+ panic("bad name\n");
+ len = strlen(name);
+ np = strtblhdr[len / STRTBLINCR].next;
+ if (np != NULL) {
+ strtblhdr[len / STRTBLINCR].next = np->next;
+ cp = (char *)np;
+ } else {
+ cp = malloc((unsigned)allocsize(len));
+ if (cp == NULL)
+ panic("no space for string table\n");
+ }
+ (void) strcpy(cp, name);
+ return (cp);
+}
+
+/*
+ * Free space for a name. The resulting entry is linked onto the
+ * appropriate free list.
+ */
+void
+freename(char *name)
+{
+ struct strhdr *tp, *np;
+
+ tp = &strtblhdr[strlen(name) / STRTBLINCR];
+ np = (struct strhdr *)name;
+ np->next = tp->next;
+ tp->next = np;
+}
+
+/*
+ * Useful quantities placed at the end of a dumped symbol table.
+ */
+struct symtableheader {
+ int32_t volno;
+ int32_t stringsize;
+ int32_t entrytblsize;
+ time_t dumptime;
+ time_t dumpdate;
+ ino_t maxino;
+ int32_t ntrec;
+};
+
+/*
+ * dump a snapshot of the symbol table
+ */
+void
+dumpsymtable(char *filename, long checkpt)
+{
+ struct entry *ep, *tep;
+ ino_t i;
+ struct entry temp, *tentry;
+ long mynum = 1, stroff = 0;
+ FILE *fd;
+ struct symtableheader hdr;
+
+ vprintf(stdout, "Check pointing the restore\n");
+ if (Nflag)
+ return;
+ if ((fd = fopen(filename, "w")) == NULL) {
+ fprintf(stderr, "fopen: %s\n", strerror(errno));
+ panic("cannot create save file %s for symbol table\n",
+ filename);
+ done(1);
+ }
+ clearerr(fd);
+ /*
+ * Assign indices to each entry
+ * Write out the string entries
+ */
+ for (i = WINO; i <= maxino; i++) {
+ for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
+ ep->e_index = mynum++;
+ (void) fwrite(ep->e_name, sizeof(char),
+ (int)allocsize(ep->e_namlen), fd);
+ }
+ }
+ /*
+ * Convert pointers to indexes, and output
+ */
+ tep = &temp;
+ stroff = 0;
+ for (i = WINO; i <= maxino; i++) {
+ for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
+ memmove(tep, ep, (long)sizeof(struct entry));
+ tep->e_name = (char *)stroff;
+ stroff += allocsize(ep->e_namlen);
+ tep->e_parent = (struct entry *)ep->e_parent->e_index;
+ if (ep->e_links != NULL)
+ tep->e_links =
+ (struct entry *)ep->e_links->e_index;
+ if (ep->e_sibling != NULL)
+ tep->e_sibling =
+ (struct entry *)ep->e_sibling->e_index;
+ if (ep->e_entries != NULL)
+ tep->e_entries =
+ (struct entry *)ep->e_entries->e_index;
+ if (ep->e_next != NULL)
+ tep->e_next =
+ (struct entry *)ep->e_next->e_index;
+ (void) fwrite((char *)tep, sizeof(struct entry), 1, fd);
+ }
+ }
+ /*
+ * Convert entry pointers to indexes, and output
+ */
+ for (i = 0; i < entrytblsize; i++) {
+ if (entry[i] == NULL)
+ tentry = NULL;
+ else
+ tentry = (struct entry *)entry[i]->e_index;
+ (void) fwrite((char *)&tentry, sizeof(struct entry *), 1, fd);
+ }
+ hdr.volno = checkpt;
+ hdr.maxino = maxino;
+ hdr.entrytblsize = entrytblsize;
+ hdr.stringsize = stroff;
+ hdr.dumptime = dumptime;
+ hdr.dumpdate = dumpdate;
+ hdr.ntrec = ntrec;
+ (void) fwrite((char *)&hdr, sizeof(struct symtableheader), 1, fd);
+ if (ferror(fd)) {
+ fprintf(stderr, "fwrite: %s\n", strerror(errno));
+ panic("output error to file %s writing symbol table\n",
+ filename);
+ }
+ (void) fclose(fd);
+}
+
+/*
+ * Initialize a symbol table from a file
+ */
+void
+initsymtable(char *filename)
+{
+ char *base;
+ long tblsize;
+ struct entry *ep;
+ struct entry *baseep, *lep;
+ struct symtableheader hdr;
+ struct stat stbuf;
+ long i;
+ int fd;
+
+ vprintf(stdout, "Initialize symbol table.\n");
+ if (filename == NULL) {
+ entrytblsize = maxino / HASHFACTOR;
+ entry = (struct entry **)
+ calloc((unsigned)entrytblsize, sizeof(struct entry *));
+ if (entry == (struct entry **)NULL)
+ panic("no memory for entry table\n");
+ ep = addentry(".", ROOTINO, NODE);
+ ep->e_flags |= NEW;
+ return;
+ }
+ if ((fd = open(filename, O_RDONLY, 0)) < 0) {
+ fprintf(stderr, "open: %s\n", strerror(errno));
+ panic("cannot open symbol table file %s\n", filename);
+ }
+ if (fstat(fd, &stbuf) < 0) {
+ fprintf(stderr, "stat: %s\n", strerror(errno));
+ panic("cannot stat symbol table file %s\n", filename);
+ }
+ tblsize = stbuf.st_size - sizeof(struct symtableheader);
+ base = calloc(sizeof(char), (unsigned)tblsize);
+ if (base == NULL)
+ panic("cannot allocate space for symbol table\n");
+ if (read(fd, base, (int)tblsize) < 0 ||
+ read(fd, (char *)&hdr, sizeof(struct symtableheader)) < 0) {
+ fprintf(stderr, "read: %s\n", strerror(errno));
+ panic("cannot read symbol table file %s\n", filename);
+ }
+ switch (command) {
+ case 'r':
+ /*
+ * For normal continuation, insure that we are using
+ * the next incremental tape
+ */
+ if (hdr.dumpdate != dumptime) {
+ if (hdr.dumpdate < dumptime)
+ fprintf(stderr, "Incremental tape too low\n");
+ else
+ fprintf(stderr, "Incremental tape too high\n");
+ done(1);
+ }
+ break;
+ case 'R':
+ /*
+ * For restart, insure that we are using the same tape
+ */
+ curfile.action = SKIP;
+ dumptime = hdr.dumptime;
+ dumpdate = hdr.dumpdate;
+ if (!bflag)
+ newtapebuf(hdr.ntrec);
+ getvol(hdr.volno);
+ break;
+ default:
+ panic("initsymtable called from command %c\n", command);
+ break;
+ }
+ maxino = hdr.maxino;
+ entrytblsize = hdr.entrytblsize;
+ entry = (struct entry **)
+ (base + tblsize - (entrytblsize * sizeof(struct entry *)));
+ baseep = (struct entry *)(base + hdr.stringsize - sizeof(struct entry));
+ lep = (struct entry *)entry;
+ for (i = 0; i < entrytblsize; i++) {
+ if (entry[i] == NULL)
+ continue;
+ entry[i] = &baseep[(long)entry[i]];
+ }
+ for (ep = &baseep[1]; ep < lep; ep++) {
+ ep->e_name = base + (long)ep->e_name;
+ ep->e_parent = &baseep[(long)ep->e_parent];
+ if (ep->e_sibling != NULL)
+ ep->e_sibling = &baseep[(long)ep->e_sibling];
+ if (ep->e_links != NULL)
+ ep->e_links = &baseep[(long)ep->e_links];
+ if (ep->e_entries != NULL)
+ ep->e_entries = &baseep[(long)ep->e_entries];
+ if (ep->e_next != NULL)
+ ep->e_next = &baseep[(long)ep->e_next];
+ }
+}
diff --git a/sbin/restore/tape.c b/sbin/restore/tape.c
new file mode 100644
index 0000000..3245926
--- /dev/null
+++ b/sbin/restore/tape.c
@@ -0,0 +1,1407 @@
+/*
+ * Copyright (c) 1983, 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.
+ *
+ * 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.
+ * 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 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 lint
+#if 0
+static char sccsid[] = "@(#)tape.c 8.9 (Berkeley) 5/1/95";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/mtio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <ufs/ufs/dinode.h>
+#include <protocols/dumprestore.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <paths.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <timeconv.h>
+#include <unistd.h>
+
+#include "restore.h"
+#include "extern.h"
+
+static long fssize = MAXBSIZE;
+static int mt = -1;
+static int pipein = 0;
+static int pipecmdin = 0;
+static FILE *popenfp = NULL;
+static char *magtape;
+static int blkcnt;
+static int numtrec;
+static char *tapebuf;
+static union u_spcl endoftapemark;
+static long blksread; /* blocks read since last header */
+static int64_t tapeaddr = 0; /* current TP_BSIZE tape record */
+static long tapesread;
+static jmp_buf restart;
+static int gettingfile = 0; /* restart has a valid frame */
+static char *host = NULL;
+static int readmapflag;
+
+static int ofile;
+static char *map;
+static char lnkbuf[MAXPATHLEN + 1];
+static int pathlen;
+
+int Bcvt; /* Swap Bytes */
+int oldinofmt; /* FreeBSD 1 inode format needs cvt */
+
+#define FLUSHTAPEBUF() blkcnt = ntrec + 1
+
+static void accthdr(struct s_spcl *);
+static int checksum(int *);
+static void findinode(struct s_spcl *);
+static void findtapeblksize(void);
+static int gethead(struct s_spcl *);
+static void readtape(char *);
+static void setdumpnum(void);
+static u_long swabl(u_long);
+static u_char *swablong(u_char *, int);
+static u_char *swabshort(u_char *, int);
+static void terminateinput(void);
+static void xtrfile(char *, long);
+static void xtrlnkfile(char *, long);
+static void xtrlnkskip(char *, long);
+static void xtrmap(char *, long);
+static void xtrmapskip(char *, long);
+static void xtrskip(char *, long);
+
+/*
+ * Set up an input source
+ */
+void
+setinput(char *source, int ispipecommand)
+{
+ FLUSHTAPEBUF();
+ if (bflag)
+ newtapebuf(ntrec);
+ else
+ newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC);
+ terminal = stdin;
+
+ if (ispipecommand)
+ pipecmdin++;
+ else
+#ifdef RRESTORE
+ if (strchr(source, ':')) {
+ host = source;
+ source = strchr(host, ':');
+ *source++ = '\0';
+ if (rmthost(host) == 0)
+ done(1);
+ } else
+#endif
+ if (strcmp(source, "-") == 0) {
+ /*
+ * Since input is coming from a pipe we must establish
+ * our own connection to the terminal.
+ */
+ terminal = fopen(_PATH_TTY, "r");
+ if (terminal == NULL) {
+ (void)fprintf(stderr, "cannot open %s: %s\n",
+ _PATH_TTY, strerror(errno));
+ terminal = fopen(_PATH_DEVNULL, "r");
+ if (terminal == NULL) {
+ (void)fprintf(stderr, "cannot open %s: %s\n",
+ _PATH_DEVNULL, strerror(errno));
+ done(1);
+ }
+ }
+ pipein++;
+ }
+ setuid(getuid()); /* no longer need or want root privileges */
+ magtape = strdup(source);
+ if (magtape == NULL) {
+ fprintf(stderr, "Cannot allocate space for magtape buffer\n");
+ done(1);
+ }
+}
+
+void
+newtapebuf(long size)
+{
+ static int tapebufsize = -1;
+
+ ntrec = size;
+ if (size <= tapebufsize)
+ return;
+ if (tapebuf != NULL)
+ free(tapebuf);
+ tapebuf = malloc(size * TP_BSIZE);
+ if (tapebuf == NULL) {
+ fprintf(stderr, "Cannot allocate space for tape buffer\n");
+ done(1);
+ }
+ tapebufsize = size;
+}
+
+/*
+ * Verify that the tape drive can be accessed and
+ * that it actually is a dump tape.
+ */
+void
+setup(void)
+{
+ int i, j, *ip;
+ struct stat stbuf;
+
+ vprintf(stdout, "Verify tape and initialize maps\n");
+ if (pipecmdin) {
+ if (setenv("RESTORE_VOLUME", "1", 1) == -1) {
+ fprintf(stderr, "Cannot set $RESTORE_VOLUME: %s\n",
+ strerror(errno));
+ done(1);
+ }
+ popenfp = popen(magtape, "r");
+ mt = popenfp ? fileno(popenfp) : -1;
+ } else
+#ifdef RRESTORE
+ if (host)
+ mt = rmtopen(magtape, 0);
+ else
+#endif
+ if (pipein)
+ mt = 0;
+ else
+ mt = open(magtape, O_RDONLY, 0);
+ if (mt < 0) {
+ fprintf(stderr, "%s: %s\n", magtape, strerror(errno));
+ done(1);
+ }
+ volno = 1;
+ setdumpnum();
+ FLUSHTAPEBUF();
+ if (!pipein && !bflag)
+ findtapeblksize();
+ if (gethead(&spcl) == FAIL) {
+ fprintf(stderr, "Tape is not a dump tape\n");
+ done(1);
+ }
+ if (pipein) {
+ endoftapemark.s_spcl.c_magic = FS_UFS2_MAGIC;
+ endoftapemark.s_spcl.c_type = TS_END;
+ ip = (int *)&endoftapemark;
+ j = sizeof(union u_spcl) / sizeof(int);
+ i = 0;
+ do
+ i += *ip++;
+ while (--j);
+ endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
+ }
+ if (vflag || command == 't')
+ printdumpinfo();
+ dumptime = _time64_to_time(spcl.c_ddate);
+ dumpdate = _time64_to_time(spcl.c_date);
+ if (stat(".", &stbuf) < 0) {
+ fprintf(stderr, "cannot stat .: %s\n", strerror(errno));
+ done(1);
+ }
+ if (stbuf.st_blksize > 0 && stbuf.st_blksize < TP_BSIZE )
+ fssize = TP_BSIZE;
+ if (stbuf.st_blksize >= TP_BSIZE && stbuf.st_blksize <= MAXBSIZE)
+ fssize = stbuf.st_blksize;
+ if (((fssize - 1) & fssize) != 0) {
+ fprintf(stderr, "bad block size %ld\n", fssize);
+ done(1);
+ }
+ if (spcl.c_volume != 1) {
+ fprintf(stderr, "Tape is not volume 1 of the dump\n");
+ done(1);
+ }
+ if (gethead(&spcl) == FAIL) {
+ dprintf(stdout, "header read failed at %ld blocks\n", blksread);
+ panic("no header after volume mark!\n");
+ }
+ findinode(&spcl);
+ if (spcl.c_type != TS_CLRI) {
+ fprintf(stderr, "Cannot find file removal list\n");
+ done(1);
+ }
+ maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
+ dprintf(stdout, "maxino = %d\n", maxino);
+ map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
+ if (map == NULL)
+ panic("no memory for active inode map\n");
+ usedinomap = map;
+ curfile.action = USING;
+ getfile(xtrmap, xtrmapskip);
+ if (spcl.c_type != TS_BITS) {
+ fprintf(stderr, "Cannot find file dump list\n");
+ done(1);
+ }
+ map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
+ if (map == (char *)NULL)
+ panic("no memory for file dump list\n");
+ dumpmap = map;
+ curfile.action = USING;
+ getfile(xtrmap, xtrmapskip);
+ /*
+ * If there may be whiteout entries on the tape, pretend that the
+ * whiteout inode exists, so that the whiteout entries can be
+ * extracted.
+ */
+ SETINO(WINO, dumpmap);
+ /* 'r' restores don't call getvol() for tape 1, so mark it as read. */
+ if (command == 'r')
+ tapesread = 1;
+}
+
+/*
+ * Prompt user to load a new dump volume.
+ * "Nextvol" is the next suggested volume to use.
+ * This suggested volume is enforced when doing full
+ * or incremental restores, but can be overridden by
+ * the user when only extracting a subset of the files.
+ */
+void
+getvol(long nextvol)
+{
+ int64_t prevtapea;
+ long i, newvol, savecnt;
+ union u_spcl tmpspcl;
+# define tmpbuf tmpspcl.s_spcl
+ char buf[TP_BSIZE];
+
+ if (nextvol == 1) {
+ tapesread = 0;
+ gettingfile = 0;
+ }
+ prevtapea = tapeaddr;
+ savecnt = blksread;
+ if (pipein) {
+ if (nextvol != 1) {
+ panic("Changing volumes on pipe input?\n");
+ /* Avoid looping if we couldn't ask the user. */
+ if (yflag || ferror(terminal) || feof(terminal))
+ done(1);
+ }
+ if (volno == 1)
+ return;
+ if (pipecmdin) {
+ closemt();
+ goto getpipecmdhdr;
+ }
+ goto gethdr;
+ }
+again:
+ if (pipein)
+ done(1); /* pipes do not get a second chance */
+ if (command == 'R' || command == 'r' || curfile.action != SKIP)
+ newvol = nextvol;
+ else
+ newvol = 0;
+ while (newvol <= 0) {
+ if (tapesread == 0) {
+ fprintf(stderr, "%s%s%s%s%s%s%s",
+ "You have not read any tapes yet.\n",
+ "If you are extracting just a few files,",
+ " start with the last volume\n",
+ "and work towards the first; restore",
+ " can quickly skip tapes that\n",
+ "have no further files to extract.",
+ " Otherwise, begin with volume 1.\n");
+ } else {
+ fprintf(stderr, "You have read volumes");
+ strcpy(buf, ": ");
+ for (i = 0; i < 32; i++)
+ if (tapesread & (1 << i)) {
+ fprintf(stderr, "%s%ld", buf, i + 1);
+ strcpy(buf, ", ");
+ }
+ fprintf(stderr, "\n");
+ }
+ do {
+ fprintf(stderr, "Specify next volume #: ");
+ (void) fflush(stderr);
+ if (fgets(buf, BUFSIZ, terminal) == NULL)
+ done(1);
+ } while (buf[0] == '\n');
+ newvol = atoi(buf);
+ if (newvol <= 0) {
+ fprintf(stderr,
+ "Volume numbers are positive numerics\n");
+ }
+ }
+ if (newvol == volno) {
+ tapesread |= 1 << (volno - 1);
+ return;
+ }
+ closemt();
+ fprintf(stderr, "Mount tape volume %ld\n", newvol);
+ fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
+ fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
+ (void) fflush(stderr);
+ if (fgets(buf, BUFSIZ, terminal) == NULL)
+ done(1);
+ if (!strcmp(buf, "none\n")) {
+ terminateinput();
+ return;
+ }
+ if (buf[0] != '\n') {
+ (void) strcpy(magtape, buf);
+ magtape[strlen(magtape) - 1] = '\0';
+ }
+ if (pipecmdin) {
+ char volno[sizeof("2147483647")];
+
+getpipecmdhdr:
+ (void)sprintf(volno, "%d", newvol);
+ if (setenv("RESTORE_VOLUME", volno, 1) == -1) {
+ fprintf(stderr, "Cannot set $RESTORE_VOLUME: %s\n",
+ strerror(errno));
+ done(1);
+ }
+ popenfp = popen(magtape, "r");
+ mt = popenfp ? fileno(popenfp) : -1;
+ } else
+#ifdef RRESTORE
+ if (host)
+ mt = rmtopen(magtape, 0);
+ else
+#endif
+ mt = open(magtape, O_RDONLY, 0);
+
+ if (mt == -1) {
+ fprintf(stderr, "Cannot open %s\n", magtape);
+ volno = -1;
+ goto again;
+ }
+gethdr:
+ volno = newvol;
+ setdumpnum();
+ FLUSHTAPEBUF();
+ if (gethead(&tmpbuf) == FAIL) {
+ dprintf(stdout, "header read failed at %ld blocks\n", blksread);
+ fprintf(stderr, "tape is not dump tape\n");
+ volno = 0;
+ goto again;
+ }
+ if (tmpbuf.c_volume != volno) {
+ fprintf(stderr, "Wrong volume (%ld)\n", tmpbuf.c_volume);
+ volno = 0;
+ goto again;
+ }
+ if (_time64_to_time(tmpbuf.c_date) != dumpdate ||
+ _time64_to_time(tmpbuf.c_ddate) != dumptime) {
+ time_t t = _time64_to_time(tmpbuf.c_date);
+ fprintf(stderr, "Wrong dump date\n\tgot: %s", ctime(&t));
+ fprintf(stderr, "\twanted: %s", ctime(&dumpdate));
+ volno = 0;
+ goto again;
+ }
+ tapesread |= 1 << (volno - 1);
+ blksread = savecnt;
+ /*
+ * If continuing from the previous volume, skip over any
+ * blocks read already at the end of the previous volume.
+ *
+ * If coming to this volume at random, skip to the beginning
+ * of the next record.
+ */
+ dprintf(stdout, "last rec %qd, tape starts with %qd\n", prevtapea,
+ tmpbuf.c_tapea);
+ if (tmpbuf.c_type == TS_TAPE) {
+ if (curfile.action != USING) {
+ /*
+ * XXX Dump incorrectly sets c_count to 1 in the
+ * volume header of the first tape, so ignore
+ * c_count when volno == 1.
+ */
+ if (volno != 1)
+ for (i = tmpbuf.c_count; i > 0; i--)
+ readtape(buf);
+ } else if (tmpbuf.c_tapea <= prevtapea) {
+ /*
+ * Normally the value of c_tapea in the volume
+ * header is the record number of the header itself.
+ * However in the volume header following an EOT-
+ * terminated tape, it is the record number of the
+ * first continuation data block (dump bug?).
+ *
+ * The next record we want is `prevtapea + 1'.
+ */
+ i = prevtapea + 1 - tmpbuf.c_tapea;
+ dprintf(stderr, "Skipping %ld duplicate record%s.\n",
+ i, i > 1 ? "s" : "");
+ while (--i >= 0)
+ readtape(buf);
+ }
+ }
+ if (curfile.action == USING) {
+ if (volno == 1)
+ panic("active file into volume 1\n");
+ return;
+ }
+ (void) gethead(&spcl);
+ findinode(&spcl);
+ if (gettingfile) {
+ gettingfile = 0;
+ longjmp(restart, 1);
+ }
+}
+
+/*
+ * Handle unexpected EOF.
+ */
+static void
+terminateinput(void)
+{
+
+ if (gettingfile && curfile.action == USING) {
+ printf("Warning: %s %s\n",
+ "End-of-input encountered while extracting", curfile.name);
+ }
+ curfile.name = "<name unknown>";
+ curfile.action = UNKNOWN;
+ curfile.mode = 0;
+ curfile.ino = maxino;
+ if (gettingfile) {
+ gettingfile = 0;
+ longjmp(restart, 1);
+ }
+}
+
+/*
+ * handle multiple dumps per tape by skipping forward to the
+ * appropriate one.
+ */
+static void
+setdumpnum(void)
+{
+ struct mtop tcom;
+
+ if (dumpnum == 1 || volno != 1)
+ return;
+ if (pipein) {
+ fprintf(stderr, "Cannot have multiple dumps on pipe input\n");
+ done(1);
+ }
+ tcom.mt_op = MTFSF;
+ tcom.mt_count = dumpnum - 1;
+#ifdef RRESTORE
+ if (host)
+ rmtioctl(MTFSF, dumpnum - 1);
+ else
+#endif
+ if (!pipecmdin && ioctl(mt, MTIOCTOP, (char *)&tcom) < 0)
+ fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno));
+}
+
+void
+printdumpinfo(void)
+{
+ time_t t;
+ t = _time64_to_time(spcl.c_date);
+ fprintf(stdout, "Dump date: %s", ctime(&t));
+ t = _time64_to_time(spcl.c_ddate);
+ fprintf(stdout, "Dumped from: %s",
+ (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&t));
+ if (spcl.c_host[0] == '\0')
+ return;
+ fprintf(stderr, "Level %ld dump of %s on %s:%s\n",
+ spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev);
+ fprintf(stderr, "Label: %s\n", spcl.c_label);
+}
+
+int
+extractfile(char *name)
+{
+ int flags;
+ mode_t mode;
+ struct timeval mtimep[2], ctimep[2];
+ struct entry *ep;
+
+ curfile.name = name;
+ curfile.action = USING;
+ mtimep[0].tv_sec = curfile.atime_sec;
+ mtimep[0].tv_usec = curfile.atime_nsec / 1000;
+ mtimep[1].tv_sec = curfile.mtime_sec;
+ mtimep[1].tv_usec = curfile.mtime_nsec / 1000;
+ ctimep[0].tv_sec = curfile.atime_sec;
+ ctimep[0].tv_usec = curfile.atime_nsec / 1000;
+ ctimep[1].tv_sec = curfile.birthtime_sec;
+ ctimep[1].tv_usec = curfile.birthtime_nsec / 1000;
+ mode = curfile.mode;
+ flags = curfile.file_flags;
+ switch (mode & IFMT) {
+
+ default:
+ fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
+ skipfile();
+ return (FAIL);
+
+ case IFSOCK:
+ vprintf(stdout, "skipped socket %s\n", name);
+ skipfile();
+ return (GOOD);
+
+ case IFDIR:
+ if (mflag) {
+ ep = lookupname(name);
+ if (ep == NULL || ep->e_flags & EXTRACT)
+ panic("unextracted directory %s\n", name);
+ skipfile();
+ return (GOOD);
+ }
+ vprintf(stdout, "extract file %s\n", name);
+ return (genliteraldir(name, curfile.ino));
+
+ case IFLNK:
+ lnkbuf[0] = '\0';
+ pathlen = 0;
+ getfile(xtrlnkfile, xtrlnkskip);
+ if (pathlen == 0) {
+ vprintf(stdout,
+ "%s: zero length symbolic link (ignored)\n", name);
+ return (GOOD);
+ }
+ if (linkit(lnkbuf, name, SYMLINK) == GOOD) {
+ (void) lchown(name, curfile.uid, curfile.gid);
+ (void) lchmod(name, mode);
+ (void) lutimes(name, ctimep);
+ (void) lutimes(name, mtimep);
+ return (GOOD);
+ }
+ return (FAIL);
+
+ case IFIFO:
+ vprintf(stdout, "extract fifo %s\n", name);
+ if (Nflag) {
+ skipfile();
+ return (GOOD);
+ }
+ if (uflag && !Nflag)
+ (void)unlink(name);
+ if (mkfifo(name, mode) < 0) {
+ fprintf(stderr, "%s: cannot create fifo: %s\n",
+ name, strerror(errno));
+ skipfile();
+ return (FAIL);
+ }
+ (void) chown(name, curfile.uid, curfile.gid);
+ (void) chmod(name, mode);
+ (void) utimes(name, ctimep);
+ (void) utimes(name, mtimep);
+ (void) chflags(name, flags);
+ skipfile();
+ return (GOOD);
+
+ case IFCHR:
+ case IFBLK:
+ vprintf(stdout, "extract special file %s\n", name);
+ if (Nflag) {
+ skipfile();
+ return (GOOD);
+ }
+ if (uflag)
+ (void)unlink(name);
+ if (mknod(name, mode, (int)curfile.rdev) < 0) {
+ fprintf(stderr, "%s: cannot create special file: %s\n",
+ name, strerror(errno));
+ skipfile();
+ return (FAIL);
+ }
+ (void) chown(name, curfile.uid, curfile.gid);
+ (void) chmod(name, mode);
+ (void) utimes(name, ctimep);
+ (void) utimes(name, mtimep);
+ (void) chflags(name, flags);
+ skipfile();
+ return (GOOD);
+
+ case IFREG:
+ vprintf(stdout, "extract file %s\n", name);
+ if (Nflag) {
+ skipfile();
+ return (GOOD);
+ }
+ if (uflag)
+ (void)unlink(name);
+ if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC,
+ 0666)) < 0) {
+ fprintf(stderr, "%s: cannot create file: %s\n",
+ name, strerror(errno));
+ skipfile();
+ return (FAIL);
+ }
+ (void) fchown(ofile, curfile.uid, curfile.gid);
+ (void) fchmod(ofile, mode);
+ getfile(xtrfile, xtrskip);
+ (void) close(ofile);
+ (void) utimes(name, ctimep);
+ (void) utimes(name, mtimep);
+ (void) chflags(name, flags);
+ return (GOOD);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * skip over bit maps on the tape
+ */
+void
+skipmaps(void)
+{
+
+ while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI)
+ skipfile();
+}
+
+/*
+ * skip over a file on the tape
+ */
+void
+skipfile(void)
+{
+
+ curfile.action = SKIP;
+ getfile(xtrnull, xtrnull);
+}
+
+/*
+ * Extract a file from the tape.
+ * When an allocated block is found it is passed to the fill function;
+ * when an unallocated block (hole) is found, a zeroed buffer is passed
+ * to the skip function.
+ */
+void
+getfile(void (*fill)(char *, long), void (*skip)(char *, long))
+{
+ int i;
+ int curblk = 0;
+ quad_t size = spcl.c_size;
+ static char clearedbuf[MAXBSIZE];
+ char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
+ char junk[TP_BSIZE];
+
+ if (spcl.c_type == TS_END)
+ panic("ran off end of tape\n");
+ if (spcl.c_magic != FS_UFS2_MAGIC)
+ panic("not at beginning of a file\n");
+ if (!gettingfile && setjmp(restart) != 0)
+ return;
+ gettingfile++;
+loop:
+ for (i = 0; i < spcl.c_count; i++) {
+ if (readmapflag || spcl.c_addr[i]) {
+ readtape(&buf[curblk++][0]);
+ if (curblk == fssize / TP_BSIZE) {
+ (*fill)((char *)buf, (long)(size > TP_BSIZE ?
+ fssize : (curblk - 1) * TP_BSIZE + size));
+ curblk = 0;
+ }
+ } else {
+ if (curblk > 0) {
+ (*fill)((char *)buf, (long)(size > TP_BSIZE ?
+ curblk * TP_BSIZE :
+ (curblk - 1) * TP_BSIZE + size));
+ curblk = 0;
+ }
+ (*skip)(clearedbuf, (long)(size > TP_BSIZE ?
+ TP_BSIZE : size));
+ }
+ if ((size -= TP_BSIZE) <= 0) {
+ for (i++; i < spcl.c_count; i++)
+ if (readmapflag || spcl.c_addr[i])
+ readtape(junk);
+ break;
+ }
+ }
+ if (gethead(&spcl) == GOOD && size > 0) {
+ if (spcl.c_type == TS_ADDR)
+ goto loop;
+ dprintf(stdout,
+ "Missing address (header) block for %s at %ld blocks\n",
+ curfile.name, blksread);
+ }
+ if (curblk > 0)
+ (*fill)((char *)buf, (long)((curblk * TP_BSIZE) + size));
+ findinode(&spcl);
+ gettingfile = 0;
+}
+
+/*
+ * Write out the next block of a file.
+ */
+static void
+xtrfile(char *buf, long size)
+{
+
+ if (Nflag)
+ return;
+ if (write(ofile, buf, (int) size) == -1) {
+ fprintf(stderr,
+ "write error extracting inode %d, name %s\nwrite: %s\n",
+ curfile.ino, curfile.name, strerror(errno));
+ }
+}
+
+/*
+ * Skip over a hole in a file.
+ */
+/* ARGSUSED */
+static void
+xtrskip(char *buf, long size)
+{
+
+ if (lseek(ofile, size, SEEK_CUR) == -1) {
+ fprintf(stderr,
+ "seek error extracting inode %d, name %s\nlseek: %s\n",
+ curfile.ino, curfile.name, strerror(errno));
+ done(1);
+ }
+}
+
+/*
+ * Collect the next block of a symbolic link.
+ */
+static void
+xtrlnkfile(char *buf, long size)
+{
+
+ pathlen += size;
+ if (pathlen > MAXPATHLEN) {
+ fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n",
+ curfile.name, lnkbuf, buf, pathlen);
+ done(1);
+ }
+ (void) strcat(lnkbuf, buf);
+}
+
+/*
+ * Skip over a hole in a symbolic link (should never happen).
+ */
+/* ARGSUSED */
+static void
+xtrlnkskip(char *buf, long size)
+{
+
+ fprintf(stderr, "unallocated block in symbolic link %s\n",
+ curfile.name);
+ done(1);
+}
+
+/*
+ * Collect the next block of a bit map.
+ */
+static void
+xtrmap(char *buf, long size)
+{
+
+ memmove(map, buf, size);
+ map += size;
+}
+
+/*
+ * Skip over a hole in a bit map (should never happen).
+ */
+/* ARGSUSED */
+static void
+xtrmapskip(char *buf, long size)
+{
+
+ panic("hole in map\n");
+ map += size;
+}
+
+/*
+ * Noop, when an extraction function is not needed.
+ */
+/* ARGSUSED */
+void
+xtrnull(char *buf, long size)
+{
+
+ return;
+}
+
+/*
+ * Read TP_BSIZE blocks from the input.
+ * Handle read errors, and end of media.
+ */
+static void
+readtape(char *buf)
+{
+ long rd, newvol, i;
+ int cnt, seek_failed;
+
+ if (blkcnt < numtrec) {
+ memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE);
+ blksread++;
+ tapeaddr++;
+ return;
+ }
+ for (i = 0; i < ntrec; i++)
+ ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
+ if (numtrec == 0)
+ numtrec = ntrec;
+ cnt = ntrec * TP_BSIZE;
+ rd = 0;
+getmore:
+#ifdef RRESTORE
+ if (host)
+ i = rmtread(&tapebuf[rd], cnt);
+ else
+#endif
+ i = read(mt, &tapebuf[rd], cnt);
+ /*
+ * Check for mid-tape short read error.
+ * If found, skip rest of buffer and start with the next.
+ */
+ if (!pipein && numtrec < ntrec && i > 0) {
+ dprintf(stdout, "mid-media short read error.\n");
+ numtrec = ntrec;
+ }
+ /*
+ * Handle partial block read.
+ */
+ if (pipein && i == 0 && rd > 0)
+ i = rd;
+ else if (i > 0 && i != ntrec * TP_BSIZE) {
+ if (pipein) {
+ rd += i;
+ cnt -= i;
+ if (cnt > 0)
+ goto getmore;
+ i = rd;
+ } else {
+ /*
+ * Short read. Process the blocks read.
+ */
+ if (i % TP_BSIZE != 0)
+ vprintf(stdout,
+ "partial block read: %ld should be %ld\n",
+ i, ntrec * TP_BSIZE);
+ numtrec = i / TP_BSIZE;
+ }
+ }
+ /*
+ * Handle read error.
+ */
+ if (i < 0) {
+ fprintf(stderr, "Tape read error while ");
+ switch (curfile.action) {
+ default:
+ fprintf(stderr, "trying to set up tape\n");
+ break;
+ case UNKNOWN:
+ fprintf(stderr, "trying to resynchronize\n");
+ break;
+ case USING:
+ fprintf(stderr, "restoring %s\n", curfile.name);
+ break;
+ case SKIP:
+ fprintf(stderr, "skipping over inode %d\n",
+ curfile.ino);
+ break;
+ }
+ if (!yflag && !reply("continue"))
+ done(1);
+ i = ntrec * TP_BSIZE;
+ memset(tapebuf, 0, i);
+#ifdef RRESTORE
+ if (host)
+ seek_failed = (rmtseek(i, 1) < 0);
+ else
+#endif
+ seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1);
+
+ if (seek_failed) {
+ fprintf(stderr,
+ "continuation failed: %s\n", strerror(errno));
+ done(1);
+ }
+ }
+ /*
+ * Handle end of tape.
+ */
+ if (i == 0) {
+ vprintf(stdout, "End-of-tape encountered\n");
+ if (!pipein) {
+ newvol = volno + 1;
+ volno = 0;
+ numtrec = 0;
+ getvol(newvol);
+ readtape(buf);
+ return;
+ }
+ if (rd % TP_BSIZE != 0)
+ panic("partial block read: %d should be %d\n",
+ rd, ntrec * TP_BSIZE);
+ terminateinput();
+ memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE);
+ }
+ blkcnt = 0;
+ memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE);
+ blksread++;
+ tapeaddr++;
+}
+
+static void
+findtapeblksize(void)
+{
+ long i;
+
+ for (i = 0; i < ntrec; i++)
+ ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
+ blkcnt = 0;
+#ifdef RRESTORE
+ if (host)
+ i = rmtread(tapebuf, ntrec * TP_BSIZE);
+ else
+#endif
+ i = read(mt, tapebuf, ntrec * TP_BSIZE);
+
+ if (i <= 0) {
+ fprintf(stderr, "tape read error: %s\n", strerror(errno));
+ done(1);
+ }
+ if (i % TP_BSIZE != 0) {
+ fprintf(stderr, "Tape block size (%ld) %s (%d)\n",
+ i, "is not a multiple of dump block size", TP_BSIZE);
+ done(1);
+ }
+ ntrec = i / TP_BSIZE;
+ numtrec = ntrec;
+ vprintf(stdout, "Tape block size is %ld\n", ntrec);
+}
+
+void
+closemt(void)
+{
+
+ if (mt < 0)
+ return;
+ if (pipecmdin) {
+ pclose(popenfp);
+ popenfp = NULL;
+ } else
+#ifdef RRESTORE
+ if (host)
+ rmtclose();
+ else
+#endif
+ (void) close(mt);
+}
+
+/*
+ * Read the next block from the tape.
+ * If it is not any valid header, return an error.
+ */
+static int
+gethead(struct s_spcl *buf)
+{
+ long i;
+
+ readtape((char *)buf);
+ if (buf->c_magic != FS_UFS2_MAGIC && buf->c_magic != NFS_MAGIC) {
+ if (buf->c_magic == OFS_MAGIC) {
+ fprintf(stderr,
+ "Format of dump tape is too old. Must use\n");
+ fprintf(stderr,
+ "a version of restore from before 2002.\n");
+ return (FAIL);
+ }
+ if (swabl(buf->c_magic) != FS_UFS2_MAGIC &&
+ buf->c_magic != NFS_MAGIC) {
+ if (buf->c_magic == OFS_MAGIC) {
+ fprintf(stderr,
+ "Format of dump tape is too old. Must use\n");
+ fprintf(stderr,
+ "a version of restore from before 2002.\n");
+ }
+ return (FAIL);
+ }
+ if (!Bcvt) {
+ vprintf(stdout, "Note: Doing Byte swapping\n");
+ Bcvt = 1;
+ }
+ }
+ if (checksum((int *)buf) == FAIL)
+ return (FAIL);
+ if (Bcvt) {
+ swabst((u_char *)"8l4s1q8l2q17l", (u_char *)buf);
+ swabst((u_char *)"l",(u_char *) &buf->c_level);
+ swabst((u_char *)"2l4q",(u_char *) &buf->c_flags);
+ }
+ readmapflag = 0;
+
+ switch (buf->c_type) {
+
+ case TS_CLRI:
+ case TS_BITS:
+ /*
+ * Have to patch up missing information in bit map headers
+ */
+ buf->c_inumber = 0;
+ buf->c_size = buf->c_count * TP_BSIZE;
+ if (buf->c_count > TP_NINDIR)
+ readmapflag = 1;
+ else
+ for (i = 0; i < buf->c_count; i++)
+ buf->c_addr[i]++;
+ break;
+
+ case TS_TAPE:
+ if (buf->c_magic == NFS_MAGIC) {
+ if ((buf->c_flags & NFS_DR_NEWINODEFMT) == 0)
+ oldinofmt = 1;
+ buf->c_date = _time32_to_time(buf->c_old_date);
+ buf->c_ddate = _time32_to_time(buf->c_old_ddate);
+ buf->c_tapea = buf->c_old_tapea;
+ buf->c_firstrec = buf->c_old_firstrec;
+ }
+ case TS_END:
+ buf->c_inumber = 0;
+ break;
+
+ case TS_INODE:
+ /*
+ * For old dump tapes, have to copy up old fields to
+ * new locations.
+ */
+ if (buf->c_magic == NFS_MAGIC) {
+ buf->c_tapea = buf->c_old_tapea;
+ buf->c_firstrec = buf->c_old_firstrec;
+ buf->c_date = _time32_to_time(buf->c_old_date);
+ buf->c_ddate = _time32_to_time(buf->c_old_ddate);
+ buf->c_atime = _time32_to_time(buf->c_old_atime);
+ buf->c_mtime = _time32_to_time(buf->c_old_mtime);
+ }
+ break;
+
+ case TS_ADDR:
+ break;
+
+ default:
+ panic("gethead: unknown inode type %d\n", buf->c_type);
+ break;
+ }
+ /*
+ * If we're restoring a filesystem with the old (FreeBSD 1)
+ * format inodes, copy the uid/gid to the new location
+ */
+ if (oldinofmt) {
+ buf->c_uid = buf->c_spare1[1];
+ buf->c_gid = buf->c_spare1[2];
+ }
+ buf->c_magic = FS_UFS2_MAGIC;
+ tapeaddr = buf->c_tapea;
+ if (dflag)
+ accthdr(buf);
+ return(GOOD);
+}
+
+/*
+ * Check that a header is where it belongs and predict the next header
+ */
+static void
+accthdr(struct s_spcl *header)
+{
+ static ino_t previno = 0x7fffffff;
+ static int prevtype;
+ static long predict;
+ long blks, i;
+
+ if (header->c_type == TS_TAPE) {
+ fprintf(stderr, "Volume header ");
+ if (header->c_firstrec)
+ fprintf(stderr, "begins with record %qd",
+ header->c_firstrec);
+ fprintf(stderr, "\n");
+ previno = 0x7fffffff;
+ return;
+ }
+ if (previno == 0x7fffffff)
+ goto newcalc;
+ switch (prevtype) {
+ case TS_BITS:
+ fprintf(stderr, "Dumped inodes map header");
+ break;
+ case TS_CLRI:
+ fprintf(stderr, "Used inodes map header");
+ break;
+ case TS_INODE:
+ fprintf(stderr, "File header, ino %d", previno);
+ break;
+ case TS_ADDR:
+ fprintf(stderr, "File continuation header, ino %d", previno);
+ break;
+ case TS_END:
+ fprintf(stderr, "End of tape header");
+ break;
+ }
+ if (predict != blksread - 1)
+ fprintf(stderr, "; predicted %ld blocks, got %ld blocks",
+ predict, blksread - 1);
+ fprintf(stderr, "\n");
+newcalc:
+ blks = 0;
+ if (header->c_type != TS_END)
+ for (i = 0; i < header->c_count; i++)
+ if (readmapflag || header->c_addr[i] != 0)
+ blks++;
+ predict = blks;
+ blksread = 0;
+ prevtype = header->c_type;
+ previno = header->c_inumber;
+}
+
+/*
+ * Find an inode header.
+ * Complain if had to skip.
+ */
+static void
+findinode(struct s_spcl *header)
+{
+ static long skipcnt = 0;
+ long i;
+ char buf[TP_BSIZE];
+ int htype;
+
+ curfile.name = "<name unknown>";
+ curfile.action = UNKNOWN;
+ curfile.mode = 0;
+ curfile.ino = 0;
+ do {
+ htype = header->c_type;
+ switch (htype) {
+
+ case TS_ADDR:
+ /*
+ * Skip up to the beginning of the next record
+ */
+ for (i = 0; i < header->c_count; i++)
+ if (header->c_addr[i])
+ readtape(buf);
+ while (gethead(header) == FAIL ||
+ _time64_to_time(header->c_date) != dumpdate)
+ skipcnt++;
+ break;
+
+ case TS_INODE:
+ curfile.mode = header->c_mode;
+ curfile.uid = header->c_uid;
+ curfile.gid = header->c_gid;
+ curfile.file_flags = header->c_file_flags;
+ curfile.rdev = header->c_rdev;
+ curfile.atime_sec = header->c_atime;
+ curfile.atime_nsec = header->c_atimensec;
+ curfile.mtime_sec = header->c_mtime;
+ curfile.mtime_nsec = header->c_mtimensec;
+ curfile.birthtime_sec = header->c_birthtime;
+ curfile.birthtime_nsec = header->c_birthtimensec;
+ curfile.size = header->c_size;
+ curfile.ino = header->c_inumber;
+ break;
+
+ case TS_END:
+ /* If we missed some tapes, get another volume. */
+ if (tapesread & (tapesread + 1)) {
+ getvol(0);
+ continue;
+ }
+ curfile.ino = maxino;
+ break;
+
+ case TS_CLRI:
+ curfile.name = "<file removal list>";
+ break;
+
+ case TS_BITS:
+ curfile.name = "<file dump list>";
+ break;
+
+ case TS_TAPE:
+ panic("unexpected tape header\n");
+ /* NOTREACHED */
+
+ default:
+ panic("unknown tape header type %d\n", spcl.c_type);
+ /* NOTREACHED */
+
+ }
+ } while (htype == TS_ADDR);
+ if (skipcnt > 0)
+ fprintf(stderr, "resync restore, skipped %ld blocks\n",
+ skipcnt);
+ skipcnt = 0;
+}
+
+static int
+checksum(int *buf)
+{
+ int i, j;
+
+ j = sizeof(union u_spcl) / sizeof(int);
+ i = 0;
+ if (!Bcvt) {
+ do
+ i += *buf++;
+ while (--j);
+ } else {
+ /* What happens if we want to read restore tapes
+ for a 16bit int machine??? */
+ do
+ i += swabl(*buf++);
+ while (--j);
+ }
+
+ if (i != CHECKSUM) {
+ fprintf(stderr, "Checksum error %o, inode %d file %s\n", i,
+ curfile.ino, curfile.name);
+ return(FAIL);
+ }
+ return(GOOD);
+}
+
+#ifdef RRESTORE
+#include <stdarg.h>
+
+void
+msg(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+#endif /* RRESTORE */
+
+static u_char *
+swabshort(u_char *sp, int n)
+{
+ char c;
+
+ while (--n >= 0) {
+ c = sp[0]; sp[0] = sp[1]; sp[1] = c;
+ sp += 2;
+ }
+ return (sp);
+}
+
+static u_char *
+swablong(u_char *sp, int n)
+{
+ char c;
+
+ while (--n >= 0) {
+ c = sp[0]; sp[0] = sp[3]; sp[3] = c;
+ c = sp[2]; sp[2] = sp[1]; sp[1] = c;
+ sp += 4;
+ }
+ return (sp);
+}
+
+static u_char *
+swabquad(u_char *sp, int n)
+{
+ char c;
+
+ while (--n >= 0) {
+ c = sp[0]; sp[0] = sp[7]; sp[7] = c;
+ c = sp[1]; sp[1] = sp[6]; sp[6] = c;
+ c = sp[2]; sp[2] = sp[5]; sp[5] = c;
+ c = sp[3]; sp[3] = sp[4]; sp[4] = c;
+ sp += 8;
+ }
+ return (sp);
+}
+
+void
+swabst(u_char *cp, u_char *sp)
+{
+ int n = 0;
+
+ while (*cp) {
+ switch (*cp) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ n = (n * 10) + (*cp++ - '0');
+ continue;
+
+ case 's': case 'w': case 'h':
+ if (n == 0)
+ n = 1;
+ sp = swabshort(sp, n);
+ break;
+
+ case 'l':
+ if (n == 0)
+ n = 1;
+ sp = swablong(sp, n);
+ break;
+
+ case 'q':
+ if (n == 0)
+ n = 1;
+ sp = swabquad(sp, n);
+ break;
+
+ case 'b':
+ if (n == 0)
+ n = 1;
+ sp += n;
+ break;
+
+ default:
+ fprintf(stderr, "Unknown conversion character: %c\n",
+ *cp);
+ done(0);
+ break;
+ }
+ cp++;
+ n = 0;
+ }
+}
+
+static u_long
+swabl(u_long x)
+{
+ swabst((u_char *)"l", (u_char *)&x);
+ return (x);
+}
diff --git a/sbin/restore/utilities.c b/sbin/restore/utilities.c
new file mode 100644
index 0000000..6b984e4
--- /dev/null
+++ b/sbin/restore/utilities.c
@@ -0,0 +1,421 @@
+/*
+ * 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.
+ * 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 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 lint
+#if 0
+static char sccsid[] = "@(#)utilities.c 8.5 (Berkeley) 4/28/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "restore.h"
+#include "extern.h"
+
+/*
+ * Insure that all the components of a pathname exist.
+ */
+void
+pathcheck(char *name)
+{
+ char *cp;
+ struct entry *ep;
+ char *start;
+
+ start = strchr(name, '/');
+ if (start == 0)
+ return;
+ for (cp = start; *cp != '\0'; cp++) {
+ if (*cp != '/')
+ continue;
+ *cp = '\0';
+ ep = lookupname(name);
+ if (ep == NULL) {
+ /* Safe; we know the pathname exists in the dump. */
+ ep = addentry(name, pathsearch(name)->d_ino, NODE);
+ newnode(ep);
+ }
+ ep->e_flags |= NEW|KEEP;
+ *cp = '/';
+ }
+}
+
+/*
+ * Change a name to a unique temporary name.
+ */
+void
+mktempname(struct entry *ep)
+{
+ char oldname[MAXPATHLEN];
+
+ if (ep->e_flags & TMPNAME)
+ badentry(ep, "mktempname: called with TMPNAME");
+ ep->e_flags |= TMPNAME;
+ (void) strcpy(oldname, myname(ep));
+ freename(ep->e_name);
+ ep->e_name = savename(gentempname(ep));
+ ep->e_namlen = strlen(ep->e_name);
+ renameit(oldname, myname(ep));
+}
+
+/*
+ * Generate a temporary name for an entry.
+ */
+char *
+gentempname(struct entry *ep)
+{
+ static char name[MAXPATHLEN];
+ struct entry *np;
+ long i = 0;
+
+ for (np = lookupino(ep->e_ino);
+ np != NULL && np != ep; np = np->e_links)
+ i++;
+ if (np == NULL)
+ badentry(ep, "not on ino list");
+ (void) sprintf(name, "%s%ld%lu", TMPHDR, i, (u_long)ep->e_ino);
+ return (name);
+}
+
+/*
+ * Rename a file or directory.
+ */
+void
+renameit(char *from, char *to)
+{
+ if (!Nflag && rename(from, to) < 0) {
+ fprintf(stderr, "warning: cannot rename %s to %s: %s\n",
+ from, to, strerror(errno));
+ return;
+ }
+ vprintf(stdout, "rename %s to %s\n", from, to);
+}
+
+/*
+ * Create a new node (directory).
+ */
+void
+newnode(struct entry *np)
+{
+ char *cp;
+
+ if (np->e_type != NODE)
+ badentry(np, "newnode: not a node");
+ cp = myname(np);
+ if (!Nflag && mkdir(cp, 0777) < 0 && !uflag) {
+ np->e_flags |= EXISTED;
+ fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
+ return;
+ }
+ vprintf(stdout, "Make node %s\n", cp);
+}
+
+/*
+ * Remove an old node (directory).
+ */
+void
+removenode(struct entry *ep)
+{
+ char *cp;
+
+ if (ep->e_type != NODE)
+ badentry(ep, "removenode: not a node");
+ if (ep->e_entries != NULL)
+ badentry(ep, "removenode: non-empty directory");
+ ep->e_flags |= REMOVED;
+ ep->e_flags &= ~TMPNAME;
+ cp = myname(ep);
+ if (!Nflag && rmdir(cp) < 0) {
+ fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
+ return;
+ }
+ vprintf(stdout, "Remove node %s\n", cp);
+}
+
+/*
+ * Remove a leaf.
+ */
+void
+removeleaf(struct entry *ep)
+{
+ char *cp;
+
+ if (ep->e_type != LEAF)
+ badentry(ep, "removeleaf: not a leaf");
+ ep->e_flags |= REMOVED;
+ ep->e_flags &= ~TMPNAME;
+ cp = myname(ep);
+ if (!Nflag && unlink(cp) < 0) {
+ fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
+ return;
+ }
+ vprintf(stdout, "Remove leaf %s\n", cp);
+}
+
+/*
+ * Create a link.
+ */
+int
+linkit(char *existing, char *new, int type)
+{
+
+ /* if we want to unlink first, do it now so *link() won't fail */
+ if (uflag && !Nflag)
+ (void)unlink(new);
+
+ if (type == SYMLINK) {
+ if (!Nflag && symlink(existing, new) < 0) {
+ fprintf(stderr,
+ "warning: cannot create symbolic link %s->%s: %s\n",
+ new, existing, strerror(errno));
+ return (FAIL);
+ }
+ } else if (type == HARDLINK) {
+ int ret;
+
+ if (!Nflag && (ret = link(existing, new)) < 0) {
+ struct stat s;
+
+ /*
+ * Most likely, the schg flag is set. Clear the
+ * flags and try again.
+ */
+ if (stat(existing, &s) == 0 && s.st_flags != 0 &&
+ chflags(existing, 0) == 0) {
+ ret = link(existing, new);
+ chflags(existing, s.st_flags);
+ }
+ if (ret < 0) {
+ fprintf(stderr, "warning: cannot create "
+ "hard link %s->%s: %s\n",
+ new, existing, strerror(errno));
+ return (FAIL);
+ }
+ }
+ } else {
+ panic("linkit: unknown type %d\n", type);
+ return (FAIL);
+ }
+ vprintf(stdout, "Create %s link %s->%s\n",
+ type == SYMLINK ? "symbolic" : "hard", new, existing);
+ return (GOOD);
+}
+
+/*
+ * Create a whiteout.
+ */
+int
+addwhiteout(char *name)
+{
+
+ if (!Nflag && mknod(name, S_IFWHT, 0) < 0) {
+ fprintf(stderr, "warning: cannot create whiteout %s: %s\n",
+ name, strerror(errno));
+ return (FAIL);
+ }
+ vprintf(stdout, "Create whiteout %s\n", name);
+ return (GOOD);
+}
+
+/*
+ * Delete a whiteout.
+ */
+void
+delwhiteout(struct entry *ep)
+{
+ char *name;
+
+ if (ep->e_type != LEAF)
+ badentry(ep, "delwhiteout: not a leaf");
+ ep->e_flags |= REMOVED;
+ ep->e_flags &= ~TMPNAME;
+ name = myname(ep);
+ if (!Nflag && undelete(name) < 0) {
+ fprintf(stderr, "warning: cannot delete whiteout %s: %s\n",
+ name, strerror(errno));
+ return;
+ }
+ vprintf(stdout, "Delete whiteout %s\n", name);
+}
+
+/*
+ * find lowest number file (above "start") that needs to be extracted
+ */
+ino_t
+lowerbnd(ino_t start)
+{
+ struct entry *ep;
+
+ for ( ; start < maxino; start++) {
+ ep = lookupino(start);
+ if (ep == NULL || ep->e_type == NODE)
+ continue;
+ if (ep->e_flags & (NEW|EXTRACT))
+ return (start);
+ }
+ return (start);
+}
+
+/*
+ * find highest number file (below "start") that needs to be extracted
+ */
+ino_t
+upperbnd(ino_t start)
+{
+ struct entry *ep;
+
+ for ( ; start > ROOTINO; start--) {
+ ep = lookupino(start);
+ if (ep == NULL || ep->e_type == NODE)
+ continue;
+ if (ep->e_flags & (NEW|EXTRACT))
+ return (start);
+ }
+ return (start);
+}
+
+/*
+ * report on a badly formed entry
+ */
+void
+badentry(struct entry *ep, char *msg)
+{
+
+ fprintf(stderr, "bad entry: %s\n", msg);
+ fprintf(stderr, "name: %s\n", myname(ep));
+ fprintf(stderr, "parent name %s\n", myname(ep->e_parent));
+ if (ep->e_sibling != NULL)
+ fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling));
+ if (ep->e_entries != NULL)
+ fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries));
+ if (ep->e_links != NULL)
+ fprintf(stderr, "next link name: %s\n", myname(ep->e_links));
+ if (ep->e_next != NULL)
+ fprintf(stderr,
+ "next hashchain name: %s\n", myname(ep->e_next));
+ fprintf(stderr, "entry type: %s\n",
+ ep->e_type == NODE ? "NODE" : "LEAF");
+ fprintf(stderr, "inode number: %lu\n", (u_long)ep->e_ino);
+ panic("flags: %s\n", flagvalues(ep));
+}
+
+/*
+ * Construct a string indicating the active flag bits of an entry.
+ */
+char *
+flagvalues(struct entry *ep)
+{
+ static char flagbuf[BUFSIZ];
+
+ (void) strcpy(flagbuf, "|NIL");
+ flagbuf[0] = '\0';
+ if (ep->e_flags & REMOVED)
+ (void) strcat(flagbuf, "|REMOVED");
+ if (ep->e_flags & TMPNAME)
+ (void) strcat(flagbuf, "|TMPNAME");
+ if (ep->e_flags & EXTRACT)
+ (void) strcat(flagbuf, "|EXTRACT");
+ if (ep->e_flags & NEW)
+ (void) strcat(flagbuf, "|NEW");
+ if (ep->e_flags & KEEP)
+ (void) strcat(flagbuf, "|KEEP");
+ if (ep->e_flags & EXISTED)
+ (void) strcat(flagbuf, "|EXISTED");
+ return (&flagbuf[1]);
+}
+
+/*
+ * Check to see if a name is on a dump tape.
+ */
+ino_t
+dirlookup(const char *name)
+{
+ struct direct *dp;
+ ino_t ino;
+
+ ino = ((dp = pathsearch(name)) == NULL) ? 0 : dp->d_ino;
+
+ if (ino == 0 || TSTINO(ino, dumpmap) == 0)
+ fprintf(stderr, "%s is not on the tape\n", name);
+ return (ino);
+}
+
+/*
+ * Elicit a reply.
+ */
+int
+reply(char *question)
+{
+ int c;
+
+ do {
+ fprintf(stderr, "%s? [yn] ", question);
+ (void) fflush(stderr);
+ c = getc(terminal);
+ while (c != '\n' && getc(terminal) != '\n')
+ if (c == EOF)
+ return (FAIL);
+ } while (c != 'y' && c != 'n');
+ if (c == 'y')
+ return (GOOD);
+ return (FAIL);
+}
+
+/*
+ * handle unexpected inconsistencies
+ */
+#include <stdarg.h>
+
+void
+panic(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ if (yflag)
+ return;
+ if (reply("abort") == GOOD) {
+ if (reply("dump core") == GOOD)
+ abort();
+ done(1);
+ }
+}
diff --git a/sbin/route/Makefile b/sbin/route/Makefile
new file mode 100644
index 0000000..fede01d
--- /dev/null
+++ b/sbin/route/Makefile
@@ -0,0 +1,22 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= route
+MAN= route.8
+SRCS= route.c keywords.h
+CFLAGS+=-I. -Wall -DNS
+CFLAGS+=-DINET6
+WARNS?= 0
+CLEANFILES+=keywords.h _keywords.tmp
+
+keywords.h: keywords
+ sed -e '/^#/d' -e '/^$$/d' ${.CURDIR}/keywords > _keywords.tmp
+ LC_ALL=C tr 'a-z' 'A-Z' < _keywords.tmp | paste _keywords.tmp - | \
+ awk '{ \
+ if (NF > 1) \
+ printf "#define\tK_%s\t%d\n\t{\"%s\", K_%s},\n", \
+ $$2, NR, $$1, $$2 }' \
+ > ${.TARGET}
+ rm -f _keywords.tmp
+
+.include <bsd.prog.mk>
diff --git a/sbin/route/keywords b/sbin/route/keywords
new file mode 100644
index 0000000..130fcd1
--- /dev/null
+++ b/sbin/route/keywords
@@ -0,0 +1,51 @@
+# @(#)keywords 8.2 (Berkeley) 3/19/94
+# $FreeBSD$
+
+add
+atalk
+blackhole
+change
+cloning
+del
+delete
+dst
+expire
+flush
+gateway
+genmask
+get
+host
+hopcount
+iface
+interface
+ifa
+ifp
+inet
+inet6
+iso
+link
+llinfo
+lock
+lockrest
+mask
+monitor
+mtu
+net
+netmask
+nostatic
+osi
+prefixlen
+proto1
+proto2
+proxy
+recvpipe
+reject
+rtt
+rttvar
+sa
+sendpipe
+ssthresh
+static
+x25
+xns
+xresolve
diff --git a/sbin/route/route.8 b/sbin/route/route.8
new file mode 100644
index 0000000..ff75189
--- /dev/null
+++ b/sbin/route/route.8
@@ -0,0 +1,428 @@
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)route.8 8.3 (Berkeley) 3/19/94
+.\" $FreeBSD$
+.\"
+.Dd October 2, 2005
+.Dt ROUTE 8
+.Os
+.Sh NAME
+.Nm route
+.Nd manually manipulate the routing tables
+.Sh SYNOPSIS
+.Nm
+.Op Fl dnqtv
+.Ar command
+.Oo
+.Op Ar modifiers
+.Ar args
+.Oc
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to manually manipulate the network
+routing tables.
+It normally is not needed, as a
+system routing table management daemon, such as
+.Xr routed 8 ,
+should tend to this task.
+.Pp
+The
+.Nm
+utility supports a limited number of general options,
+but a rich command language, enabling the user to specify
+any arbitrary request that could be delivered via the
+programmatic interface discussed in
+.Xr route 4 .
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Run in debug-only mode, i.e., do not actually modify the routing table.
+.It Fl n
+Bypass attempts to print host and network names symbolically
+when reporting actions.
+(The process of translating between symbolic
+names and numerical equivalents can be quite time consuming, and
+may require correct operation of the network; thus it may be expedient
+to forget this, especially when attempting to repair networking operations).
+.It Fl v
+(verbose) Print additional details.
+.It Fl q
+Suppress all output from the
+.Cm add , change , delete ,
+and
+.Cm flush
+commands.
+.El
+.Pp
+The
+.Nm
+utility provides the following commands:
+.Pp
+.Bl -tag -width Fl -compact
+.It Cm add
+Add a route.
+.It Cm flush
+Remove all routes.
+.It Cm delete
+Delete a specific route.
+.It Cm del
+Another name for the
+.Cm delete
+command.
+.It Cm change
+Change aspects of a route (such as its gateway).
+.It Cm get
+Lookup and display the route for a destination.
+.It Cm monitor
+Continuously report any changes to the routing information base,
+routing lookup misses, or suspected network partitionings.
+.El
+.Pp
+The monitor command has the syntax:
+.Pp
+.Bd -ragged -offset indent -compact
+.Nm
+.Op Fl n
+.Cm monitor
+.Ed
+.Pp
+The flush command has the syntax:
+.Pp
+.Bd -ragged -offset indent -compact
+.Nm
+.Op Fl n
+.Cm flush
+.Op Ar family
+.Ed
+.Pp
+If the
+.Cm flush
+command is specified,
+.Nm
+will ``flush'' the routing tables of all gateway entries.
+When the address family may is specified by any of the
+.Fl osi ,
+.Fl xns ,
+.Fl atalk ,
+.Fl inet6 ,
+or
+.Fl inet
+modifiers, only routes having destinations with addresses in the
+delineated family will be deleted.
+.Pp
+The other commands have the following syntax:
+.Pp
+.Bd -ragged -offset indent -compact
+.Nm
+.Op Fl n
+.Ar command
+.Op Fl net No \&| Fl host
+.Ar destination gateway
+.Op Ar netmask
+.Ed
+.Pp
+where
+.Ar destination
+is the destination host or network,
+.Ar gateway
+is the next-hop intermediary via which packets should be routed.
+Routes to a particular host may be distinguished from those to
+a network by interpreting the Internet address specified as the
+.Ar destination
+argument.
+The optional modifiers
+.Fl net
+and
+.Fl host
+force the destination to be interpreted as a network or a host, respectively.
+Otherwise, if the
+.Ar destination
+has a
+.Dq local address part
+of
+INADDR_ANY
+.Pq Li 0.0.0.0 ,
+or if the
+.Ar destination
+is the symbolic name of a network, then the route is
+assumed to be to a network; otherwise, it is presumed to be a
+route to a host.
+Optionally, the
+.Ar destination
+could also be specified in the
+.Ar net Ns / Ns Ar bits
+format.
+.Pp
+For example,
+.Li 128.32
+is interpreted as
+.Fl host Li 128.0.0.32 ;
+.Li 128.32.130
+is interpreted as
+.Fl host Li 128.32.0.130 ;
+.Fl net Li 128.32
+is interpreted as
+.Li 128.32.0.0;
+.Fl net Li 128.32.130
+is interpreted as
+.Li 128.32.130.0;
+and
+.Li 192.168.64/20
+is interpreted as
+.Fl net Li 192.168.64 Fl netmask Li 255.255.240.0 .
+.Pp
+A
+.Ar destination
+of
+.Ar default
+is a synonym for
+.Fl net Li 0.0.0.0 ,
+which is the default route.
+.Pp
+If the destination is directly reachable
+via an interface requiring
+no intermediary system to act as a gateway, the
+.Fl interface
+modifier should be specified;
+the gateway given is the address of this host on the common network,
+indicating the interface to be used for transmission.
+Alternately, if the interface is point to point the name of the interface
+itself may be given, in which case the route remains valid even
+if the local or remote addresses change.
+.Pp
+The optional modifiers
+.Fl xns ,
+.Fl osi ,
+.Fl atalk ,
+and
+.Fl link
+specify that all subsequent addresses are in the
+.Tn XNS ,
+.Tn OSI ,
+or
+.Tn AppleTalk
+address families,
+or are specified as link-level addresses,
+and the names must be numeric specifications rather than
+symbolic names.
+.Pp
+The optional
+.Fl netmask
+modifier is intended
+to achieve the effect of an
+.Tn OSI
+.Tn ESIS
+redirect with the netmask option,
+or to manually add subnet routes with
+netmasks different from that of the implied network interface
+(as would otherwise be communicated using the OSPF or ISIS routing protocols).
+One specifies an additional ensuing address parameter
+(to be interpreted as a network mask).
+The implicit network mask generated in the AF_INET case
+can be overridden by making sure this option follows the destination parameter.
+.Pp
+For
+.Dv AF_INET6 ,
+the
+.Fl prefixlen
+qualifier
+is available instead of the
+.Fl mask
+qualifier because non-continuous masks are not allowed in IPv6.
+For example,
+.Fl prefixlen Li 32
+specifies network mask of
+.Li ffff:ffff:0000:0000:0000:0000:0000:0000
+to be used.
+The default value of prefixlen is 64 to get along with
+the aggregatable address.
+But 0 is assumed if
+.Cm default
+is specified.
+Note that the qualifier works only for
+.Dv AF_INET6
+address family.
+.Pp
+Routes have associated flags which influence operation of the protocols
+when sending to destinations matched by the routes.
+These flags may be set (or sometimes cleared)
+by indicating the following corresponding modifiers:
+.Bd -literal
+-cloning RTF_CLONING - generates a new route on use
+-xresolve RTF_XRESOLVE - emit mesg on use (for external lookup)
+-iface ~RTF_GATEWAY - destination is directly reachable
+-static RTF_STATIC - manually added route
+-nostatic ~RTF_STATIC - pretend route added by kernel or daemon
+-reject RTF_REJECT - emit an ICMP unreachable when matched
+-blackhole RTF_BLACKHOLE - silently discard pkts (during updates)
+-proto1 RTF_PROTO1 - set protocol specific routing flag #1
+-proto2 RTF_PROTO2 - set protocol specific routing flag #2
+-llinfo RTF_LLINFO - validly translates proto addr to link addr
+.Ed
+.Pp
+The optional modifiers
+.Fl rtt ,
+.Fl rttvar ,
+.Fl sendpipe ,
+.Fl recvpipe ,
+.Fl mtu ,
+.Fl hopcount ,
+.Fl expire ,
+and
+.Fl ssthresh
+provide initial values to quantities maintained in the routing entry
+by transport level protocols, such as TCP or TP4.
+These may be individually locked by preceding each such modifier to
+be locked by
+the
+.Fl lock
+meta-modifier, or one can
+specify that all ensuing metrics may be locked by the
+.Fl lockrest
+meta-modifier.
+.Pp
+In a
+.Cm change
+or
+.Cm add
+command where the destination and gateway are not sufficient to specify
+the route (as in the
+.Tn ISO
+case where several interfaces may have the
+same address), the
+.Fl ifp
+or
+.Fl ifa
+modifiers may be used to determine the interface or interface address.
+.Pp
+The optional
+.Fl proxy
+modifier specifies that the
+.Dv RTF_LLINFO
+routing table entry is the
+.Dq published (proxy-only)
+.Tn ARP
+entry, as reported by
+.Xr arp 8 .
+.Pp
+The optional
+.Fl genmask
+modifier specifies that a cloning mask is present.
+This specifies the mask applied when determining if a child route
+should be created.
+It is only applicable to network routes with the
+.Dv RTF_CLONING
+flag set.
+.Pp
+All symbolic names specified for a
+.Ar destination
+or
+.Ar gateway
+are looked up first as a host name using
+.Xr gethostbyname 3 .
+If this lookup fails,
+.Xr getnetbyname 3
+is then used to interpret the name as that of a network.
+.Pp
+The
+.Nm
+utility uses a routing socket and the new message types
+.Dv RTM_ADD , RTM_DELETE , RTM_GET ,
+and
+.Dv RTM_CHANGE .
+As such, only the super-user may modify
+the routing tables.
+.Sh EXIT STATUS
+.Ex -std
+.Sh DIAGNOSTICS
+.Bl -diag
+.It "add [host \&| network ] %s: gateway %s flags %x"
+The specified route is being added to the tables.
+The
+values printed are from the routing table entry supplied
+in the
+.Xr ioctl 2
+call.
+If the gateway address used was not the primary address of the gateway
+(the first one returned by
+.Xr gethostbyname 3 ) ,
+the gateway address is printed numerically as well as symbolically.
+.It "delete [ host \&| network ] %s: gateway %s flags %x"
+As above, but when deleting an entry.
+.It "%s %s done"
+When the
+.Cm flush
+command is specified, each routing table entry deleted
+is indicated with a message of this form.
+.It "Network is unreachable"
+An attempt to add a route failed because the gateway listed was not
+on a directly-connected network.
+The next-hop gateway must be given.
+.It "not in table"
+A delete operation was attempted for an entry which
+was not present in the tables.
+.It "routing table overflow"
+An add operation was attempted, but the system was
+low on resources and was unable to allocate memory
+to create the new entry.
+.It "gateway uses the same route"
+A
+.Cm change
+operation resulted in a route whose gateway uses the
+same route as the one being changed.
+The next-hop gateway should be reachable through a different route.
+.El
+.Sh SEE ALSO
+.\".Xr esis 4 ,
+.Xr netintro 4 ,
+.Xr route 4 ,
+.Xr arp 8 ,
+.Xr IPXrouted 8 ,
+.Xr routed 8
+.\".Xr XNSrouted 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
+.Sh BUGS
+The first paragraph may have slightly exaggerated
+.Xr routed 8 Ns 's
+abilities.
+.Pp
+Currently, routes with the
+.Dv RTF_BLACKHOLE
+flag set need to have the gateway set to an instance of the
+.Xr lo 4
+driver, using the
+.Fl iface
+option, for the flag to have any effect; unless IP fast forwarding
+is enabled, in which case the meaning of the flag will always
+be honored.
diff --git a/sbin/route/route.c b/sbin/route/route.c
new file mode 100644
index 0000000..2edea13
--- /dev/null
+++ b/sbin/route/route.c
@@ -0,0 +1,1664 @@
+/*
+ * Copyright (c) 1983, 1989, 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1989, 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)route.c 8.6 (Berkeley) 4/28/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netatalk/at.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+
+struct keytab {
+ char *kt_cp;
+ int kt_i;
+} keywords[] = {
+#include "keywords.h"
+ {0, 0}
+};
+
+struct ortentry route;
+union sockunion {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+#ifdef INET6
+ struct sockaddr_in6 sin6;
+#endif
+ struct sockaddr_at sat;
+ struct sockaddr_dl sdl;
+ struct sockaddr_inarp sinarp;
+ struct sockaddr_storage ss; /* added to avoid memory overrun */
+} so_dst, so_gate, so_mask, so_genmask, so_ifa, so_ifp;
+
+typedef union sockunion *sup;
+int pid, rtm_addrs;
+int s;
+int forcehost, forcenet, doflush, nflag, af, qflag, tflag, keyword();
+int iflag, verbose, aflen = sizeof (struct sockaddr_in);
+int locking, lockrest, debugonly;
+struct rt_metrics rt_metrics;
+u_long rtm_inits;
+uid_t uid;
+int atalk_aton(const char *, struct at_addr *);
+char *atalk_ntoa(struct at_addr);
+const char *routename(), *netname();
+void flushroutes(), newroute(), monitor(), sockaddr(), sodump(), bprintf();
+void print_getmsg(), print_rtmsg(), pmsg_common(), pmsg_addrs(), mask_addr();
+#ifdef INET6
+static int inet6_makenetandmask(struct sockaddr_in6 *, char *);
+#endif
+int getaddr(), rtmsg(), x25_makemask();
+int prefixlen();
+extern char *iso_ntoa();
+
+void usage(const char *) __dead2;
+
+void
+usage(cp)
+ const char *cp;
+{
+ if (cp)
+ warnx("bad keyword: %s", cp);
+ (void) fprintf(stderr,
+ "usage: route [-dnqtv] command [[modifiers] args]\n");
+ exit(EX_USAGE);
+ /* NOTREACHED */
+}
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch;
+
+ if (argc < 2)
+ usage((char *)NULL);
+
+ while ((ch = getopt(argc, argv, "nqdtv")) != -1)
+ switch(ch) {
+ case 'n':
+ nflag = 1;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 't':
+ tflag = 1;
+ break;
+ case 'd':
+ debugonly = 1;
+ break;
+ case '?':
+ default:
+ usage((char *)NULL);
+ }
+ argc -= optind;
+ argv += optind;
+
+ pid = getpid();
+ uid = geteuid();
+ if (tflag)
+ s = open(_PATH_DEVNULL, O_WRONLY, 0);
+ else
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0)
+ err(EX_OSERR, "socket");
+ if (*argv)
+ switch (keyword(*argv)) {
+ case K_GET:
+ uid = 0;
+ /* FALLTHROUGH */
+
+ case K_CHANGE:
+ case K_ADD:
+ case K_DEL:
+ case K_DELETE:
+ newroute(argc, argv);
+ /* NOTREACHED */
+
+ case K_MONITOR:
+ monitor();
+ /* NOTREACHED */
+
+ case K_FLUSH:
+ flushroutes(argc, argv);
+ exit(0);
+ /* NOTREACHED */
+ }
+ usage(*argv);
+ /* NOTREACHED */
+}
+
+/*
+ * Purge all entries in the routing tables not
+ * associated with network interfaces.
+ */
+void
+flushroutes(argc, argv)
+ int argc;
+ char *argv[];
+{
+ size_t needed;
+ int mib[6], rlen, seqno, count = 0;
+ char *buf, *next, *lim;
+ struct rt_msghdr *rtm;
+
+ if (uid && !debugonly) {
+ errx(EX_NOPERM, "must be root to alter routing table");
+ }
+ shutdown(s, SHUT_RD); /* Don't want to read back our messages */
+ if (argc > 1) {
+ argv++;
+ if (argc == 2 && **argv == '-')
+ switch (keyword(*argv + 1)) {
+ case K_INET:
+ af = AF_INET;
+ break;
+#ifdef INET6
+ case K_INET6:
+ af = AF_INET6;
+ break;
+#endif
+ case K_ATALK:
+ af = AF_APPLETALK;
+ break;
+ case K_LINK:
+ af = AF_LINK;
+ break;
+ default:
+ goto bad;
+ } else
+bad: usage(*argv);
+ }
+retry:
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0; /* protocol */
+ mib[3] = 0; /* wildcard address family */
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0; /* no flags */
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ err(EX_OSERR, "route-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ errx(EX_OSERR, "malloc failed");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
+ if (errno == ENOMEM && count++ < 10) {
+ warnx("Routing table grew, retrying");
+ sleep(1);
+ free(buf);
+ goto retry;
+ }
+ err(EX_OSERR, "route-sysctl-get");
+ }
+ lim = buf + needed;
+ if (verbose)
+ (void) printf("Examining routing table from sysctl\n");
+ seqno = 0; /* ??? */
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ if (verbose)
+ print_rtmsg(rtm, rtm->rtm_msglen);
+ if ((rtm->rtm_flags & RTF_GATEWAY) == 0)
+ continue;
+ if (af) {
+ struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
+
+ if (sa->sa_family != af)
+ continue;
+ }
+ if (debugonly)
+ continue;
+ rtm->rtm_type = RTM_DELETE;
+ rtm->rtm_seq = seqno;
+ rlen = write(s, next, rtm->rtm_msglen);
+ if (rlen < 0 && errno == EPERM)
+ err(1, "write to routing socket");
+ if (rlen < (int)rtm->rtm_msglen) {
+ warn("write to routing socket");
+ (void) printf("got only %d for rlen\n", rlen);
+ free(buf);
+ goto retry;
+ break;
+ }
+ seqno++;
+ if (qflag)
+ continue;
+ if (verbose)
+ print_rtmsg(rtm, rlen);
+ else {
+ struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
+ (void) printf("%-20.20s ", rtm->rtm_flags & RTF_HOST ?
+ routename(sa) : netname(sa));
+ sa = (struct sockaddr *)(SA_SIZE(sa) + (char *)sa);
+ (void) printf("%-20.20s ", routename(sa));
+ (void) printf("done\n");
+ }
+ }
+}
+
+const char *
+routename(sa)
+ struct sockaddr *sa;
+{
+ char *cp;
+ static char line[MAXHOSTNAMELEN + 1];
+ struct hostent *hp;
+ static char domain[MAXHOSTNAMELEN + 1];
+ static int first = 1, n;
+
+ if (first) {
+ first = 0;
+ if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
+ (cp = strchr(domain, '.'))) {
+ domain[MAXHOSTNAMELEN] = '\0';
+ (void) strcpy(domain, cp + 1);
+ } else
+ domain[0] = 0;
+ }
+
+ if (sa->sa_len == 0)
+ strcpy(line, "default");
+ else switch (sa->sa_family) {
+
+ case AF_INET:
+ { struct in_addr in;
+ in = ((struct sockaddr_in *)sa)->sin_addr;
+
+ cp = 0;
+ if (in.s_addr == INADDR_ANY || sa->sa_len < 4)
+ cp = "default";
+ if (cp == 0 && !nflag) {
+ hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
+ AF_INET);
+ if (hp) {
+ if ((cp = strchr(hp->h_name, '.')) &&
+ !strcmp(cp + 1, domain))
+ *cp = 0;
+ cp = hp->h_name;
+ }
+ }
+ if (cp) {
+ strncpy(line, cp, sizeof(line) - 1);
+ line[sizeof(line) - 1] = '\0';
+ } else
+ (void) sprintf(line, "%s", inet_ntoa(in));
+ break;
+ }
+
+#ifdef INET6
+ case AF_INET6:
+ {
+ struct sockaddr_in6 sin6; /* use static var for safety */
+ int niflags = 0;
+
+ memset(&sin6, 0, sizeof(sin6));
+ memcpy(&sin6, sa, sa->sa_len);
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_family = AF_INET6;
+#ifdef __KAME__
+ if (sa->sa_len == sizeof(struct sockaddr_in6) &&
+ (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) &&
+ sin6.sin6_scope_id == 0) {
+ sin6.sin6_scope_id =
+ ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
+ sin6.sin6_addr.s6_addr[2] = 0;
+ sin6.sin6_addr.s6_addr[3] = 0;
+ }
+#endif
+ if (nflag)
+ niflags |= NI_NUMERICHOST;
+ if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
+ line, sizeof(line), NULL, 0, niflags) != 0)
+ strncpy(line, "invalid", sizeof(line));
+
+ return(line);
+ }
+#endif
+
+ case AF_APPLETALK:
+ (void) snprintf(line, sizeof(line), "atalk %s",
+ atalk_ntoa(((struct sockaddr_at *)sa)->sat_addr));
+ break;
+
+ case AF_LINK:
+ return (link_ntoa((struct sockaddr_dl *)sa));
+
+ default:
+ { u_short *s = (u_short *)sa;
+ u_short *slim = s + ((sa->sa_len + 1) >> 1);
+ char *cp = line + sprintf(line, "(%d)", sa->sa_family);
+ char *cpe = line + sizeof(line);
+
+ while (++s < slim && cp < cpe) /* start with sa->sa_data */
+ if ((n = snprintf(cp, cpe - cp, " %x", *s)) > 0)
+ cp += n;
+ else
+ *cp = '\0';
+ break;
+ }
+ }
+ return (line);
+}
+
+/*
+ * Return the name of the network whose address is given.
+ * The address is assumed to be that of a net or subnet, not a host.
+ */
+const char *
+netname(sa)
+ struct sockaddr *sa;
+{
+ char *cp = 0;
+ static char line[MAXHOSTNAMELEN + 1];
+ struct netent *np = 0;
+ u_long net, mask;
+ u_long i;
+ int n, subnetshift;
+
+ switch (sa->sa_family) {
+
+ case AF_INET:
+ { struct in_addr in;
+ in = ((struct sockaddr_in *)sa)->sin_addr;
+
+ i = in.s_addr = ntohl(in.s_addr);
+ if (in.s_addr == 0)
+ cp = "default";
+ else if (!nflag) {
+ if (IN_CLASSA(i)) {
+ mask = IN_CLASSA_NET;
+ subnetshift = 8;
+ } else if (IN_CLASSB(i)) {
+ mask = IN_CLASSB_NET;
+ subnetshift = 8;
+ } else {
+ mask = IN_CLASSC_NET;
+ subnetshift = 4;
+ }
+ /*
+ * If there are more bits than the standard mask
+ * would suggest, subnets must be in use.
+ * Guess at the subnet mask, assuming reasonable
+ * width subnet fields.
+ */
+ while (in.s_addr &~ mask)
+ mask = (long)mask >> subnetshift;
+ net = in.s_addr & mask;
+ while ((mask & 1) == 0)
+ mask >>= 1, net >>= 1;
+ np = getnetbyaddr(net, AF_INET);
+ if (np)
+ cp = np->n_name;
+ }
+#define C(x) (unsigned)((x) & 0xff)
+ if (cp)
+ strncpy(line, cp, sizeof(line));
+ else if ((in.s_addr & 0xffffff) == 0)
+ (void) sprintf(line, "%u", C(in.s_addr >> 24));
+ else if ((in.s_addr & 0xffff) == 0)
+ (void) sprintf(line, "%u.%u", C(in.s_addr >> 24),
+ C(in.s_addr >> 16));
+ else if ((in.s_addr & 0xff) == 0)
+ (void) sprintf(line, "%u.%u.%u", C(in.s_addr >> 24),
+ C(in.s_addr >> 16), C(in.s_addr >> 8));
+ else
+ (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
+ C(in.s_addr >> 16), C(in.s_addr >> 8),
+ C(in.s_addr));
+#undef C
+ break;
+ }
+
+#ifdef INET6
+ case AF_INET6:
+ {
+ struct sockaddr_in6 sin6; /* use static var for safety */
+ int niflags = 0;
+
+ memset(&sin6, 0, sizeof(sin6));
+ memcpy(&sin6, sa, sa->sa_len);
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_family = AF_INET6;
+#ifdef __KAME__
+ if (sa->sa_len == sizeof(struct sockaddr_in6) &&
+ (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) &&
+ sin6.sin6_scope_id == 0) {
+ sin6.sin6_scope_id =
+ ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
+ sin6.sin6_addr.s6_addr[2] = 0;
+ sin6.sin6_addr.s6_addr[3] = 0;
+ }
+#endif
+ if (nflag)
+ niflags |= NI_NUMERICHOST;
+ if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
+ line, sizeof(line), NULL, 0, niflags) != 0)
+ strncpy(line, "invalid", sizeof(line));
+
+ return(line);
+ }
+#endif
+
+ case AF_APPLETALK:
+ (void) snprintf(line, sizeof(line), "atalk %s",
+ atalk_ntoa(((struct sockaddr_at *)sa)->sat_addr));
+ break;
+
+ case AF_LINK:
+ return (link_ntoa((struct sockaddr_dl *)sa));
+
+
+ default:
+ { u_short *s = (u_short *)sa->sa_data;
+ u_short *slim = s + ((sa->sa_len + 1)>>1);
+ char *cp = line + sprintf(line, "af %d:", sa->sa_family);
+ char *cpe = line + sizeof(line);
+
+ while (s < slim && cp < cpe)
+ if ((n = snprintf(cp, cpe - cp, " %x", *s++)) > 0)
+ cp += n;
+ else
+ *cp = '\0';
+ break;
+ }
+ }
+ return (line);
+}
+
+void
+set_metric(value, key)
+ char *value;
+ int key;
+{
+ int flag = 0;
+ u_long noval, *valp = &noval;
+
+ switch (key) {
+#define caseof(x, y, z) case x: valp = &rt_metrics.z; flag = y; break
+ caseof(K_MTU, RTV_MTU, rmx_mtu);
+ caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount);
+ caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire);
+ caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe);
+ caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe);
+ caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh);
+ caseof(K_RTT, RTV_RTT, rmx_rtt);
+ caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar);
+ }
+ rtm_inits |= flag;
+ if (lockrest || locking)
+ rt_metrics.rmx_locks |= flag;
+ if (locking)
+ locking = 0;
+ *valp = atoi(value);
+}
+
+void
+newroute(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *cmd, *dest = "", *gateway = "", *err;
+ int ishost = 0, proxy = 0, ret, attempts, oerrno, flags = RTF_STATIC;
+ int key;
+ struct hostent *hp = 0;
+
+ if (uid) {
+ errx(EX_NOPERM, "must be root to alter routing table");
+ }
+ cmd = argv[0];
+ if (*cmd != 'g')
+ shutdown(s, SHUT_RD); /* Don't want to read back our messages */
+ while (--argc > 0) {
+ if (**(++argv)== '-') {
+ switch (key = keyword(1 + *argv)) {
+ case K_LINK:
+ af = AF_LINK;
+ aflen = sizeof(struct sockaddr_dl);
+ break;
+ case K_INET:
+ af = AF_INET;
+ aflen = sizeof(struct sockaddr_in);
+ break;
+#ifdef INET6
+ case K_INET6:
+ af = AF_INET6;
+ aflen = sizeof(struct sockaddr_in6);
+ break;
+#endif
+ case K_ATALK:
+ af = AF_APPLETALK;
+ aflen = sizeof(struct sockaddr_at);
+ break;
+ case K_SA:
+ af = PF_ROUTE;
+ aflen = sizeof(union sockunion);
+ break;
+ case K_IFACE:
+ case K_INTERFACE:
+ iflag++;
+ break;
+ case K_NOSTATIC:
+ flags &= ~RTF_STATIC;
+ break;
+ case K_LLINFO:
+ flags |= RTF_LLINFO;
+ break;
+ case K_LOCK:
+ locking = 1;
+ break;
+ case K_LOCKREST:
+ lockrest = 1;
+ break;
+ case K_HOST:
+ forcehost++;
+ break;
+ case K_REJECT:
+ flags |= RTF_REJECT;
+ break;
+ case K_BLACKHOLE:
+ flags |= RTF_BLACKHOLE;
+ break;
+ case K_PROTO1:
+ flags |= RTF_PROTO1;
+ break;
+ case K_PROTO2:
+ flags |= RTF_PROTO2;
+ break;
+ case K_PROXY:
+ proxy = 1;
+ break;
+ case K_CLONING:
+ flags |= RTF_CLONING;
+ break;
+ case K_XRESOLVE:
+ flags |= RTF_XRESOLVE;
+ break;
+ case K_STATIC:
+ flags |= RTF_STATIC;
+ break;
+ case K_IFA:
+ if (!--argc)
+ usage((char *)NULL);
+ (void) getaddr(RTA_IFA, *++argv, 0);
+ break;
+ case K_IFP:
+ if (!--argc)
+ usage((char *)NULL);
+ (void) getaddr(RTA_IFP, *++argv, 0);
+ break;
+ case K_GENMASK:
+ if (!--argc)
+ usage((char *)NULL);
+ (void) getaddr(RTA_GENMASK, *++argv, 0);
+ break;
+ case K_GATEWAY:
+ if (!--argc)
+ usage((char *)NULL);
+ (void) getaddr(RTA_GATEWAY, *++argv, 0);
+ break;
+ case K_DST:
+ if (!--argc)
+ usage((char *)NULL);
+ ishost = getaddr(RTA_DST, *++argv, &hp);
+ dest = *argv;
+ break;
+ case K_NETMASK:
+ if (!--argc)
+ usage((char *)NULL);
+ (void) getaddr(RTA_NETMASK, *++argv, 0);
+ /* FALLTHROUGH */
+ case K_NET:
+ forcenet++;
+ break;
+ case K_PREFIXLEN:
+ if (!--argc)
+ usage((char *)NULL);
+ if (prefixlen(*++argv) == -1) {
+ forcenet = 0;
+ ishost = 1;
+ } else {
+ forcenet = 1;
+ ishost = 0;
+ }
+ break;
+ case K_MTU:
+ case K_HOPCOUNT:
+ case K_EXPIRE:
+ case K_RECVPIPE:
+ case K_SENDPIPE:
+ case K_SSTHRESH:
+ case K_RTT:
+ case K_RTTVAR:
+ if (!--argc)
+ usage((char *)NULL);
+ set_metric(*++argv, key);
+ break;
+ default:
+ usage(1+*argv);
+ }
+ } else {
+ if ((rtm_addrs & RTA_DST) == 0) {
+ dest = *argv;
+ ishost = getaddr(RTA_DST, *argv, &hp);
+ } else if ((rtm_addrs & RTA_GATEWAY) == 0) {
+ gateway = *argv;
+ (void) getaddr(RTA_GATEWAY, *argv, &hp);
+ } else {
+ (void) getaddr(RTA_NETMASK, *argv, 0);
+ forcenet = 1;
+ }
+ }
+ }
+ if (forcehost) {
+ ishost = 1;
+#ifdef INET6
+ if (af == AF_INET6) {
+ rtm_addrs &= ~RTA_NETMASK;
+ memset((void *)&so_mask, 0, sizeof(so_mask));
+ }
+#endif
+ }
+ if (forcenet)
+ ishost = 0;
+ flags |= RTF_UP;
+ if (ishost)
+ flags |= RTF_HOST;
+ if (iflag == 0)
+ flags |= RTF_GATEWAY;
+ if (proxy) {
+ so_dst.sinarp.sin_other = SIN_PROXY;
+ flags |= RTF_ANNOUNCE;
+ }
+ for (attempts = 1; ; attempts++) {
+ errno = 0;
+ if ((ret = rtmsg(*cmd, flags)) == 0)
+ break;
+ if (errno != ENETUNREACH && errno != ESRCH)
+ break;
+ if (af == AF_INET && *gateway && hp && hp->h_addr_list[1]) {
+ hp->h_addr_list++;
+ memmove(&so_gate.sin.sin_addr, hp->h_addr_list[0],
+ MIN(hp->h_length, sizeof(so_gate.sin.sin_addr)));
+ } else
+ break;
+ }
+ if (*cmd == 'g')
+ exit(0);
+ if (!qflag) {
+ oerrno = errno;
+ (void) printf("%s %s %s", cmd, ishost? "host" : "net", dest);
+ if (*gateway) {
+ (void) printf(": gateway %s", gateway);
+ if (attempts > 1 && ret == 0 && af == AF_INET)
+ (void) printf(" (%s)",
+ inet_ntoa(((struct sockaddr_in *)&route.rt_gateway)->sin_addr));
+ }
+ if (ret == 0) {
+ (void) printf("\n");
+ } else {
+ switch (oerrno) {
+ case ESRCH:
+ err = "not in table";
+ break;
+ case EBUSY:
+ err = "entry in use";
+ break;
+ case ENOBUFS:
+ err = "not enough memory";
+ break;
+ case EADDRINUSE:
+ /* handle recursion avoidance in rt_setgate() */
+ err = "gateway uses the same route";
+ break;
+ case EEXIST:
+ err = "route already in table";
+ break;
+ default:
+ err = strerror(oerrno);
+ break;
+ }
+ (void) printf(": %s\n", err);
+ }
+ }
+ exit(ret != 0);
+}
+
+void
+inet_makenetandmask(net, sin, bits)
+ u_long net, bits;
+ struct sockaddr_in *sin;
+{
+ u_long addr, mask = 0;
+ char *cp;
+
+ rtm_addrs |= RTA_NETMASK;
+ if (net == 0)
+ mask = addr = 0;
+ else if (net < 128) {
+ addr = net << IN_CLASSA_NSHIFT;
+ mask = IN_CLASSA_NET;
+ } else if (net < 65536) {
+ addr = net << IN_CLASSB_NSHIFT;
+ mask = IN_CLASSB_NET;
+ } else if (net < 16777216L) {
+ addr = net << IN_CLASSC_NSHIFT;
+ mask = IN_CLASSC_NET;
+ } else {
+ addr = net;
+ if ((addr & IN_CLASSA_HOST) == 0)
+ mask = IN_CLASSA_NET;
+ else if ((addr & IN_CLASSB_HOST) == 0)
+ mask = IN_CLASSB_NET;
+ else if ((addr & IN_CLASSC_HOST) == 0)
+ mask = IN_CLASSC_NET;
+ else
+ mask = -1;
+ }
+ if (bits)
+ mask = 0xffffffff << (32 - bits);
+ sin->sin_addr.s_addr = htonl(addr);
+ sin = &so_mask.sin;
+ sin->sin_addr.s_addr = htonl(mask);
+ sin->sin_len = 0;
+ sin->sin_family = 0;
+ cp = (char *)(&sin->sin_addr + 1);
+ while (*--cp == 0 && cp > (char *)sin)
+ ;
+ sin->sin_len = 1 + cp - (char *)sin;
+}
+
+#ifdef INET6
+/*
+ * XXX the function may need more improvement...
+ */
+static int
+inet6_makenetandmask(sin6, plen)
+ struct sockaddr_in6 *sin6;
+ char *plen;
+{
+ struct in6_addr in6;
+
+ if (!plen) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) &&
+ sin6->sin6_scope_id == 0) {
+ plen = "0";
+ } else if ((sin6->sin6_addr.s6_addr[0] & 0xe0) == 0x20) {
+ /* aggregatable global unicast - RFC2374 */
+ memset(&in6, 0, sizeof(in6));
+ if (!memcmp(&sin6->sin6_addr.s6_addr[8],
+ &in6.s6_addr[8], 8))
+ plen = "64";
+ }
+ }
+
+ if (!plen || strcmp(plen, "128") == 0)
+ return 1;
+ rtm_addrs |= RTA_NETMASK;
+ (void)prefixlen(plen);
+ return 0;
+}
+#endif
+
+/*
+ * Interpret an argument as a network address of some kind,
+ * returning 1 if a host address, 0 if a network address.
+ */
+int
+getaddr(which, s, hpp)
+ int which;
+ char *s;
+ struct hostent **hpp;
+{
+ sup su;
+ struct hostent *hp;
+ struct netent *np;
+ u_long val;
+ char *q;
+ int afamily; /* local copy of af so we can change it */
+
+ if (af == 0) {
+ af = AF_INET;
+ aflen = sizeof(struct sockaddr_in);
+ }
+ afamily = af;
+ rtm_addrs |= which;
+ switch (which) {
+ case RTA_DST:
+ su = &so_dst;
+ break;
+ case RTA_GATEWAY:
+ su = &so_gate;
+ if (iflag) {
+ struct ifaddrs *ifap, *ifa;
+ struct sockaddr_dl *sdl = NULL;
+
+ if (getifaddrs(&ifap))
+ err(1, "getifaddrs");
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+
+ if (strcmp(s, ifa->ifa_name))
+ continue;
+
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ }
+ /* If we found it, then use it */
+ if (sdl) {
+ /*
+ * Copy is safe since we have a
+ * sockaddr_storage member in sockunion{}.
+ * Note that we need to copy before calling
+ * freeifaddrs().
+ */
+ memcpy(&su->sdl, sdl, sdl->sdl_len);
+ }
+ freeifaddrs(ifap);
+ if (sdl)
+ return(1);
+ }
+ break;
+ case RTA_NETMASK:
+ su = &so_mask;
+ break;
+ case RTA_GENMASK:
+ su = &so_genmask;
+ break;
+ case RTA_IFP:
+ su = &so_ifp;
+ afamily = AF_LINK;
+ break;
+ case RTA_IFA:
+ su = &so_ifa;
+ break;
+ default:
+ usage("internal error");
+ /*NOTREACHED*/
+ }
+ su->sa.sa_len = aflen;
+ su->sa.sa_family = afamily; /* cases that don't want it have left already */
+ if (strcmp(s, "default") == 0) {
+ /*
+ * Default is net 0.0.0.0/0
+ */
+ switch (which) {
+ case RTA_DST:
+ forcenet++;
+#if 0
+ bzero(su, sizeof(*su)); /* for readability */
+#endif
+ (void) getaddr(RTA_NETMASK, s, 0);
+ break;
+#if 0
+ case RTA_NETMASK:
+ case RTA_GENMASK:
+ bzero(su, sizeof(*su)); /* for readability */
+#endif
+ }
+ return (0);
+ }
+ switch (afamily) {
+#ifdef INET6
+ case AF_INET6:
+ {
+ struct addrinfo hints, *res;
+ int ecode;
+
+ q = NULL;
+ if (which == RTA_DST && (q = strchr(s, '/')) != NULL)
+ *q = '\0';
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = afamily; /*AF_INET6*/
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ ecode = getaddrinfo(s, NULL, &hints, &res);
+ if (ecode != 0 || res->ai_family != AF_INET6 ||
+ res->ai_addrlen != sizeof(su->sin6)) {
+ (void) fprintf(stderr, "%s: %s\n", s,
+ gai_strerror(ecode));
+ exit(1);
+ }
+ memcpy(&su->sin6, res->ai_addr, sizeof(su->sin6));
+#ifdef __KAME__
+ if ((IN6_IS_ADDR_LINKLOCAL(&su->sin6.sin6_addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&su->sin6.sin6_addr)) &&
+ su->sin6.sin6_scope_id) {
+ *(u_int16_t *)&su->sin6.sin6_addr.s6_addr[2] =
+ htons(su->sin6.sin6_scope_id);
+ su->sin6.sin6_scope_id = 0;
+ }
+#endif
+ freeaddrinfo(res);
+ if (q != NULL)
+ *q++ = '/';
+ if (which == RTA_DST)
+ return (inet6_makenetandmask(&su->sin6, q));
+ return (0);
+ }
+#endif /* INET6 */
+
+ case AF_APPLETALK:
+ if (!atalk_aton(s, &su->sat.sat_addr))
+ errx(EX_NOHOST, "bad address: %s", s);
+ rtm_addrs |= RTA_NETMASK;
+ return(forcehost || su->sat.sat_addr.s_node != 0);
+
+ case AF_LINK:
+ link_addr(s, &su->sdl);
+ return (1);
+
+
+ case PF_ROUTE:
+ su->sa.sa_len = sizeof(*su);
+ sockaddr(s, &su->sa);
+ return (1);
+
+ case AF_INET:
+ default:
+ break;
+ }
+
+ if (hpp == NULL)
+ hpp = &hp;
+ *hpp = NULL;
+
+ q = strchr(s,'/');
+ if (q && which == RTA_DST) {
+ *q = '\0';
+ if ((val = inet_network(s)) != INADDR_NONE) {
+ inet_makenetandmask(
+ val, &su->sin, strtoul(q+1, 0, 0));
+ return (0);
+ }
+ *q = '/';
+ }
+ if ((which != RTA_DST || forcenet == 0) &&
+ inet_aton(s, &su->sin.sin_addr)) {
+ val = su->sin.sin_addr.s_addr;
+ if (which != RTA_DST || forcehost ||
+ inet_lnaof(su->sin.sin_addr) != INADDR_ANY)
+ return (1);
+ else {
+ val = ntohl(val);
+ goto netdone;
+ }
+ }
+ if (which == RTA_DST && forcehost == 0 &&
+ ((val = inet_network(s)) != INADDR_NONE ||
+ ((np = getnetbyname(s)) != NULL && (val = np->n_net) != 0))) {
+netdone:
+ inet_makenetandmask(val, &su->sin, 0);
+ return (0);
+ }
+ hp = gethostbyname(s);
+ if (hp) {
+ *hpp = hp;
+ su->sin.sin_family = hp->h_addrtype;
+ memmove((char *)&su->sin.sin_addr, hp->h_addr,
+ MIN(hp->h_length, sizeof(su->sin.sin_addr)));
+ return (1);
+ }
+ errx(EX_NOHOST, "bad address: %s", s);
+}
+
+int
+prefixlen(s)
+ char *s;
+{
+ int len = atoi(s), q, r;
+ int max;
+ char *p;
+
+ rtm_addrs |= RTA_NETMASK;
+ switch (af) {
+#ifdef INET6
+ case AF_INET6:
+ max = 128;
+ p = (char *)&so_mask.sin6.sin6_addr;
+ break;
+#endif
+ case AF_INET:
+ max = 32;
+ p = (char *)&so_mask.sin.sin_addr;
+ break;
+ default:
+ (void) fprintf(stderr, "prefixlen not supported in this af\n");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+ if (len < 0 || max < len) {
+ (void) fprintf(stderr, "%s: bad value\n", s);
+ exit(1);
+ }
+
+ q = len >> 3;
+ r = len & 7;
+ so_mask.sa.sa_family = af;
+ so_mask.sa.sa_len = aflen;
+ memset((void *)p, 0, max / 8);
+ if (q > 0)
+ memset((void *)p, 0xff, q);
+ if (r > 0)
+ *((u_char *)p + q) = (0xff00 >> r) & 0xff;
+ if (len == max)
+ return -1;
+ else
+ return len;
+}
+
+void
+interfaces()
+{
+ size_t needed;
+ int mib[6];
+ char *buf, *lim, *next, count = 0;
+ struct rt_msghdr *rtm;
+
+retry2:
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0; /* protocol */
+ mib[3] = 0; /* wildcard address family */
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0; /* no flags */
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ err(EX_OSERR, "route-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ errx(EX_OSERR, "malloc failed");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
+ if (errno == ENOMEM && count++ < 10) {
+ warnx("Routing table grew, retrying");
+ sleep(1);
+ free(buf);
+ goto retry2;
+ }
+ err(EX_OSERR, "actual retrieval of interface table");
+ }
+ lim = buf + needed;
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ print_rtmsg(rtm, rtm->rtm_msglen);
+ }
+}
+
+void
+monitor()
+{
+ int n;
+ char msg[2048];
+
+ verbose = 1;
+ if (debugonly) {
+ interfaces();
+ exit(0);
+ }
+ for(;;) {
+ time_t now;
+ n = read(s, msg, 2048);
+ now = time(NULL);
+ (void) printf("\ngot message of size %d on %s", n, ctime(&now));
+ print_rtmsg((struct rt_msghdr *)msg, n);
+ }
+}
+
+struct {
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+} m_rtmsg;
+
+int
+rtmsg(cmd, flags)
+ int cmd, flags;
+{
+ static int seq;
+ int rlen;
+ char *cp = m_rtmsg.m_space;
+ int l;
+
+#define NEXTADDR(w, u) \
+ if (rtm_addrs & (w)) {\
+ l = SA_SIZE(&(u.sa)); memmove(cp, &(u), l); cp += l;\
+ if (verbose) sodump(&(u),"u");\
+ }
+
+ errno = 0;
+ memset(&m_rtmsg, 0, sizeof(m_rtmsg));
+ if (cmd == 'a')
+ cmd = RTM_ADD;
+ else if (cmd == 'c')
+ cmd = RTM_CHANGE;
+ else if (cmd == 'g') {
+ cmd = RTM_GET;
+ if (so_ifp.sa.sa_family == 0) {
+ so_ifp.sa.sa_family = AF_LINK;
+ so_ifp.sa.sa_len = sizeof(struct sockaddr_dl);
+ rtm_addrs |= RTA_IFP;
+ }
+ } else
+ cmd = RTM_DELETE;
+#define rtm m_rtmsg.m_rtm
+ rtm.rtm_type = cmd;
+ rtm.rtm_flags = flags;
+ rtm.rtm_version = RTM_VERSION;
+ rtm.rtm_seq = ++seq;
+ rtm.rtm_addrs = rtm_addrs;
+ rtm.rtm_rmx = rt_metrics;
+ rtm.rtm_inits = rtm_inits;
+
+ if (rtm_addrs & RTA_NETMASK)
+ mask_addr();
+ NEXTADDR(RTA_DST, so_dst);
+ NEXTADDR(RTA_GATEWAY, so_gate);
+ NEXTADDR(RTA_NETMASK, so_mask);
+ NEXTADDR(RTA_GENMASK, so_genmask);
+ NEXTADDR(RTA_IFP, so_ifp);
+ NEXTADDR(RTA_IFA, so_ifa);
+ rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
+ if (verbose)
+ print_rtmsg(&rtm, l);
+ if (debugonly)
+ return (0);
+ if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
+ if (errno == EPERM)
+ err(1, "writing to routing socket");
+ warn("writing to routing socket");
+ return (-1);
+ }
+ if (cmd == RTM_GET) {
+ do {
+ l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
+ } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
+ if (l < 0)
+ warn("read from routing socket");
+ else
+ print_getmsg(&rtm, l);
+ }
+#undef rtm
+ return (0);
+}
+
+void
+mask_addr()
+{
+ int olen = so_mask.sa.sa_len;
+ char *cp1 = olen + (char *)&so_mask, *cp2;
+
+ for (so_mask.sa.sa_len = 0; cp1 > (char *)&so_mask; )
+ if (*--cp1 != 0) {
+ so_mask.sa.sa_len = 1 + cp1 - (char *)&so_mask;
+ break;
+ }
+ if ((rtm_addrs & RTA_DST) == 0)
+ return;
+ switch (so_dst.sa.sa_family) {
+ case AF_INET:
+#ifdef INET6
+ case AF_INET6:
+#endif
+ case AF_APPLETALK:
+ case 0:
+ return;
+ }
+ cp1 = so_mask.sa.sa_len + 1 + (char *)&so_dst;
+ cp2 = so_dst.sa.sa_len + 1 + (char *)&so_dst;
+ while (cp2 > cp1)
+ *--cp2 = 0;
+ cp2 = so_mask.sa.sa_len + 1 + (char *)&so_mask;
+ while (cp1 > so_dst.sa.sa_data)
+ *--cp1 &= *--cp2;
+}
+
+char *msgtypes[] = {
+ "",
+ "RTM_ADD: Add Route",
+ "RTM_DELETE: Delete Route",
+ "RTM_CHANGE: Change Metrics or flags",
+ "RTM_GET: Report Metrics",
+ "RTM_LOSING: Kernel Suspects Partitioning",
+ "RTM_REDIRECT: Told to use different route",
+ "RTM_MISS: Lookup failed on this address",
+ "RTM_LOCK: fix specified metrics",
+ "RTM_OLDADD: caused by SIOCADDRT",
+ "RTM_OLDDEL: caused by SIOCDELRT",
+ "RTM_RESOLVE: Route created by cloning",
+ "RTM_NEWADDR: address being added to iface",
+ "RTM_DELADDR: address being removed from iface",
+ "RTM_IFINFO: iface status change",
+ "RTM_NEWMADDR: new multicast group membership on iface",
+ "RTM_DELMADDR: multicast group membership removed from iface",
+ "RTM_IFANNOUNCE: interface arrival/departure",
+ 0,
+};
+
+char metricnames[] =
+"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
+"\1mtu";
+char routeflags[] =
+"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
+"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE\016b016"
+"\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3\024CHAINDELETE"
+"\025PINNED\026LOCAL\027BROADCAST\030MULTICAST";
+char ifnetflags[] =
+"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP"
+"\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1"
+"\017LINK2\020MULTICAST";
+char addrnames[] =
+"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD";
+
+void
+print_rtmsg(rtm, msglen)
+ struct rt_msghdr *rtm;
+ int msglen;
+{
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+#ifdef RTM_NEWMADDR
+ struct ifma_msghdr *ifmam;
+#endif
+ struct if_announcemsghdr *ifan;
+ char *state;
+
+ if (verbose == 0)
+ return;
+ if (rtm->rtm_version != RTM_VERSION) {
+ (void) printf("routing message version %d not understood\n",
+ rtm->rtm_version);
+ return;
+ }
+ if (msgtypes[rtm->rtm_type] != NULL)
+ (void)printf("%s: ", msgtypes[rtm->rtm_type]);
+ else
+ (void)printf("#%d: ", rtm->rtm_type);
+ (void)printf("len %d, ", rtm->rtm_msglen);
+ switch (rtm->rtm_type) {
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *)rtm;
+ (void) printf("if# %d, ", ifm->ifm_index);
+ switch (ifm->ifm_data.ifi_link_state) {
+ case LINK_STATE_DOWN:
+ state = "down";
+ break;
+ case LINK_STATE_UP:
+ state = "up";
+ break;
+ default:
+ state = "unknown";
+ break;
+ }
+ (void) printf("link: %s, flags:", state);
+ bprintf(stdout, ifm->ifm_flags, ifnetflags);
+ pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs);
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+ (void) printf("metric %d, flags:", ifam->ifam_metric);
+ bprintf(stdout, ifam->ifam_flags, routeflags);
+ pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs);
+ break;
+#ifdef RTM_NEWMADDR
+ case RTM_NEWMADDR:
+ case RTM_DELMADDR:
+ ifmam = (struct ifma_msghdr *)rtm;
+ pmsg_addrs((char *)(ifmam + 1), ifmam->ifmam_addrs);
+ break;
+#endif
+ case RTM_IFANNOUNCE:
+ ifan = (struct if_announcemsghdr *)rtm;
+ (void) printf("if# %d, what: ", ifan->ifan_index);
+ switch (ifan->ifan_what) {
+ case IFAN_ARRIVAL:
+ printf("arrival");
+ break;
+ case IFAN_DEPARTURE:
+ printf("departure");
+ break;
+ default:
+ printf("#%d", ifan->ifan_what);
+ break;
+ }
+ printf("\n");
+ break;
+
+ default:
+ (void) printf("pid: %ld, seq %d, errno %d, flags:",
+ (long)rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno);
+ bprintf(stdout, rtm->rtm_flags, routeflags);
+ pmsg_common(rtm);
+ }
+}
+
+void
+print_getmsg(rtm, msglen)
+ struct rt_msghdr *rtm;
+ int msglen;
+{
+ struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL;
+ struct sockaddr_dl *ifp = NULL;
+ struct sockaddr *sa;
+ char *cp;
+ int i;
+
+ (void) printf(" route to: %s\n", routename(&so_dst));
+ if (rtm->rtm_version != RTM_VERSION) {
+ warnx("routing message version %d not understood",
+ rtm->rtm_version);
+ return;
+ }
+ if (rtm->rtm_msglen > msglen) {
+ warnx("message length mismatch, in packet %d, returned %d",
+ rtm->rtm_msglen, msglen);
+ }
+ if (rtm->rtm_errno) {
+ errno = rtm->rtm_errno;
+ warn("message indicates error %d", errno);
+ return;
+ }
+ cp = ((char *)(rtm + 1));
+ if (rtm->rtm_addrs)
+ for (i = 1; i; i <<= 1)
+ if (i & rtm->rtm_addrs) {
+ sa = (struct sockaddr *)cp;
+ switch (i) {
+ case RTA_DST:
+ dst = sa;
+ break;
+ case RTA_GATEWAY:
+ gate = sa;
+ break;
+ case RTA_NETMASK:
+ mask = sa;
+ break;
+ case RTA_IFP:
+ if (sa->sa_family == AF_LINK &&
+ ((struct sockaddr_dl *)sa)->sdl_nlen)
+ ifp = (struct sockaddr_dl *)sa;
+ break;
+ }
+ cp += SA_SIZE(sa);
+ }
+ if (dst && mask)
+ mask->sa_family = dst->sa_family; /* XXX */
+ if (dst)
+ (void)printf("destination: %s\n", routename(dst));
+ if (mask) {
+ int savenflag = nflag;
+
+ nflag = 1;
+ (void)printf(" mask: %s\n", routename(mask));
+ nflag = savenflag;
+ }
+ if (gate && rtm->rtm_flags & RTF_GATEWAY)
+ (void)printf(" gateway: %s\n", routename(gate));
+ if (ifp)
+ (void)printf(" interface: %.*s\n",
+ ifp->sdl_nlen, ifp->sdl_data);
+ (void)printf(" flags: ");
+ bprintf(stdout, rtm->rtm_flags, routeflags);
+
+#define lock(f) ((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ')
+#define msec(u) (((u) + 500) / 1000) /* usec to msec */
+
+ (void) printf("\n%s\n", "\
+ recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire");
+ printf("%8ld%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE));
+ printf("%8ld%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE));
+ printf("%8ld%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH));
+ printf("%8ld%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT));
+ printf("%8ld%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR));
+ printf("%8ld%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT));
+ printf("%8ld%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU));
+ if (rtm->rtm_rmx.rmx_expire)
+ rtm->rtm_rmx.rmx_expire -= time(0);
+ printf("%8ld%c\n", rtm->rtm_rmx.rmx_expire, lock(EXPIRE));
+#undef lock
+#undef msec
+#define RTA_IGN (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD)
+ if (verbose)
+ pmsg_common(rtm);
+ else if (rtm->rtm_addrs &~ RTA_IGN) {
+ (void) printf("sockaddrs: ");
+ bprintf(stdout, rtm->rtm_addrs, addrnames);
+ putchar('\n');
+ }
+#undef RTA_IGN
+}
+
+void
+pmsg_common(rtm)
+ struct rt_msghdr *rtm;
+{
+ (void) printf("\nlocks: ");
+ bprintf(stdout, rtm->rtm_rmx.rmx_locks, metricnames);
+ (void) printf(" inits: ");
+ bprintf(stdout, rtm->rtm_inits, metricnames);
+ pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs);
+}
+
+void
+pmsg_addrs(cp, addrs)
+ char *cp;
+ int addrs;
+{
+ struct sockaddr *sa;
+ int i;
+
+ if (addrs == 0) {
+ (void) putchar('\n');
+ return;
+ }
+ (void) printf("\nsockaddrs: ");
+ bprintf(stdout, addrs, addrnames);
+ (void) putchar('\n');
+ for (i = 1; i; i <<= 1)
+ if (i & addrs) {
+ sa = (struct sockaddr *)cp;
+ (void) printf(" %s", routename(sa));
+ cp += SA_SIZE(sa);
+ }
+ (void) putchar('\n');
+ (void) fflush(stdout);
+}
+
+void
+bprintf(fp, b, s)
+ FILE *fp;
+ int b;
+ u_char *s;
+{
+ int i;
+ int gotsome = 0;
+
+ if (b == 0)
+ return;
+ while ((i = *s++) != 0) {
+ if (b & (1 << (i-1))) {
+ if (gotsome == 0)
+ i = '<';
+ else
+ i = ',';
+ (void) putc(i, fp);
+ gotsome = 1;
+ for (; (i = *s) > 32; s++)
+ (void) putc(i, fp);
+ } else
+ while (*s > 32)
+ s++;
+ }
+ if (gotsome)
+ (void) putc('>', fp);
+}
+
+int
+keyword(cp)
+ char *cp;
+{
+ struct keytab *kt = keywords;
+
+ while (kt->kt_cp && strcmp(kt->kt_cp, cp))
+ kt++;
+ return kt->kt_i;
+}
+
+void
+sodump(su, which)
+ sup su;
+ char *which;
+{
+ switch (su->sa.sa_family) {
+ case AF_LINK:
+ (void) printf("%s: link %s; ",
+ which, link_ntoa(&su->sdl));
+ break;
+ case AF_INET:
+ (void) printf("%s: inet %s; ",
+ which, inet_ntoa(su->sin.sin_addr));
+ break;
+ case AF_APPLETALK:
+ (void) printf("%s: atalk %s; ",
+ which, atalk_ntoa(su->sat.sat_addr));
+ break;
+ }
+ (void) fflush(stdout);
+}
+
+/* States*/
+#define VIRGIN 0
+#define GOTONE 1
+#define GOTTWO 2
+/* Inputs */
+#define DIGIT (4*0)
+#define END (4*1)
+#define DELIM (4*2)
+
+void
+sockaddr(addr, sa)
+ char *addr;
+ struct sockaddr *sa;
+{
+ char *cp = (char *)sa;
+ int size = sa->sa_len;
+ char *cplim = cp + size;
+ int byte = 0, state = VIRGIN, new = 0 /* foil gcc */;
+
+ memset(cp, 0, size);
+ cp++;
+ do {
+ if ((*addr >= '0') && (*addr <= '9')) {
+ new = *addr - '0';
+ } else if ((*addr >= 'a') && (*addr <= 'f')) {
+ new = *addr - 'a' + 10;
+ } else if ((*addr >= 'A') && (*addr <= 'F')) {
+ new = *addr - 'A' + 10;
+ } else if (*addr == 0)
+ state |= END;
+ else
+ state |= DELIM;
+ addr++;
+ switch (state /* | INPUT */) {
+ case GOTTWO | DIGIT:
+ *cp++ = byte; /*FALLTHROUGH*/
+ case VIRGIN | DIGIT:
+ state = GOTONE; byte = new; continue;
+ case GOTONE | DIGIT:
+ state = GOTTWO; byte = new + (byte << 4); continue;
+ default: /* | DELIM */
+ state = VIRGIN; *cp++ = byte; byte = 0; continue;
+ case GOTONE | END:
+ case GOTTWO | END:
+ *cp++ = byte; /* FALLTHROUGH */
+ case VIRGIN | END:
+ break;
+ }
+ break;
+ } while (cp < cplim);
+ sa->sa_len = cp - (char *)sa;
+}
+
+int
+atalk_aton(const char *text, struct at_addr *addr)
+{
+ u_int net, node;
+
+ if (sscanf(text, "%u.%u", &net, &node) != 2
+ || net > 0xffff || node > 0xff)
+ return(0);
+ addr->s_net = htons(net);
+ addr->s_node = node;
+ return(1);
+}
+
+char *
+atalk_ntoa(struct at_addr at)
+{
+ static char buf[20];
+
+ (void) snprintf(buf, sizeof(buf), "%u.%u", ntohs(at.s_net), at.s_node);
+ return(buf);
+}
diff --git a/sbin/routed/Makefile b/sbin/routed/Makefile
new file mode 100644
index 0000000..1ac16c4
--- /dev/null
+++ b/sbin/routed/Makefile
@@ -0,0 +1,12 @@
+# Make `routed` for FreeBSD
+# $FreeBSD$
+
+PROG= routed
+SRCS= if.c input.c main.c output.c parms.c radix.c rdisc.c table.c trace.c
+MAN= routed.8
+SUBDIR= rtquery
+LDADD= -lmd
+DPADD= ${LIBMD}
+WARNS?= 0
+
+.include <bsd.prog.mk>
diff --git a/sbin/routed/Makefile.inc b/sbin/routed/Makefile.inc
new file mode 100644
index 0000000..265f86d
--- /dev/null
+++ b/sbin/routed/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+.include "../Makefile.inc"
diff --git a/sbin/routed/defs.h b/sbin/routed/defs.h
new file mode 100644
index 0000000..9860e4d
--- /dev/null
+++ b/sbin/routed/defs.h
@@ -0,0 +1,674 @@
+/*
+ * Copyright (c) 1983, 1988, 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.
+ * 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 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.
+ *
+ * @(#)defs.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+#ifdef sgi
+#ident "$FreeBSD$"
+#endif
+
+/* Definitions for RIPv2 routing process.
+ *
+ * This code is based on the 4.4BSD `routed` daemon, with extensions to
+ * support:
+ * RIPv2, including variable length subnet masks.
+ * Router Discovery
+ * aggregate routes in the kernel tables.
+ * aggregate advertised routes.
+ * maintain spare routes for faster selection of another gateway
+ * when the current gateway dies.
+ * timers on routes with second granularity so that selection
+ * of a new route does not wait 30-60 seconds.
+ * tolerance of static routes.
+ * tell the kernel hop counts
+ * do not advertise if ipforwarding=0
+ *
+ * The vestigial support for other protocols has been removed. There
+ * is no likelihood that IETF RIPv1 or RIPv2 will ever be used with
+ * other protocols. The result is far smaller, faster, cleaner, and
+ * perhaps understandable.
+ *
+ * The accumulation of special flags and kludges added over the many
+ * years have been simplified and integrated.
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#ifdef sgi
+#include <strings.h>
+#include <bstring.h>
+#endif
+#include <stdarg.h>
+#include <syslog.h>
+#include <time.h>
+#include <sys/cdefs.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#ifdef sgi
+#define _USER_ROUTE_TREE
+#include <net/radix.h>
+#else
+#include "radix.h"
+#define UNUSED __attribute__((unused))
+#define PATTRIB(f,l) __attribute__((format (printf,f,l)))
+#endif
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#define RIPVERSION RIPv2
+#include <protocols/routed.h>
+
+#ifndef __RCSID
+#define __RCSID(_s) static const char rcsid[] UNUSED = _s
+#endif
+#ifndef __COPYRIGHT
+#define __COPYRIGHT(_s) static const char copyright[] UNUSED = _s
+#endif
+
+/* Type of an IP address.
+ * Some systems do not like to pass structures, so do not use in_addr.
+ * Some systems think a long has 64 bits, which would be a gross waste.
+ * So define it here so it can be changed for the target system.
+ * It should be defined somewhere netinet/in.h, but it is not.
+ */
+#ifdef sgi
+#define naddr u_int32_t
+#elif defined (__NetBSD__)
+#define naddr u_int32_t
+#define _HAVE_SA_LEN
+#define _HAVE_SIN_LEN
+#else
+#define naddr u_long
+#define _HAVE_SA_LEN
+#define _HAVE_SIN_LEN
+#endif
+
+/* Turn on if IP_{ADD,DROP}_MEMBERSHIP and IP_MULTICAST_IF considers address
+ * within 0.0.0.0/8 as interface index.
+ */
+#ifdef __FreeBSD__
+#define MCAST_IFINDEX
+#endif
+
+/* Turn on if IP_DROP_MEMBERSHIP and IP_ADD_MEMBERSHIP do not look at
+ * the dstaddr of point-to-point interfaces.
+ * #define MCAST_PPP_BUG
+ */
+#ifdef MCAST_IFINDEX
+#undef MCAST_PPP_BUG
+#endif
+
+#define DAY (24*60*60)
+#define NEVER DAY /* a long time */
+#define EPOCH NEVER /* bias time by this to avoid <0 */
+
+/* Scan the kernel regularly to see if any interfaces have appeared or been
+ * turned off. These must be less than STALE_TIME.
+ */
+#define CHECK_BAD_INTERVAL 5 /* when an interface is known bad */
+#define CHECK_ACT_INTERVAL 30 /* when advertising */
+#define CHECK_QUIET_INTERVAL 300 /* when not */
+
+#define LIM_SEC(s,l) ((s).tv_sec = MIN((s).tv_sec, (l)))
+
+/* Metric used for fake default routes. It ought to be 15, but when
+ * processing advertised routes, previous versions of `routed` added
+ * to the received metric and discarded the route if the total was 16
+ * or larger.
+ */
+#define FAKE_METRIC (HOPCNT_INFINITY-2)
+
+
+/* Router Discovery parameters */
+#ifndef sgi
+#define INADDR_ALLROUTERS_GROUP 0xe0000002 /* 224.0.0.2 */
+#endif
+#define MaxMaxAdvertiseInterval 1800
+#define MinMaxAdvertiseInterval 4
+#define DefMaxAdvertiseInterval 600
+#define DEF_PreferenceLevel 0
+#define MIN_PreferenceLevel 0x80000000
+
+#define MAX_INITIAL_ADVERT_INTERVAL 16
+#define MAX_INITIAL_ADVERTS 3
+#define MAX_RESPONSE_DELAY 2
+
+#define MAX_SOLICITATION_DELAY 1
+#define SOLICITATION_INTERVAL 3
+#define MAX_SOLICITATIONS 3
+
+
+/* Bloated packet size for systems that simply add authentication to
+ * full-sized packets
+ */
+#define OVER_MAXPACKETSIZE (MAXPACKETSIZE+sizeof(struct netinfo)*2)
+/* typical packet buffers */
+union pkt_buf {
+ char packet[OVER_MAXPACKETSIZE*2];
+ struct rip rip;
+};
+
+#define GNAME_LEN 64 /* assumed=64 in parms.c */
+/* bigger than IFNAMSIZ, with room for "external()" or "remote()" */
+#define IF_NAME_LEN (GNAME_LEN+15)
+
+/* No more routes than this, to protect ourself in case something goes
+ * whacko and starts broadcasting zillions of bogus routes.
+ */
+#define MAX_ROUTES (128*1024)
+extern int total_routes;
+
+/* Main, daemon routing table structure
+ */
+struct rt_entry {
+ struct radix_node rt_nodes[2]; /* radix tree glue */
+ u_int rt_state;
+# define RS_IF 0x001 /* for network interface */
+# define RS_NET_INT 0x002 /* authority route */
+# define RS_NET_SYN 0x004 /* fake net route for subnet */
+# define RS_NO_NET_SYN (RS_LOCAL | RS_LOCAL | RS_IF)
+# define RS_SUBNET 0x008 /* subnet route from any source */
+# define RS_LOCAL 0x010 /* loopback for pt-to-pt */
+# define RS_MHOME 0x020 /* from -m */
+# define RS_STATIC 0x040 /* from the kernel */
+# define RS_RDISC 0x080 /* from router discovery */
+ struct sockaddr_in rt_dst_sock;
+ naddr rt_mask;
+ struct rt_spare {
+ struct interface *rts_ifp;
+ naddr rts_gate; /* forward packets here */
+ naddr rts_router; /* on the authority of this router */
+ char rts_metric;
+ u_short rts_tag;
+ time_t rts_time; /* timer to junk stale routes */
+ u_int rts_de_ag; /* de-aggregation level */
+#define NUM_SPARES 4
+ } rt_spares[NUM_SPARES];
+ u_int rt_seqno; /* when last changed */
+ char rt_poison_metric; /* to notice maximum recently */
+ time_t rt_poison_time; /* advertised metric */
+};
+#define rt_dst rt_dst_sock.sin_addr.s_addr
+#define rt_ifp rt_spares[0].rts_ifp
+#define rt_gate rt_spares[0].rts_gate
+#define rt_router rt_spares[0].rts_router
+#define rt_metric rt_spares[0].rts_metric
+#define rt_tag rt_spares[0].rts_tag
+#define rt_time rt_spares[0].rts_time
+#define rt_de_ag rt_spares[0].rts_de_ag
+
+#define HOST_MASK 0xffffffff
+#define RT_ISHOST(rt) ((rt)->rt_mask == HOST_MASK)
+
+/* age all routes that
+ * are not from -g, -m, or static routes from the kernel
+ * not unbroken interface routes
+ * but not broken interfaces
+ * nor non-passive, remote interfaces that are not aliases
+ * (i.e. remote & metric=0)
+ */
+#define AGE_RT(rt_state,ifp) (0 == ((rt_state) & (RS_MHOME | RS_STATIC \
+ | RS_NET_SYN | RS_RDISC)) \
+ && (!((rt_state) & RS_IF) \
+ || (ifp) == 0 \
+ || (((ifp)->int_state & IS_REMOTE) \
+ && !((ifp)->int_state & IS_PASSIVE))))
+
+/* true if A is better than B
+ * Better if
+ * - A is not a poisoned route
+ * - and A is not stale
+ * - and A has a shorter path
+ * - or is the router speaking for itself
+ * - or the current route is equal but stale
+ * - or it is a host route advertised by a system for itself
+ */
+#define BETTER_LINK(rt,A,B) ((A)->rts_metric < HOPCNT_INFINITY \
+ && now_stale <= (A)->rts_time \
+ && ((A)->rts_metric < (B)->rts_metric \
+ || ((A)->rts_gate == (A)->rts_router \
+ && (B)->rts_gate != (B)->rts_router) \
+ || ((A)->rts_metric == (B)->rts_metric \
+ && now_stale > (B)->rts_time) \
+ || (RT_ISHOST(rt) \
+ && (rt)->rt_dst == (A)->rts_router \
+ && (A)->rts_metric == (B)->rts_metric)))
+
+
+/* An "interface" is similar to a kernel ifnet structure, except it also
+ * handles "logical" or "IS_REMOTE" interfaces (remote gateways).
+ */
+struct interface {
+ struct interface *int_next, **int_prev;
+ struct interface *int_ahash, **int_ahash_prev;
+ struct interface *int_bhash, **int_bhash_prev;
+ struct interface *int_rlink, **int_rlink_prev;
+ struct interface *int_nhash, **int_nhash_prev;
+ char int_name[IF_NAME_LEN+1];
+ u_short int_index;
+ naddr int_addr; /* address on this host (net order) */
+ naddr int_brdaddr; /* broadcast address (n) */
+ naddr int_dstaddr; /* other end of pt-to-pt link (n) */
+ naddr int_net; /* working network # (host order)*/
+ naddr int_mask; /* working net mask (host order) */
+ naddr int_ripv1_mask; /* for inferring a mask (n) */
+ naddr int_std_addr; /* class A/B/C address (n) */
+ naddr int_std_net; /* class A/B/C network (h) */
+ naddr int_std_mask; /* class A/B/C netmask (h) */
+ int int_rip_sock; /* for queries */
+ int int_if_flags; /* some bits copied from kernel */
+ u_int int_state;
+ time_t int_act_time; /* last thought healthy */
+ time_t int_query_time;
+ u_short int_transitions; /* times gone up-down */
+ char int_metric;
+ u_char int_d_metric; /* for faked default route */
+ u_char int_adj_inmetric; /* adjust advertised metrics */
+ u_char int_adj_outmetric; /* instead of interface metric */
+ struct int_data {
+ u_int ipackets; /* previous network stats */
+ u_int ierrors;
+ u_int opackets;
+ u_int oerrors;
+#ifdef sgi
+ u_int odrops;
+#endif
+ time_t ts; /* timestamp on network stats */
+ } int_data;
+# define MAX_AUTH_KEYS 5
+ struct auth { /* authentication info */
+ u_int16_t type;
+ u_char key[RIP_AUTH_PW_LEN];
+ u_char keyid;
+ time_t start, end;
+ } int_auth[MAX_AUTH_KEYS];
+ /* router discovery parameters */
+ int int_rdisc_pref; /* signed preference to advertise */
+ int int_rdisc_int; /* MaxAdvertiseInterval */
+ int int_rdisc_cnt;
+ struct timeval int_rdisc_timer;
+};
+
+/* bits in int_state */
+#define IS_ALIAS 0x0000001 /* interface alias */
+#define IS_SUBNET 0x0000002 /* interface on subnetted network */
+#define IS_REMOTE 0x0000004 /* interface is not on this machine */
+#define IS_PASSIVE 0x0000008 /* remote and does not do RIP */
+#define IS_EXTERNAL 0x0000010 /* handled by EGP or something */
+#define IS_CHECKED 0x0000020 /* still exists */
+#define IS_ALL_HOSTS 0x0000040 /* in INADDR_ALLHOSTS_GROUP */
+#define IS_ALL_ROUTERS 0x0000080 /* in INADDR_ALLROUTERS_GROUP */
+#define IS_DISTRUST 0x0000100 /* ignore untrusted routers */
+#define IS_REDIRECT_OK 0x0000200 /* accept ICMP redirects */
+#define IS_BROKE 0x0000400 /* seems to be broken */
+#define IS_SICK 0x0000800 /* seems to be broken */
+#define IS_DUP 0x0001000 /* has a duplicate address */
+#define IS_NEED_NET_SYN 0x0002000 /* need RS_NET_SYN route */
+#define IS_NO_AG 0x0004000 /* do not aggregate subnets */
+#define IS_NO_SUPER_AG 0x0008000 /* do not aggregate networks */
+#define IS_NO_RIPV1_IN 0x0010000 /* no RIPv1 input at all */
+#define IS_NO_RIPV2_IN 0x0020000 /* no RIPv2 input at all */
+#define IS_NO_RIP_IN (IS_NO_RIPV1_IN | IS_NO_RIPV2_IN)
+#define IS_RIP_IN_OFF(s) (((s) & IS_NO_RIP_IN) == IS_NO_RIP_IN)
+#define IS_NO_RIPV1_OUT 0x0040000 /* no RIPv1 output at all */
+#define IS_NO_RIPV2_OUT 0x0080000 /* no RIPv2 output at all */
+#define IS_NO_RIP_OUT (IS_NO_RIPV1_OUT | IS_NO_RIPV2_OUT)
+#define IS_NO_RIP (IS_NO_RIP_OUT | IS_NO_RIP_IN)
+#define IS_RIP_OUT_OFF(s) (((s) & IS_NO_RIP_OUT) == IS_NO_RIP_OUT)
+#define IS_RIP_OFF(s) (((s) & IS_NO_RIP) == IS_NO_RIP)
+#define IS_NO_RIP_MCAST 0x0100000 /* broadcast RIPv2 */
+#define IS_NO_ADV_IN 0x0200000 /* do not listen to advertisements */
+#define IS_NO_SOL_OUT 0x0400000 /* send no solicitations */
+#define IS_SOL_OUT 0x0800000 /* send solicitations */
+#define GROUP_IS_SOL_OUT (IS_SOL_OUT | IS_NO_SOL_OUT)
+#define IS_NO_ADV_OUT 0x1000000 /* do not advertise rdisc */
+#define IS_ADV_OUT 0x2000000 /* advertise rdisc */
+#define GROUP_IS_ADV_OUT (IS_NO_ADV_OUT | IS_ADV_OUT)
+#define IS_BCAST_RDISC 0x4000000 /* broadcast instead of multicast */
+#define IS_NO_RDISC (IS_NO_ADV_IN | IS_NO_SOL_OUT | IS_NO_ADV_OUT)
+#define IS_PM_RDISC 0x8000000 /* poor-man's router discovery */
+
+#define iff_up(f) ((f) & IFF_UP)
+
+
+/* Information for aggregating routes */
+#define NUM_AG_SLOTS 32
+struct ag_info {
+ struct ag_info *ag_fine; /* slot with finer netmask */
+ struct ag_info *ag_cors; /* more coarse netmask */
+ naddr ag_dst_h; /* destination in host byte order */
+ naddr ag_mask;
+ naddr ag_gate;
+ naddr ag_nhop;
+ char ag_metric; /* metric to be advertised */
+ char ag_pref; /* aggregate based on this */
+ u_int ag_seqno;
+ u_short ag_tag;
+ u_short ag_state;
+#define AGS_SUPPRESS 0x001 /* combine with coarser mask */
+#define AGS_AGGREGATE 0x002 /* synthesize combined routes */
+#define AGS_REDUN0 0x004 /* redundant, finer routes output */
+#define AGS_REDUN1 0x008
+#define AG_IS_REDUN(state) (((state) & (AGS_REDUN0 | AGS_REDUN1)) \
+ == (AGS_REDUN0 | AGS_REDUN1))
+#define AGS_GATEWAY 0x010 /* tell kernel RTF_GATEWAY */
+#define AGS_IF 0x020 /* for an interface */
+#define AGS_RIPV2 0x040 /* send only as RIPv2 */
+#define AGS_FINE_GATE 0x080 /* ignore differing ag_gate when this
+ * has the finer netmask */
+#define AGS_CORS_GATE 0x100 /* ignore differing gate when this
+ * has the coarser netmasks */
+#define AGS_SPLIT_HZ 0x200 /* suppress for split horizon */
+
+ /* some bits are set if they are set on either route */
+#define AGS_AGGREGATE_EITHER (AGS_RIPV2 | AGS_GATEWAY | \
+ AGS_SUPPRESS | AGS_CORS_GATE)
+};
+
+
+/* parameters for interfaces */
+extern struct parm {
+ struct parm *parm_next;
+ char parm_name[IF_NAME_LEN+1];
+ naddr parm_net;
+ naddr parm_mask;
+
+ u_char parm_d_metric;
+ u_char parm_adj_inmetric;
+ char parm_adj_outmetric;
+ u_int parm_int_state;
+ int parm_rdisc_pref; /* signed IRDP preference */
+ int parm_rdisc_int; /* IRDP advertising interval */
+ struct auth parm_auth[MAX_AUTH_KEYS];
+} *parms;
+
+/* authority for internal networks */
+extern struct intnet {
+ struct intnet *intnet_next;
+ naddr intnet_addr; /* network byte order */
+ naddr intnet_mask;
+ char intnet_metric;
+} *intnets;
+
+/* defined RIPv1 netmasks */
+extern struct r1net {
+ struct r1net *r1net_next;
+ naddr r1net_net; /* host order */
+ naddr r1net_match;
+ naddr r1net_mask;
+} *r1nets;
+
+/* trusted routers */
+extern struct tgate {
+ struct tgate *tgate_next;
+ naddr tgate_addr;
+#define MAX_TGATE_NETS 32
+ struct tgate_net {
+ naddr net; /* host order */
+ naddr mask;
+ } tgate_nets[MAX_TGATE_NETS];
+} *tgates;
+
+enum output_type {OUT_QUERY, OUT_UNICAST, OUT_BROADCAST, OUT_MULTICAST,
+ NO_OUT_MULTICAST, NO_OUT_RIPV2};
+
+/* common output buffers */
+extern struct ws_buf {
+ struct rip *buf;
+ struct netinfo *n;
+ struct netinfo *base;
+ struct netinfo *lim;
+ enum output_type type;
+} v12buf, v2buf;
+
+extern pid_t mypid;
+extern naddr myaddr; /* main address of this system */
+
+extern int stopint; /* !=0 to stop */
+
+extern int sock_max;
+extern int rip_sock; /* RIP socket */
+extern struct interface *rip_sock_mcast; /* current multicast interface */
+extern int rt_sock; /* routing socket */
+extern int rt_sock_seqno;
+extern int rdisc_sock; /* router-discovery raw socket */
+
+extern int seqno; /* sequence number for messages */
+extern int supplier; /* process should supply updates */
+extern int supplier_set; /* -s or -q requested */
+extern int lookforinterfaces; /* 1=probe for new up interfaces */
+extern int ridhosts; /* 1=reduce host routes */
+extern int mhome; /* 1=want multi-homed host route */
+extern int advertise_mhome; /* 1=must continue advertising it */
+extern int auth_ok; /* 1=ignore auth if we do not care */
+
+extern struct timeval clk; /* system clock's idea of time */
+extern struct timeval epoch; /* system clock when started */
+extern struct timeval now; /* current idea of time */
+extern time_t now_stale;
+extern time_t now_expire;
+extern time_t now_garbage;
+
+extern struct timeval next_bcast; /* next general broadcast */
+extern struct timeval age_timer; /* next check of old routes */
+extern struct timeval no_flash; /* inhibit flash update until then */
+extern struct timeval rdisc_timer; /* next advert. or solicitation */
+extern int rdisc_ok; /* using solicited route */
+
+extern struct timeval ifinit_timer; /* time to check interfaces */
+
+extern naddr loopaddr; /* our address on loopback */
+extern int tot_interfaces; /* # of remote and local interfaces */
+extern int rip_interfaces; /* # of interfaces doing RIP */
+extern struct interface *ifnet; /* all interfaces */
+extern struct interface *remote_if; /* remote interfaces */
+extern int have_ripv1_out; /* have a RIPv1 interface */
+extern int have_ripv1_in;
+extern int need_flash; /* flash update needed */
+extern struct timeval need_kern; /* need to update kernel table */
+extern u_int update_seqno; /* a route has changed */
+
+extern int tracelevel, new_tracelevel;
+#define MAX_TRACELEVEL 4
+#define TRACEKERNEL (tracelevel >= 4) /* log kernel changes */
+#define TRACECONTENTS (tracelevel >= 3) /* display packet contents */
+#define TRACEPACKETS (tracelevel >= 2) /* note packets */
+#define TRACEACTIONS (tracelevel != 0)
+extern FILE *ftrace; /* output trace file */
+extern char inittracename[PATH_MAX];
+
+extern struct radix_node_head *rhead;
+
+
+#ifdef sgi
+/* Fix conflicts */
+#define dup2(x,y) BSDdup2(x,y)
+#endif /* sgi */
+
+extern void fix_sock(int, const char *);
+extern void fix_select(void);
+extern void rip_off(void);
+extern void rip_on(struct interface *);
+
+extern void bufinit(void);
+extern int output(enum output_type, struct sockaddr_in *,
+ struct interface *, struct rip *, int);
+extern void clr_ws_buf(struct ws_buf *, struct auth *);
+extern void rip_query(void);
+extern void rip_bcast(int);
+extern void supply(struct sockaddr_in *, struct interface *,
+ enum output_type, int, int, int);
+
+extern void msglog(const char *, ...) PATTRIB(1,2);
+struct msg_limit {
+ time_t reuse;
+ struct msg_sub {
+ naddr addr;
+ time_t until;
+# define MSG_SUBJECT_N 8
+ } subs[MSG_SUBJECT_N];
+};
+extern void msglim(struct msg_limit *, naddr,
+ const char *, ...) PATTRIB(3,4);
+#define LOGERR(msg) msglog(msg ": %s", strerror(errno))
+extern void logbad(int, const char *, ...) PATTRIB(2,3);
+#define BADERR(dump,msg) logbad(dump,msg ": %s", strerror(errno))
+#ifdef DEBUG
+#define DBGERR(dump,msg) BADERR(dump,msg)
+#else
+#define DBGERR(dump,msg) LOGERR(msg)
+#endif
+extern char *naddr_ntoa(naddr);
+extern const char *saddr_ntoa(struct sockaddr *);
+
+extern void *rtmalloc(size_t, const char *);
+extern void timevaladd(struct timeval *, struct timeval *);
+extern void intvl_random(struct timeval *, u_long, u_long);
+extern int getnet(char *, naddr *, naddr *);
+extern int gethost(char *, naddr *);
+extern void gwkludge(void);
+extern const char *parse_parms(char *, int);
+extern const char *check_parms(struct parm *);
+extern void get_parms(struct interface *);
+
+extern void lastlog(void);
+extern void trace_close(int);
+extern void set_tracefile(const char *, const char *, int);
+extern void tracelevel_msg(const char *, int);
+extern void trace_off(const char*, ...) PATTRIB(1,2);
+extern void set_tracelevel(void);
+extern void trace_flush(void);
+extern void trace_misc(const char *, ...) PATTRIB(1,2);
+extern void trace_act(const char *, ...) PATTRIB(1,2);
+extern void trace_pkt(const char *, ...) PATTRIB(1,2);
+extern void trace_add_del(const char *, struct rt_entry *);
+extern void trace_change(struct rt_entry *, u_int, struct rt_spare *,
+ const char *);
+extern void trace_if(const char *, struct interface *);
+extern void trace_upslot(struct rt_entry *, struct rt_spare *,
+ struct rt_spare *);
+extern void trace_rip(const char*, const char*, struct sockaddr_in *,
+ struct interface *, struct rip *, int);
+extern char *addrname(naddr, naddr, int);
+extern char *rtname(naddr, naddr, naddr);
+
+extern void rdisc_age(naddr);
+extern void set_rdisc_mg(struct interface *, int);
+extern void set_supplier(void);
+extern void if_bad_rdisc(struct interface *);
+extern void if_ok_rdisc(struct interface *);
+extern void read_rip(int, struct interface *);
+extern void read_rt(void);
+extern void read_d(void);
+extern void rdisc_adv(void);
+extern void rdisc_sol(void);
+
+extern void sigalrm(int);
+extern void sigterm(int);
+
+extern void sigtrace_on(int);
+extern void sigtrace_off(int);
+
+extern void flush_kern(void);
+extern void age(naddr);
+
+extern void ag_flush(naddr, naddr, void (*)(struct ag_info *));
+extern void ag_check(naddr, naddr, naddr, naddr, char, char, u_int,
+ u_short, u_short, void (*)(struct ag_info *));
+extern void del_static(naddr, naddr, naddr, int);
+extern void del_redirects(naddr, time_t);
+extern struct rt_entry *rtget(naddr, naddr);
+extern struct rt_entry *rtfind(naddr);
+extern void rtinit(void);
+extern void rtadd(naddr, naddr, u_int, struct rt_spare *);
+extern void rtchange(struct rt_entry *, u_int, struct rt_spare *, char *);
+extern void rtdelete(struct rt_entry *);
+extern void rts_delete(struct rt_entry *, struct rt_spare *);
+extern void rtbad_sub(struct rt_entry *);
+extern void rtswitch(struct rt_entry *, struct rt_spare *);
+extern void rtbad(struct rt_entry *);
+
+#define S_ADDR(x) (((struct sockaddr_in *)(x))->sin_addr.s_addr)
+#define INFO_DST(I) ((I)->rti_info[RTAX_DST])
+#define INFO_GATE(I) ((I)->rti_info[RTAX_GATEWAY])
+#define INFO_MASK(I) ((I)->rti_info[RTAX_NETMASK])
+#define INFO_IFA(I) ((I)->rti_info[RTAX_IFA])
+#define INFO_IFP(I) ((I)->rti_info[RTAX_IFP])
+#define INFO_AUTHOR(I) ((I)->rti_info[RTAX_AUTHOR])
+#define INFO_BRD(I) ((I)->rti_info[RTAX_BRD])
+void rt_xaddrs(struct rt_addrinfo *, struct sockaddr *, struct sockaddr *,
+ int);
+
+extern naddr std_mask(naddr);
+extern naddr ripv1_mask_net(naddr, struct interface *);
+extern naddr ripv1_mask_host(naddr,struct interface *);
+#define on_net(a,net,mask) (((ntohl(a) ^ (net)) & (mask)) == 0)
+extern int check_dst(naddr);
+extern struct interface *check_dup(naddr, naddr, naddr, int);
+extern int check_remote(struct interface *);
+extern int addrouteforif(struct interface *);
+extern void ifinit(void);
+extern int walk_bad(struct radix_node *, struct walkarg *);
+extern int if_ok(struct interface *, const char *);
+extern void if_sick(struct interface *);
+extern void if_bad(struct interface *);
+extern void if_link(struct interface *);
+extern struct interface *ifwithaddr(naddr, int, int);
+extern struct interface *ifwithname(char *, naddr);
+extern struct interface *ifwithindex(u_short, int);
+extern struct interface *iflookup(naddr);
+
+extern struct auth *find_auth(struct interface *);
+extern void end_md5_auth(struct ws_buf *, struct auth *);
+
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+#include <md5.h>
+#else
+#define MD5_DIGEST_LEN 16
+typedef struct {
+ u_int32_t state[4]; /* state (ABCD) */
+ u_int32_t count[2]; /* # of bits, modulo 2^64 (LSB 1st) */
+ unsigned char buffer[64]; /* input buffer */
+} MD5_CTX;
+extern void MD5Init(MD5_CTX*);
+extern void MD5Update(MD5_CTX*, u_char*, u_int);
+extern void MD5Final(u_char[MD5_DIGEST_LEN], MD5_CTX*);
+#endif
diff --git a/sbin/routed/if.c b/sbin/routed/if.c
new file mode 100644
index 0000000..f8038f6a
--- /dev/null
+++ b/sbin/routed/if.c
@@ -0,0 +1,1398 @@
+/*
+ * 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.
+ * 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+#include "pathnames.h"
+
+#ifdef __NetBSD__
+__RCSID("$NetBSD$");
+#elif defined(__FreeBSD__)
+__RCSID("$FreeBSD$");
+#else
+__RCSID("$Revision: 2.27 $");
+#ident "$Revision: 2.27 $"
+#endif
+
+struct interface *ifnet; /* all interfaces */
+
+/* hash table for all interfaces, big enough to tolerate ridiculous
+ * numbers of IP aliases. Crazy numbers of aliases such as 7000
+ * still will not do well, but not just in looking up interfaces
+ * by name or address.
+ */
+#define AHASH_LEN 211 /* must be prime */
+#define AHASH(a) &ahash_tbl[(a)%AHASH_LEN]
+struct interface *ahash_tbl[AHASH_LEN];
+
+#define BHASH_LEN 211 /* must be prime */
+#define BHASH(a) &bhash_tbl[(a)%BHASH_LEN]
+struct interface *bhash_tbl[BHASH_LEN];
+
+struct interface *remote_if; /* remote interfaces */
+
+/* hash for physical interface names.
+ * Assume there are never more 100 or 200 real interfaces, and that
+ * aliases are put on the end of the hash chains.
+ */
+#define NHASH_LEN 97
+struct interface *nhash_tbl[NHASH_LEN];
+
+int tot_interfaces; /* # of remote and local interfaces */
+int rip_interfaces; /* # of interfaces doing RIP */
+int foundloopback; /* valid flag for loopaddr */
+naddr loopaddr; /* our address on loopback */
+struct rt_spare loop_rts;
+
+struct timeval ifinit_timer;
+static struct timeval last_ifinit;
+#define IF_RESCAN_DELAY() (last_ifinit.tv_sec == now.tv_sec \
+ && last_ifinit.tv_usec == now.tv_usec \
+ && timercmp(&ifinit_timer, &now, >))
+
+int have_ripv1_out; /* have a RIPv1 interface */
+int have_ripv1_in;
+
+
+static struct interface**
+nhash(char *p)
+{
+ u_int i;
+
+ for (i = 0; *p != '\0'; p++) {
+ i = ((i<<1) & 0x7fffffff) | ((i>>31) & 1);
+ i ^= *p;
+ }
+ return &nhash_tbl[i % NHASH_LEN];
+}
+
+
+/* Link a new interface into the lists and hash tables.
+ */
+void
+if_link(struct interface *ifp)
+{
+ struct interface **hifp;
+
+ ifp->int_prev = &ifnet;
+ ifp->int_next = ifnet;
+ if (ifnet != 0)
+ ifnet->int_prev = &ifp->int_next;
+ ifnet = ifp;
+
+ hifp = AHASH(ifp->int_addr);
+ ifp->int_ahash_prev = hifp;
+ if ((ifp->int_ahash = *hifp) != 0)
+ (*hifp)->int_ahash_prev = &ifp->int_ahash;
+ *hifp = ifp;
+
+ if (ifp->int_if_flags & IFF_BROADCAST) {
+ hifp = BHASH(ifp->int_brdaddr);
+ ifp->int_bhash_prev = hifp;
+ if ((ifp->int_bhash = *hifp) != 0)
+ (*hifp)->int_bhash_prev = &ifp->int_bhash;
+ *hifp = ifp;
+ }
+
+ if (ifp->int_state & IS_REMOTE) {
+ ifp->int_rlink_prev = &remote_if;
+ ifp->int_rlink = remote_if;
+ if (remote_if != 0)
+ remote_if->int_rlink_prev = &ifp->int_rlink;
+ remote_if = ifp;
+ }
+
+ hifp = nhash(ifp->int_name);
+ if (ifp->int_state & IS_ALIAS) {
+ /* put aliases on the end of the hash chain */
+ while (*hifp != 0)
+ hifp = &(*hifp)->int_nhash;
+ }
+ ifp->int_nhash_prev = hifp;
+ if ((ifp->int_nhash = *hifp) != 0)
+ (*hifp)->int_nhash_prev = &ifp->int_nhash;
+ *hifp = ifp;
+}
+
+
+/* Find the interface with an address
+ */
+struct interface *
+ifwithaddr(naddr addr,
+ int bcast, /* notice IFF_BROADCAST address */
+ int remote) /* include IS_REMOTE interfaces */
+{
+ struct interface *ifp, *possible = 0;
+
+ remote = (remote == 0) ? IS_REMOTE : 0;
+
+ for (ifp = *AHASH(addr); ifp; ifp = ifp->int_ahash) {
+ if (ifp->int_addr != addr)
+ continue;
+ if ((ifp->int_state & remote) != 0)
+ continue;
+ if ((ifp->int_state & (IS_BROKE | IS_PASSIVE)) == 0)
+ return ifp;
+ possible = ifp;
+ }
+
+ if (possible || !bcast)
+ return possible;
+
+ for (ifp = *BHASH(addr); ifp; ifp = ifp->int_bhash) {
+ if (ifp->int_brdaddr != addr)
+ continue;
+ if ((ifp->int_state & remote) != 0)
+ continue;
+ if ((ifp->int_state & (IS_BROKE | IS_PASSIVE)) == 0)
+ return ifp;
+ possible = ifp;
+ }
+
+ return possible;
+}
+
+
+/* find the interface with a name
+ */
+struct interface *
+ifwithname(char *name, /* "ec0" or whatever */
+ naddr addr) /* 0 or network address */
+{
+ struct interface *ifp;
+
+ for (;;) {
+ for (ifp = *nhash(name); ifp != 0; ifp = ifp->int_nhash) {
+ /* If the network address is not specified,
+ * ignore any alias interfaces. Otherwise, look
+ * for the interface with the target name and address.
+ */
+ if (!strcmp(ifp->int_name, name)
+ && ((addr == 0 && !(ifp->int_state & IS_ALIAS))
+ || (ifp->int_addr == addr)))
+ return ifp;
+ }
+
+ /* If there is no known interface, maybe there is a
+ * new interface. So just once look for new interfaces.
+ */
+ if (IF_RESCAN_DELAY())
+ return 0;
+ ifinit();
+ }
+}
+
+
+struct interface *
+ifwithindex(u_short ifindex,
+ int rescan_ok)
+{
+ struct interface *ifp;
+
+ for (;;) {
+ for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) {
+ if (ifp->int_index == ifindex)
+ return ifp;
+ }
+
+ /* If there is no known interface, maybe there is a
+ * new interface. So just once look for new interfaces.
+ */
+ if (!rescan_ok
+ || IF_RESCAN_DELAY())
+ return 0;
+ ifinit();
+ }
+}
+
+
+/* Find an interface from which the specified address
+ * should have come from. Used for figuring out which
+ * interface a packet came in on.
+ */
+struct interface *
+iflookup(naddr addr)
+{
+ struct interface *ifp, *maybe;
+ int once = 0;
+
+ maybe = 0;
+ for (;;) {
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ /* finished with a match */
+ if (ifp->int_dstaddr == addr)
+ return ifp;
+
+ } else {
+ /* finished with an exact match */
+ if (ifp->int_addr == addr)
+ return ifp;
+
+ /* Look for the longest approximate match.
+ */
+ if (on_net(addr, ifp->int_net, ifp->int_mask)
+ && (maybe == 0
+ || ifp->int_mask > maybe->int_mask))
+ maybe = ifp;
+ }
+ }
+
+ if (maybe != 0 || once || IF_RESCAN_DELAY())
+ return maybe;
+ once = 1;
+
+ /* If there is no known interface, maybe there is a
+ * new interface. So just once look for new interfaces.
+ */
+ ifinit();
+ }
+}
+
+
+/* Return the classical netmask for an IP address.
+ */
+naddr /* host byte order */
+std_mask(naddr addr) /* network byte order */
+{
+ addr = ntohl(addr); /* was a host, not a network */
+
+ if (addr == 0) /* default route has mask 0 */
+ return 0;
+ if (IN_CLASSA(addr))
+ return IN_CLASSA_NET;
+ if (IN_CLASSB(addr))
+ return IN_CLASSB_NET;
+ return IN_CLASSC_NET;
+}
+
+
+/* Find the netmask that would be inferred by RIPv1 listeners
+ * on the given interface for a given network.
+ * If no interface is specified, look for the best fitting interface.
+ */
+naddr
+ripv1_mask_net(naddr addr, /* in network byte order */
+ struct interface *ifp) /* as seen on this interface */
+{
+ struct r1net *r1p;
+ naddr mask = 0;
+
+ if (addr == 0) /* default always has 0 mask */
+ return mask;
+
+ if (ifp != 0 && ifp->int_ripv1_mask != HOST_MASK) {
+ /* If the target network is that of the associated interface
+ * on which it arrived, then use the netmask of the interface.
+ */
+ if (on_net(addr, ifp->int_net, ifp->int_std_mask))
+ mask = ifp->int_ripv1_mask;
+
+ } else {
+ /* Examine all interfaces, and if it the target seems
+ * to have the same network number of an interface, use the
+ * netmask of that interface. If there is more than one
+ * such interface, prefer the interface with the longest
+ * match.
+ */
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
+ if (on_net(addr, ifp->int_std_net, ifp->int_std_mask)
+ && ifp->int_ripv1_mask > mask
+ && ifp->int_ripv1_mask != HOST_MASK)
+ mask = ifp->int_ripv1_mask;
+ }
+
+ }
+
+ /* check special definitions */
+ if (mask == 0) {
+ for (r1p = r1nets; r1p != 0; r1p = r1p->r1net_next) {
+ if (on_net(addr, r1p->r1net_net, r1p->r1net_match)
+ && r1p->r1net_mask > mask)
+ mask = r1p->r1net_mask;
+ }
+
+ /* Otherwise, make the classic A/B/C guess.
+ */
+ if (mask == 0)
+ mask = std_mask(addr);
+ }
+
+ return mask;
+}
+
+
+naddr
+ripv1_mask_host(naddr addr, /* in network byte order */
+ struct interface *ifp) /* as seen on this interface */
+{
+ naddr mask = ripv1_mask_net(addr, ifp);
+
+
+ /* If the computed netmask does not mask the address,
+ * then assume it is a host address
+ */
+ if ((ntohl(addr) & ~mask) != 0)
+ mask = HOST_MASK;
+ return mask;
+}
+
+
+/* See if an IP address looks reasonable as a destination.
+ */
+int /* 0=bad */
+check_dst(naddr addr)
+{
+ addr = ntohl(addr);
+
+ if (IN_CLASSA(addr)) {
+ if (addr == 0)
+ return 1; /* default */
+
+ addr >>= IN_CLASSA_NSHIFT;
+ return (addr != 0 && addr != IN_LOOPBACKNET);
+ }
+
+ return (IN_CLASSB(addr) || IN_CLASSC(addr));
+}
+
+
+/* See a new interface duplicates an existing interface.
+ */
+struct interface *
+check_dup(naddr addr, /* IP address, so network byte order */
+ naddr dstaddr, /* ditto */
+ naddr mask, /* mask, so host byte order */
+ int if_flags)
+{
+ struct interface *ifp;
+
+ for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) {
+ if (ifp->int_mask != mask)
+ continue;
+
+ if (!iff_up(ifp->int_if_flags))
+ continue;
+
+ /* The local address can only be shared with a point-to-point
+ * link.
+ */
+ if ((!(ifp->int_state & IS_REMOTE) || !(if_flags & IS_REMOTE))
+ && ifp->int_addr == addr
+ && (((if_flags|ifp->int_if_flags) & IFF_POINTOPOINT) == 0))
+ return ifp;
+
+ if (on_net(ifp->int_dstaddr, ntohl(dstaddr),mask))
+ return ifp;
+ }
+ return 0;
+}
+
+
+/* See that a remote gateway is reachable.
+ * Note that the answer can change as real interfaces come and go.
+ */
+int /* 0=bad */
+check_remote(struct interface *ifp)
+{
+ struct rt_entry *rt;
+
+ /* do not worry about other kinds */
+ if (!(ifp->int_state & IS_REMOTE))
+ return 1;
+
+ rt = rtfind(ifp->int_addr);
+ if (rt != 0
+ && rt->rt_ifp != 0
+ &&on_net(ifp->int_addr,
+ rt->rt_ifp->int_net, rt->rt_ifp->int_mask))
+ return 1;
+
+ /* the gateway cannot be reached directly from one of our
+ * interfaces
+ */
+ if (!(ifp->int_state & IS_BROKE)) {
+ msglog("unreachable gateway %s in "_PATH_GATEWAYS,
+ naddr_ntoa(ifp->int_addr));
+ if_bad(ifp);
+ }
+ return 0;
+}
+
+
+/* Delete an interface.
+ */
+static void
+ifdel(struct interface *ifp)
+{
+ struct ip_mreq m;
+ struct interface *ifp1;
+
+
+ trace_if("Del", ifp);
+
+ ifp->int_state |= IS_BROKE;
+
+ /* unlink the interface
+ */
+ *ifp->int_prev = ifp->int_next;
+ if (ifp->int_next != 0)
+ ifp->int_next->int_prev = ifp->int_prev;
+ *ifp->int_ahash_prev = ifp->int_ahash;
+ if (ifp->int_ahash != 0)
+ ifp->int_ahash->int_ahash_prev = ifp->int_ahash_prev;
+ *ifp->int_nhash_prev = ifp->int_nhash;
+ if (ifp->int_nhash != 0)
+ ifp->int_nhash->int_nhash_prev = ifp->int_nhash_prev;
+ if (ifp->int_if_flags & IFF_BROADCAST) {
+ *ifp->int_bhash_prev = ifp->int_bhash;
+ if (ifp->int_bhash != 0)
+ ifp->int_bhash->int_bhash_prev = ifp->int_bhash_prev;
+ }
+ if (ifp->int_state & IS_REMOTE) {
+ *ifp->int_rlink_prev = ifp->int_rlink;
+ if (ifp->int_rlink != 0)
+ ifp->int_rlink->int_rlink_prev = ifp->int_rlink_prev;
+ }
+
+ if (!(ifp->int_state & IS_ALIAS)) {
+ /* delete aliases when the main interface dies
+ */
+ for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) {
+ if (ifp1 != ifp
+ && !strcmp(ifp->int_name, ifp1->int_name))
+ ifdel(ifp1);
+ }
+
+ if ((ifp->int_if_flags & IFF_MULTICAST)
+#ifdef MCAST_PPP_BUG
+ && !(ifp->int_if_flags & IFF_POINTOPOINT)
+#endif
+ && rip_sock >= 0) {
+ m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP);
+#ifdef MCAST_IFINDEX
+ m.imr_interface.s_addr = htonl(ifp->int_index);
+#else
+ m.imr_interface.s_addr = ((ifp->int_if_flags
+ & IFF_POINTOPOINT)
+ ? ifp->int_dstaddr
+ : ifp->int_addr);
+#endif
+ if (setsockopt(rip_sock,IPPROTO_IP,IP_DROP_MEMBERSHIP,
+ &m, sizeof(m)) < 0
+ && errno != EADDRNOTAVAIL
+ && !TRACEACTIONS)
+ LOGERR("setsockopt(IP_DROP_MEMBERSHIP RIP)");
+ if (rip_sock_mcast == ifp)
+ rip_sock_mcast = 0;
+ }
+ if (ifp->int_rip_sock >= 0) {
+ (void)close(ifp->int_rip_sock);
+ ifp->int_rip_sock = -1;
+ fix_select();
+ }
+
+ tot_interfaces--;
+ if (!IS_RIP_OFF(ifp->int_state))
+ rip_interfaces--;
+
+ /* Zap all routes associated with this interface.
+ * Assume routes just using gateways beyond this interface
+ * will timeout naturally, and have probably already died.
+ */
+ (void)rn_walktree(rhead, walk_bad, 0);
+
+ set_rdisc_mg(ifp, 0);
+ if_bad_rdisc(ifp);
+ }
+
+ free(ifp);
+}
+
+
+/* Mark an interface ill.
+ */
+void
+if_sick(struct interface *ifp)
+{
+ if (0 == (ifp->int_state & (IS_SICK | IS_BROKE))) {
+ ifp->int_state |= IS_SICK;
+ ifp->int_act_time = NEVER;
+ trace_if("Chg", ifp);
+
+ LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL);
+ }
+}
+
+
+/* Mark an interface dead.
+ */
+void
+if_bad(struct interface *ifp)
+{
+ struct interface *ifp1;
+
+
+ if (ifp->int_state & IS_BROKE)
+ return;
+
+ LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL);
+
+ ifp->int_state |= (IS_BROKE | IS_SICK);
+ ifp->int_act_time = NEVER;
+ ifp->int_query_time = NEVER;
+ ifp->int_data.ts = now.tv_sec;
+
+ trace_if("Chg", ifp);
+
+ if (!(ifp->int_state & IS_ALIAS)) {
+ for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) {
+ if (ifp1 != ifp
+ && !strcmp(ifp->int_name, ifp1->int_name))
+ if_bad(ifp1);
+ }
+ (void)rn_walktree(rhead, walk_bad, 0);
+ if_bad_rdisc(ifp);
+ }
+}
+
+
+/* Mark an interface alive
+ */
+int /* 1=it was dead */
+if_ok(struct interface *ifp,
+ const char *type)
+{
+ struct interface *ifp1;
+
+
+ if (!(ifp->int_state & IS_BROKE)) {
+ if (ifp->int_state & IS_SICK) {
+ trace_act("%sinterface %s to %s working better",
+ type,
+ ifp->int_name, naddr_ntoa(ifp->int_dstaddr));
+ ifp->int_state &= ~IS_SICK;
+ }
+ return 0;
+ }
+
+ msglog("%sinterface %s to %s restored",
+ type, ifp->int_name, naddr_ntoa(ifp->int_dstaddr));
+ ifp->int_state &= ~(IS_BROKE | IS_SICK);
+ ifp->int_data.ts = 0;
+
+ if (!(ifp->int_state & IS_ALIAS)) {
+ for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) {
+ if (ifp1 != ifp
+ && !strcmp(ifp->int_name, ifp1->int_name))
+ if_ok(ifp1, type);
+ }
+ if_ok_rdisc(ifp);
+ }
+
+ if (ifp->int_state & IS_REMOTE) {
+ if (!addrouteforif(ifp))
+ return 0;
+ }
+ return 1;
+}
+
+
+/* disassemble routing message
+ */
+void
+rt_xaddrs(struct rt_addrinfo *info,
+ struct sockaddr *sa,
+ struct sockaddr *lim,
+ int addrs)
+{
+ int i;
+#ifdef _HAVE_SA_LEN
+ static struct sockaddr sa_zero;
+#endif
+
+ memset(info, 0, sizeof(*info));
+ info->rti_addrs = addrs;
+ for (i = 0; i < RTAX_MAX && sa < lim; i++) {
+ if ((addrs & (1 << i)) == 0)
+ continue;
+ info->rti_info[i] = (sa->sa_len != 0) ? sa : &sa_zero;
+ sa = (struct sockaddr *)((char*)(sa) + SA_SIZE(sa));
+ }
+}
+
+
+/* Find the network interfaces which have configured themselves.
+ * This must be done regularly, if only for extra addresses
+ * that come and go on interfaces.
+ */
+void
+ifinit(void)
+{
+ static char *sysctl_buf;
+ static size_t sysctl_buf_size = 0;
+ uint complaints = 0;
+ static u_int prev_complaints = 0;
+# define COMP_NOT_INET 0x001
+# define COMP_NOADDR 0x002
+# define COMP_BADADDR 0x004
+# define COMP_NODST 0x008
+# define COMP_NOBADR 0x010
+# define COMP_NOMASK 0x020
+# define COMP_DUP 0x040
+# define COMP_BAD_METRIC 0x080
+# define COMP_NETMASK 0x100
+
+ struct interface ifs, ifs0, *ifp, *ifp1;
+ struct rt_entry *rt;
+ size_t needed;
+ int mib[6];
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam, *ifam_lim, *ifam2;
+ int in, ierr, out, oerr;
+ struct intnet *intnetp;
+ struct rt_addrinfo info;
+#ifdef SIOCGIFMETRIC
+ struct ifreq ifr;
+#endif
+
+
+ last_ifinit = now;
+ ifinit_timer.tv_sec = now.tv_sec + (supplier
+ ? CHECK_ACT_INTERVAL
+ : CHECK_QUIET_INTERVAL);
+
+ /* mark all interfaces so we can get rid of those that disappear */
+ for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next)
+ ifp->int_state &= ~(IS_CHECKED | IS_DUP);
+
+ /* Fetch the interface list, without too many system calls
+ * since we do it repeatedly.
+ */
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+ for (;;) {
+ if ((needed = sysctl_buf_size) != 0) {
+ if (sysctl(mib, 6, sysctl_buf,&needed, 0, 0) >= 0)
+ break;
+ /* retry if the table grew */
+ if (errno != ENOMEM && errno != EFAULT)
+ BADERR(1, "ifinit: sysctl(RT_IFLIST)");
+ free(sysctl_buf);
+ needed = 0;
+ }
+ if (sysctl(mib, 6, 0, &needed, 0, 0) < 0)
+ BADERR(1,"ifinit: sysctl(RT_IFLIST) estimate");
+ sysctl_buf = rtmalloc(sysctl_buf_size = needed,
+ "ifinit sysctl");
+ }
+
+ ifam_lim = (struct ifa_msghdr *)(sysctl_buf + needed);
+ for (ifam = (struct ifa_msghdr *)sysctl_buf;
+ ifam < ifam_lim;
+ ifam = ifam2) {
+
+ ifam2 = (struct ifa_msghdr*)((char*)ifam + ifam->ifam_msglen);
+
+#ifdef RTM_OIFINFO
+ if (ifam->ifam_type == RTM_OIFINFO)
+ continue; /* just ignore compat message */
+#endif
+ if (ifam->ifam_type == RTM_IFINFO) {
+ struct sockaddr_dl *sdl;
+
+ ifm = (struct if_msghdr *)ifam;
+ /* make prototype structure for the IP aliases
+ */
+ memset(&ifs0, 0, sizeof(ifs0));
+ ifs0.int_rip_sock = -1;
+ ifs0.int_index = ifm->ifm_index;
+ ifs0.int_if_flags = ifm->ifm_flags;
+ ifs0.int_state = IS_CHECKED;
+ ifs0.int_query_time = NEVER;
+ ifs0.int_act_time = now.tv_sec;
+ ifs0.int_data.ts = now.tv_sec;
+ ifs0.int_data.ipackets = ifm->ifm_data.ifi_ipackets;
+ ifs0.int_data.ierrors = ifm->ifm_data.ifi_ierrors;
+ ifs0.int_data.opackets = ifm->ifm_data.ifi_opackets;
+ ifs0.int_data.oerrors = ifm->ifm_data.ifi_oerrors;
+#ifdef sgi
+ ifs0.int_data.odrops = ifm->ifm_data.ifi_odrops;
+#endif
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ sdl->sdl_data[sdl->sdl_nlen] = 0;
+ strncpy(ifs0.int_name, sdl->sdl_data,
+ MIN(sizeof(ifs0.int_name), sdl->sdl_nlen));
+ continue;
+ }
+ if (ifam->ifam_type != RTM_NEWADDR) {
+ logbad(1,"ifinit: out of sync");
+ continue;
+ }
+ rt_xaddrs(&info, (struct sockaddr *)(ifam+1),
+ (struct sockaddr *)ifam2,
+ ifam->ifam_addrs);
+
+ /* Prepare for the next address of this interface, which
+ * will be an alias.
+ * Do not output RIP or Router-Discovery packets via aliases.
+ */
+ memcpy(&ifs, &ifs0, sizeof(ifs));
+ ifs0.int_state |= (IS_ALIAS | IS_NO_RIP_OUT | IS_NO_RDISC);
+
+ if (INFO_IFA(&info) == 0) {
+ if (iff_up(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_NOADDR))
+ msglog("%s has no address",
+ ifs.int_name);
+ complaints |= COMP_NOADDR;
+ }
+ continue;
+ }
+ if (INFO_IFA(&info)->sa_family != AF_INET) {
+ if (iff_up(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_NOT_INET))
+ trace_act("%s: not AF_INET",
+ ifs.int_name);
+ complaints |= COMP_NOT_INET;
+ }
+ continue;
+ }
+
+ ifs.int_addr = S_ADDR(INFO_IFA(&info));
+
+ if (ntohl(ifs.int_addr)>>24 == 0
+ || ntohl(ifs.int_addr)>>24 == 0xff) {
+ if (iff_up(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_BADADDR))
+ msglog("%s has a bad address",
+ ifs.int_name);
+ complaints |= COMP_BADADDR;
+ }
+ continue;
+ }
+
+ if (ifs.int_if_flags & IFF_LOOPBACK) {
+ ifs.int_state |= IS_NO_RIP | IS_NO_RDISC;
+ if (ifs.int_addr == htonl(INADDR_LOOPBACK))
+ ifs.int_state |= IS_PASSIVE;
+ ifs.int_dstaddr = ifs.int_addr;
+ ifs.int_mask = HOST_MASK;
+ ifs.int_ripv1_mask = HOST_MASK;
+ ifs.int_std_mask = std_mask(ifs.int_dstaddr);
+ ifs.int_net = ntohl(ifs.int_dstaddr);
+ if (!foundloopback) {
+ foundloopback = 1;
+ loopaddr = ifs.int_addr;
+ loop_rts.rts_gate = loopaddr;
+ loop_rts.rts_router = loopaddr;
+ }
+
+ } else if (ifs.int_if_flags & IFF_POINTOPOINT) {
+ if (INFO_BRD(&info) == 0
+ || INFO_BRD(&info)->sa_family != AF_INET) {
+ if (iff_up(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_NODST))
+ msglog("%s has a bad"
+ " destination address",
+ ifs.int_name);
+ complaints |= COMP_NODST;
+ }
+ continue;
+ }
+ ifs.int_dstaddr = S_ADDR(INFO_BRD(&info));
+ if (ntohl(ifs.int_dstaddr)>>24 == 0
+ || ntohl(ifs.int_dstaddr)>>24 == 0xff) {
+ if (iff_up(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_NODST))
+ msglog("%s has a bad"
+ " destination address",
+ ifs.int_name);
+ complaints |= COMP_NODST;
+ }
+ continue;
+ }
+ ifs.int_mask = HOST_MASK;
+ ifs.int_ripv1_mask = ntohl(S_ADDR(INFO_MASK(&info)));
+ ifs.int_std_mask = std_mask(ifs.int_dstaddr);
+ ifs.int_net = ntohl(ifs.int_dstaddr);
+
+ } else {
+ if (INFO_MASK(&info) == 0) {
+ if (iff_up(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_NOMASK))
+ msglog("%s has no netmask",
+ ifs.int_name);
+ complaints |= COMP_NOMASK;
+ }
+ continue;
+ }
+ ifs.int_dstaddr = ifs.int_addr;
+ ifs.int_mask = ntohl(S_ADDR(INFO_MASK(&info)));
+ ifs.int_ripv1_mask = ifs.int_mask;
+ ifs.int_std_mask = std_mask(ifs.int_addr);
+ ifs.int_net = ntohl(ifs.int_addr) & ifs.int_mask;
+ if (ifs.int_mask != ifs.int_std_mask)
+ ifs.int_state |= IS_SUBNET;
+
+ if (ifs.int_if_flags & IFF_BROADCAST) {
+ if (INFO_BRD(&info) == 0) {
+ if (iff_up(ifs.int_if_flags)) {
+ if (!(prev_complaints
+ & COMP_NOBADR))
+ msglog("%s has"
+ "no broadcast address",
+ ifs.int_name);
+ complaints |= COMP_NOBADR;
+ }
+ continue;
+ }
+ ifs.int_brdaddr = S_ADDR(INFO_BRD(&info));
+ }
+ }
+ ifs.int_std_net = ifs.int_net & ifs.int_std_mask;
+ ifs.int_std_addr = htonl(ifs.int_std_net);
+
+ /* Use a minimum metric of one. Treat the interface metric
+ * (default 0) as an increment to the hop count of one.
+ *
+ * The metric obtained from the routing socket dump of
+ * interface addresses is wrong. It is not set by the
+ * SIOCSIFMETRIC ioctl.
+ */
+#ifdef SIOCGIFMETRIC
+ strncpy(ifr.ifr_name, ifs.int_name, sizeof(ifr.ifr_name));
+ if (ioctl(rt_sock, SIOCGIFMETRIC, &ifr) < 0) {
+ DBGERR(1, "ioctl(SIOCGIFMETRIC)");
+ ifs.int_metric = 0;
+ } else {
+ ifs.int_metric = ifr.ifr_metric;
+ }
+#else
+ ifs.int_metric = ifam->ifam_metric;
+#endif
+ if (ifs.int_metric > HOPCNT_INFINITY) {
+ ifs.int_metric = 0;
+ if (!(prev_complaints & COMP_BAD_METRIC)
+ && iff_up(ifs.int_if_flags)) {
+ complaints |= COMP_BAD_METRIC;
+ msglog("%s has a metric of %d",
+ ifs.int_name, ifs.int_metric);
+ }
+ }
+
+ /* See if this is a familiar interface.
+ * If so, stop worrying about it if it is the same.
+ * Start it over if it now is to somewhere else, as happens
+ * frequently with PPP and SLIP.
+ */
+ ifp = ifwithname(ifs.int_name, ((ifs.int_state & IS_ALIAS)
+ ? ifs.int_addr
+ : 0));
+ if (ifp != 0) {
+ ifp->int_state |= IS_CHECKED;
+
+ if (0 != ((ifp->int_if_flags ^ ifs.int_if_flags)
+ & (IFF_BROADCAST
+ | IFF_LOOPBACK
+ | IFF_POINTOPOINT
+ | IFF_MULTICAST))
+ || 0 != ((ifp->int_state ^ ifs.int_state)
+ & IS_ALIAS)
+ || ifp->int_addr != ifs.int_addr
+ || ifp->int_brdaddr != ifs.int_brdaddr
+ || ifp->int_dstaddr != ifs.int_dstaddr
+ || ifp->int_mask != ifs.int_mask
+ || ifp->int_metric != ifs.int_metric) {
+ /* Forget old information about
+ * a changed interface.
+ */
+ trace_act("interface %s has changed",
+ ifp->int_name);
+ ifdel(ifp);
+ ifp = 0;
+ }
+ }
+
+ if (ifp != 0) {
+ /* The primary representative of an alias worries
+ * about how things are working.
+ */
+ if (ifp->int_state & IS_ALIAS)
+ continue;
+
+ /* note interfaces that have been turned off
+ */
+ if (!iff_up(ifs.int_if_flags)) {
+ if (iff_up(ifp->int_if_flags)) {
+ msglog("interface %s to %s turned off",
+ ifp->int_name,
+ naddr_ntoa(ifp->int_dstaddr));
+ if_bad(ifp);
+ ifp->int_if_flags &= ~IFF_UP;
+ } else if (now.tv_sec>(ifp->int_data.ts
+ + CHECK_BAD_INTERVAL)) {
+ trace_act("interface %s has been off"
+ " %ld seconds; forget it",
+ ifp->int_name,
+ now.tv_sec-ifp->int_data.ts);
+ ifdel(ifp);
+ }
+ continue;
+ }
+ /* or that were off and are now ok */
+ if (!iff_up(ifp->int_if_flags)) {
+ ifp->int_if_flags |= IFF_UP;
+ (void)if_ok(ifp, "");
+ }
+
+ /* If it has been long enough,
+ * see if the interface is broken.
+ */
+ if (now.tv_sec < ifp->int_data.ts+CHECK_BAD_INTERVAL)
+ continue;
+
+ in = ifs.int_data.ipackets - ifp->int_data.ipackets;
+ ierr = ifs.int_data.ierrors - ifp->int_data.ierrors;
+ out = ifs.int_data.opackets - ifp->int_data.opackets;
+ oerr = ifs.int_data.oerrors - ifp->int_data.oerrors;
+#ifdef sgi
+ /* Through at least IRIX 6.2, PPP and SLIP
+ * count packets dropped by the filters.
+ * But FDDI rings stuck non-operational count
+ * dropped packets as they wait for improvement.
+ */
+ if (!(ifp->int_if_flags & IFF_POINTOPOINT))
+ oerr += (ifs.int_data.odrops
+ - ifp->int_data.odrops);
+#endif
+ /* If the interface just awoke, restart the counters.
+ */
+ if (ifp->int_data.ts == 0) {
+ ifp->int_data = ifs.int_data;
+ continue;
+ }
+ ifp->int_data = ifs.int_data;
+
+ /* Withhold judgment when the short error
+ * counters wrap or the interface is reset.
+ */
+ if (ierr < 0 || in < 0 || oerr < 0 || out < 0) {
+ LIM_SEC(ifinit_timer,
+ now.tv_sec+CHECK_BAD_INTERVAL);
+ continue;
+ }
+
+ /* Withhold judgement when there is no traffic
+ */
+ if (in == 0 && out == 0 && ierr == 0 && oerr == 0)
+ continue;
+
+ /* It is bad if input or output is not working.
+ * Require presistent problems before marking it dead.
+ */
+ if ((in <= ierr && ierr > 0)
+ || (out <= oerr && oerr > 0)) {
+ if (!(ifp->int_state & IS_SICK)) {
+ trace_act("interface %s to %s"
+ " sick: in=%d ierr=%d"
+ " out=%d oerr=%d",
+ ifp->int_name,
+ naddr_ntoa(ifp->int_dstaddr),
+ in, ierr, out, oerr);
+ if_sick(ifp);
+ continue;
+ }
+ if (!(ifp->int_state & IS_BROKE)) {
+ msglog("interface %s to %s broken:"
+ " in=%d ierr=%d out=%d oerr=%d",
+ ifp->int_name,
+ naddr_ntoa(ifp->int_dstaddr),
+ in, ierr, out, oerr);
+ if_bad(ifp);
+ }
+ continue;
+ }
+
+ /* otherwise, it is active and healthy
+ */
+ ifp->int_act_time = now.tv_sec;
+ (void)if_ok(ifp, "");
+ continue;
+ }
+
+ /* This is a new interface.
+ * If it is dead, forget it.
+ */
+ if (!iff_up(ifs.int_if_flags))
+ continue;
+
+ /* If it duplicates an existing interface,
+ * complain about it, mark the other one
+ * duplicated, and forget this one.
+ */
+ ifp = check_dup(ifs.int_addr,ifs.int_dstaddr,ifs.int_mask,
+ ifs.int_if_flags);
+ if (ifp != 0) {
+ /* Ignore duplicates of itself, caused by having
+ * IP aliases on the same network.
+ */
+ if (!strcmp(ifp->int_name, ifs.int_name))
+ continue;
+
+ if (!(prev_complaints & COMP_DUP)) {
+ complaints |= COMP_DUP;
+ msglog("%s (%s%s%s) is duplicated by"
+ " %s (%s%s%s)",
+ ifs.int_name,
+ addrname(ifs.int_addr,ifs.int_mask,1),
+ ((ifs.int_if_flags & IFF_POINTOPOINT)
+ ? "-->" : ""),
+ ((ifs.int_if_flags & IFF_POINTOPOINT)
+ ? naddr_ntoa(ifs.int_dstaddr) : ""),
+ ifp->int_name,
+ addrname(ifp->int_addr,ifp->int_mask,1),
+ ((ifp->int_if_flags & IFF_POINTOPOINT)
+ ? "-->" : ""),
+ ((ifp->int_if_flags & IFF_POINTOPOINT)
+ ? naddr_ntoa(ifp->int_dstaddr) : ""));
+ }
+ ifp->int_state |= IS_DUP;
+ continue;
+ }
+
+ if (0 == (ifs.int_if_flags & (IFF_POINTOPOINT | IFF_BROADCAST | IFF_LOOPBACK))) {
+ trace_act("%s is neither broadcast, point-to-point,"
+ " nor loopback",
+ ifs.int_name);
+ if (!(ifs.int_state & IFF_MULTICAST))
+ ifs.int_state |= IS_NO_RDISC;
+ }
+
+
+ /* It is new and ok. Add it to the list of interfaces
+ */
+ ifp = (struct interface *)rtmalloc(sizeof(*ifp), "ifinit ifp");
+ memcpy(ifp, &ifs, sizeof(*ifp));
+ get_parms(ifp);
+ if_link(ifp);
+ trace_if("Add", ifp);
+
+ /* Notice likely bad netmask.
+ */
+ if (!(prev_complaints & COMP_NETMASK)
+ && !(ifp->int_if_flags & IFF_POINTOPOINT)
+ && ifp->int_addr != RIP_DEFAULT) {
+ for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) {
+ if (ifp1->int_mask == ifp->int_mask)
+ continue;
+ if (ifp1->int_if_flags & IFF_POINTOPOINT)
+ continue;
+ if (ifp1->int_dstaddr == RIP_DEFAULT)
+ continue;
+ /* ignore aliases on the right network */
+ if (!strcmp(ifp->int_name, ifp1->int_name))
+ continue;
+ if (on_net(ifp->int_dstaddr,
+ ifp1->int_net, ifp1->int_mask)
+ || on_net(ifp1->int_dstaddr,
+ ifp->int_net, ifp->int_mask)) {
+ msglog("possible netmask problem"
+ " between %s:%s and %s:%s",
+ ifp->int_name,
+ addrname(htonl(ifp->int_net),
+ ifp->int_mask, 1),
+ ifp1->int_name,
+ addrname(htonl(ifp1->int_net),
+ ifp1->int_mask, 1));
+ complaints |= COMP_NETMASK;
+ }
+ }
+ }
+
+ if (!(ifp->int_state & IS_ALIAS)) {
+ /* Count the # of directly connected networks.
+ */
+ if (!(ifp->int_if_flags & IFF_LOOPBACK))
+ tot_interfaces++;
+ if (!IS_RIP_OFF(ifp->int_state))
+ rip_interfaces++;
+
+ /* turn on router discovery and RIP If needed */
+ if_ok_rdisc(ifp);
+ rip_on(ifp);
+ }
+ }
+
+ /* If we are multi-homed and have at least two interfaces
+ * listening to RIP, then output by default.
+ */
+ if (!supplier_set && rip_interfaces > 1)
+ set_supplier();
+
+ /* If we are multi-homed, optionally advertise a route to
+ * our main address.
+ */
+ if (advertise_mhome
+ || (tot_interfaces > 1
+ && mhome
+ && (ifp = ifwithaddr(myaddr, 0, 0)) != 0
+ && foundloopback)) {
+ advertise_mhome = 1;
+ rt = rtget(myaddr, HOST_MASK);
+ if (rt != 0) {
+ if (rt->rt_ifp != ifp
+ || rt->rt_router != loopaddr) {
+ rtdelete(rt);
+ rt = 0;
+ } else {
+ loop_rts.rts_ifp = ifp;
+ loop_rts.rts_metric = 0;
+ loop_rts.rts_time = rt->rt_time;
+ rtchange(rt, rt->rt_state | RS_MHOME,
+ &loop_rts, 0);
+ }
+ }
+ if (rt == 0) {
+ loop_rts.rts_ifp = ifp;
+ loop_rts.rts_metric = 0;
+ rtadd(myaddr, HOST_MASK, RS_MHOME, &loop_rts);
+ }
+ }
+
+ for (ifp = ifnet; ifp != 0; ifp = ifp1) {
+ ifp1 = ifp->int_next; /* because we may delete it */
+
+ /* Forget any interfaces that have disappeared.
+ */
+ if (!(ifp->int_state & (IS_CHECKED | IS_REMOTE))) {
+ trace_act("interface %s has disappeared",
+ ifp->int_name);
+ ifdel(ifp);
+ continue;
+ }
+
+ if ((ifp->int_state & IS_BROKE)
+ && !(ifp->int_state & IS_PASSIVE))
+ LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL);
+
+ /* If we ever have a RIPv1 interface, assume we always will.
+ * It might come back if it ever goes away.
+ */
+ if (!(ifp->int_state & IS_NO_RIPV1_OUT) && supplier)
+ have_ripv1_out = 1;
+ if (!(ifp->int_state & IS_NO_RIPV1_IN))
+ have_ripv1_in = 1;
+ }
+
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
+ /* Ensure there is always a network route for interfaces,
+ * after any dead interfaces have been deleted, which
+ * might affect routes for point-to-point links.
+ */
+ if (!addrouteforif(ifp))
+ continue;
+
+ /* Add routes to the local end of point-to-point interfaces
+ * using loopback.
+ */
+ if ((ifp->int_if_flags & IFF_POINTOPOINT)
+ && !(ifp->int_state & IS_REMOTE)
+ && foundloopback) {
+ /* Delete any routes to the network address through
+ * foreign routers. Remove even static routes.
+ */
+ del_static(ifp->int_addr, HOST_MASK, 0, 0);
+ rt = rtget(ifp->int_addr, HOST_MASK);
+ if (rt != 0 && rt->rt_router != loopaddr) {
+ rtdelete(rt);
+ rt = 0;
+ }
+ if (rt != 0) {
+ if (!(rt->rt_state & RS_LOCAL)
+ || rt->rt_metric > ifp->int_metric) {
+ ifp1 = ifp;
+ } else {
+ ifp1 = rt->rt_ifp;
+ }
+ loop_rts.rts_ifp = ifp1;
+ loop_rts.rts_metric = 0;
+ loop_rts.rts_time = rt->rt_time;
+ rtchange(rt, ((rt->rt_state & ~RS_NET_SYN)
+ | (RS_IF|RS_LOCAL)),
+ &loop_rts, 0);
+ } else {
+ loop_rts.rts_ifp = ifp;
+ loop_rts.rts_metric = 0;
+ rtadd(ifp->int_addr, HOST_MASK,
+ (RS_IF | RS_LOCAL), &loop_rts);
+ }
+ }
+ }
+
+ /* add the authority routes */
+ for (intnetp = intnets; intnetp!=0; intnetp = intnetp->intnet_next) {
+ rt = rtget(intnetp->intnet_addr, intnetp->intnet_mask);
+ if (rt != 0
+ && !(rt->rt_state & RS_NO_NET_SYN)
+ && !(rt->rt_state & RS_NET_INT)) {
+ rtdelete(rt);
+ rt = 0;
+ }
+ if (rt == 0) {
+ loop_rts.rts_ifp = 0;
+ loop_rts.rts_metric = intnetp->intnet_metric-1;
+ rtadd(intnetp->intnet_addr, intnetp->intnet_mask,
+ RS_NET_SYN | RS_NET_INT, &loop_rts);
+ }
+ }
+
+ prev_complaints = complaints;
+}
+
+
+static void
+check_net_syn(struct interface *ifp)
+{
+ struct rt_entry *rt;
+ static struct rt_spare new;
+
+
+ /* Turn on the need to automatically synthesize a network route
+ * for this interface only if we are running RIPv1 on some other
+ * interface that is on a different class-A,B,or C network.
+ */
+ if (have_ripv1_out || have_ripv1_in) {
+ ifp->int_state |= IS_NEED_NET_SYN;
+ rt = rtget(ifp->int_std_addr, ifp->int_std_mask);
+ if (rt != 0
+ && 0 == (rt->rt_state & RS_NO_NET_SYN)
+ && (!(rt->rt_state & RS_NET_SYN)
+ || rt->rt_metric > ifp->int_metric)) {
+ rtdelete(rt);
+ rt = 0;
+ }
+ if (rt == 0) {
+ new.rts_ifp = ifp;
+ new.rts_gate = ifp->int_addr;
+ new.rts_router = ifp->int_addr;
+ new.rts_metric = ifp->int_metric;
+ rtadd(ifp->int_std_addr, ifp->int_std_mask,
+ RS_NET_SYN, &new);
+ }
+
+ } else {
+ ifp->int_state &= ~IS_NEED_NET_SYN;
+
+ rt = rtget(ifp->int_std_addr,
+ ifp->int_std_mask);
+ if (rt != 0
+ && (rt->rt_state & RS_NET_SYN)
+ && rt->rt_ifp == ifp)
+ rtbad_sub(rt);
+ }
+}
+
+
+/* Add route for interface if not currently installed.
+ * Create route to other end if a point-to-point link,
+ * otherwise a route to this (sub)network.
+ */
+int /* 0=bad interface */
+addrouteforif(struct interface *ifp)
+{
+ struct rt_entry *rt;
+ static struct rt_spare new;
+ naddr dst;
+
+
+ /* skip sick interfaces
+ */
+ if (ifp->int_state & IS_BROKE)
+ return 0;
+
+ /* If the interface on a subnet, then install a RIPv1 route to
+ * the network as well (unless it is sick).
+ */
+ if (ifp->int_state & IS_SUBNET)
+ check_net_syn(ifp);
+
+ dst = (0 != (ifp->int_if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK))
+ ? ifp->int_dstaddr
+ : htonl(ifp->int_net));
+
+ new.rts_ifp = ifp;
+ new.rts_router = ifp->int_addr;
+ new.rts_gate = ifp->int_addr;
+ new.rts_metric = ifp->int_metric;
+ new.rts_time = now.tv_sec;
+
+ /* If we are going to send packets to the gateway,
+ * it must be reachable using our physical interfaces
+ */
+ if ((ifp->int_state & IS_REMOTE)
+ && !(ifp->int_state & IS_EXTERNAL)
+ && !check_remote(ifp))
+ return 0;
+
+ /* We are finished if the correct main interface route exists.
+ * The right route must be for the right interface, not synthesized
+ * from a subnet, be a "gateway" or not as appropriate, and so forth.
+ */
+ del_static(dst, ifp->int_mask, 0, 0);
+ rt = rtget(dst, ifp->int_mask);
+ if (rt != 0) {
+ if ((rt->rt_ifp != ifp
+ || rt->rt_router != ifp->int_addr)
+ && (!(ifp->int_state & IS_DUP)
+ || rt->rt_ifp == 0
+ || (rt->rt_ifp->int_state & IS_BROKE))) {
+ rtdelete(rt);
+ rt = 0;
+ } else {
+ rtchange(rt, ((rt->rt_state | RS_IF)
+ & ~(RS_NET_SYN | RS_LOCAL)),
+ &new, 0);
+ }
+ }
+ if (rt == 0) {
+ if (ifp->int_transitions++ > 0)
+ trace_act("re-install interface %s",
+ ifp->int_name);
+
+ rtadd(dst, ifp->int_mask, RS_IF, &new);
+ }
+
+ return 1;
+}
diff --git a/sbin/routed/input.c b/sbin/routed/input.c
new file mode 100644
index 0000000..f655346
--- /dev/null
+++ b/sbin/routed/input.c
@@ -0,0 +1,1013 @@
+/*
+ * Copyright (c) 1983, 1988, 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.
+ * 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+
+#ifdef __NetBSD__
+__RCSID("$NetBSD$");
+#elif defined(__FreeBSD__)
+__RCSID("$FreeBSD$");
+#else
+__RCSID("$Revision: 2.26 $");
+#ident "$Revision: 2.26 $"
+#endif
+
+static void input(struct sockaddr_in *, struct interface *, struct interface *,
+ struct rip *, int);
+static void input_route(naddr, naddr, struct rt_spare *, struct netinfo *);
+static int ck_passwd(struct interface *, struct rip *, void *,
+ naddr, struct msg_limit *);
+
+
+/* process RIP input
+ */
+void
+read_rip(int sock,
+ struct interface *sifp)
+{
+ struct sockaddr_in from;
+ struct interface *aifp;
+ socklen_t fromlen;
+ int cc;
+#ifdef USE_PASSIFNAME
+ static struct msg_limit bad_name;
+ struct {
+ char ifname[IFNAMSIZ];
+ union pkt_buf pbuf;
+ } inbuf;
+#else
+ struct {
+ union pkt_buf pbuf;
+ } inbuf;
+#endif
+
+
+ for (;;) {
+ fromlen = sizeof(from);
+ cc = recvfrom(sock, &inbuf, sizeof(inbuf), 0,
+ (struct sockaddr*)&from, &fromlen);
+ if (cc <= 0) {
+ if (cc < 0 && errno != EWOULDBLOCK)
+ LOGERR("recvfrom(rip)");
+ break;
+ }
+ if (fromlen != sizeof(struct sockaddr_in))
+ logbad(1,"impossible recvfrom(rip) fromlen=%d",
+ (int)fromlen);
+
+ /* aifp is the "authenticated" interface via which the packet
+ * arrived. In fact, it is only the interface on which
+ * the packet should have arrived based on is source
+ * address.
+ * sifp is interface associated with the socket through which
+ * the packet was received.
+ */
+#ifdef USE_PASSIFNAME
+ if ((cc -= sizeof(inbuf.ifname)) < 0)
+ logbad(0,"missing USE_PASSIFNAME; only %d bytes",
+ cc+sizeof(inbuf.ifname));
+
+ /* check the remote interfaces first */
+ for (aifp = remote_if; aifp; aifp = aifp->int_rlink) {
+ if (aifp->int_addr == from.sin_addr.s_addr)
+ break;
+ }
+ if (aifp == 0) {
+ aifp = ifwithname(inbuf.ifname, 0);
+ if (aifp == 0) {
+ msglim(&bad_name, from.sin_addr.s_addr,
+ "impossible interface name %.*s",
+ IFNAMSIZ, inbuf.ifname);
+ } else if (((aifp->int_if_flags & IFF_POINTOPOINT)
+ && aifp->int_dstaddr!=from.sin_addr.s_addr)
+ || (!(aifp->int_if_flags & IFF_POINTOPOINT)
+ && !on_net(from.sin_addr.s_addr,
+ aifp->int_net,
+ aifp->int_mask))) {
+ /* If it came via the wrong interface, do not
+ * trust it.
+ */
+ aifp = 0;
+ }
+ }
+#else
+ aifp = iflookup(from.sin_addr.s_addr);
+#endif
+ if (sifp == 0)
+ sifp = aifp;
+
+ input(&from, sifp, aifp, &inbuf.pbuf.rip, cc);
+ }
+}
+
+
+/* Process a RIP packet
+ */
+static void
+input(struct sockaddr_in *from, /* received from this IP address */
+ struct interface *sifp, /* interface of incoming socket */
+ struct interface *aifp, /* "authenticated" interface */
+ struct rip *rip,
+ int cc)
+{
+# define FROM_NADDR from->sin_addr.s_addr
+ static struct msg_limit use_auth, bad_len, bad_mask;
+ static struct msg_limit unk_router, bad_router, bad_nhop;
+
+ struct rt_entry *rt;
+ struct rt_spare new;
+ struct netinfo *n, *lim;
+ struct interface *ifp1;
+ naddr gate, mask, v1_mask, dst, ddst_h = 0;
+ struct auth *ap;
+ struct tgate *tg = 0;
+ struct tgate_net *tn;
+ int i, j;
+
+ /* Notice when we hear from a remote gateway
+ */
+ if (aifp != 0
+ && (aifp->int_state & IS_REMOTE))
+ aifp->int_act_time = now.tv_sec;
+
+ trace_rip("Recv", "from", from, sifp, rip, cc);
+
+ if (rip->rip_vers == 0) {
+ msglim(&bad_router, FROM_NADDR,
+ "RIP version 0, cmd %d, packet received from %s",
+ rip->rip_cmd, naddr_ntoa(FROM_NADDR));
+ return;
+ } else if (rip->rip_vers > RIPv2) {
+ rip->rip_vers = RIPv2;
+ }
+ if (cc > (int)OVER_MAXPACKETSIZE) {
+ msglim(&bad_router, FROM_NADDR,
+ "packet at least %d bytes too long received from %s",
+ cc-MAXPACKETSIZE, naddr_ntoa(FROM_NADDR));
+ return;
+ }
+
+ n = rip->rip_nets;
+ lim = (struct netinfo *)((char*)rip + cc);
+
+ /* Notice authentication.
+ * As required by section 4.2 in RFC 1723, discard authenticated
+ * RIPv2 messages, but only if configured for that silliness.
+ *
+ * RIPv2 authentication is lame. Why authenticate queries?
+ * Why should a RIPv2 implementation with authentication disabled
+ * not be able to listen to RIPv2 packets with authentication, while
+ * RIPv1 systems will listen? Crazy!
+ */
+ if (!auth_ok
+ && rip->rip_vers == RIPv2
+ && n < lim && n->n_family == RIP_AF_AUTH) {
+ msglim(&use_auth, FROM_NADDR,
+ "RIPv2 message with authentication from %s discarded",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+
+ switch (rip->rip_cmd) {
+ case RIPCMD_REQUEST:
+ /* For mere requests, be a little sloppy about the source
+ */
+ if (aifp == 0)
+ aifp = sifp;
+
+ /* Are we talking to ourself or a remote gateway?
+ */
+ ifp1 = ifwithaddr(FROM_NADDR, 0, 1);
+ if (ifp1) {
+ if (ifp1->int_state & IS_REMOTE) {
+ /* remote gateway */
+ aifp = ifp1;
+ if (check_remote(aifp)) {
+ aifp->int_act_time = now.tv_sec;
+ (void)if_ok(aifp, "remote ");
+ }
+ } else if (from->sin_port == htons(RIP_PORT)) {
+ trace_pkt(" discard our own RIP request");
+ return;
+ }
+ }
+
+ /* did the request come from a router?
+ */
+ if (from->sin_port == htons(RIP_PORT)) {
+ /* yes, ignore the request if RIP is off so that
+ * the router does not depend on us.
+ */
+ if (rip_sock < 0
+ || (aifp != 0
+ && IS_RIP_OUT_OFF(aifp->int_state))) {
+ trace_pkt(" discard request while RIP off");
+ return;
+ }
+ }
+
+ /* According to RFC 1723, we should ignore unauthenticated
+ * queries. That is too silly to bother with. Sheesh!
+ * Are forwarding tables supposed to be secret, when
+ * a bad guy can infer them with test traffic? When RIP
+ * is still the most common router-discovery protocol
+ * and so hosts need to send queries that will be answered?
+ * What about `rtquery`?
+ * Maybe on firewalls you'd care, but not enough to
+ * give up the diagnostic facilities of remote probing.
+ */
+
+ if (n >= lim) {
+ msglim(&bad_len, FROM_NADDR, "empty request from %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ if (cc%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) {
+ msglim(&bad_len, FROM_NADDR,
+ "request of bad length (%d) from %s",
+ cc, naddr_ntoa(FROM_NADDR));
+ }
+
+ if (rip->rip_vers == RIPv2
+ && (aifp == 0 || (aifp->int_state & IS_NO_RIPV1_OUT))) {
+ v12buf.buf->rip_vers = RIPv2;
+ /* If we have a secret but it is a cleartext secret,
+ * do not disclose our secret unless the other guy
+ * already knows it.
+ */
+ ap = find_auth(aifp);
+ if (ap != 0 && ap->type == RIP_AUTH_PW
+ && n->n_family == RIP_AF_AUTH
+ && !ck_passwd(aifp,rip,lim,FROM_NADDR,&use_auth))
+ ap = 0;
+ } else {
+ v12buf.buf->rip_vers = RIPv1;
+ ap = 0;
+ }
+ clr_ws_buf(&v12buf, ap);
+
+ do {
+ n->n_metric = ntohl(n->n_metric);
+
+ /* A single entry with family RIP_AF_UNSPEC and
+ * metric HOPCNT_INFINITY means "all routes".
+ * We respond to routers only if we are acting
+ * as a supplier, or to anyone other than a router
+ * (i.e. a query).
+ */
+ if (n->n_family == RIP_AF_UNSPEC
+ && n->n_metric == HOPCNT_INFINITY) {
+ /* Answer a query from a utility program
+ * with all we know.
+ */
+ if (from->sin_port != htons(RIP_PORT)) {
+ supply(from, aifp, OUT_QUERY, 0,
+ rip->rip_vers, ap != 0);
+ return;
+ }
+
+ /* A router trying to prime its tables.
+ * Filter the answer in the about same way
+ * broadcasts are filtered.
+ *
+ * Only answer a router if we are a supplier
+ * to keep an unwary host that is just starting
+ * from picking us as a router.
+ */
+ if (aifp == 0) {
+ trace_pkt("ignore distant router");
+ return;
+ }
+ if (!supplier
+ || IS_RIP_OFF(aifp->int_state)) {
+ trace_pkt("ignore; not supplying");
+ return;
+ }
+
+ /* Do not answer a RIPv1 router if
+ * we are sending RIPv2. But do offer
+ * poor man's router discovery.
+ */
+ if ((aifp->int_state & IS_NO_RIPV1_OUT)
+ && rip->rip_vers == RIPv1) {
+ if (!(aifp->int_state & IS_PM_RDISC)) {
+ trace_pkt("ignore; sending RIPv2");
+ return;
+ }
+
+ v12buf.n->n_family = RIP_AF_INET;
+ v12buf.n->n_dst = RIP_DEFAULT;
+ i = aifp->int_d_metric;
+ if (0 != (rt = rtget(RIP_DEFAULT, 0))) {
+ j = (rt->rt_metric
+ +aifp->int_metric
+ +aifp->int_adj_outmetric
+ +1);
+ if (i > j)
+ i = j;
+ }
+ v12buf.n->n_metric = htonl(i);
+ v12buf.n++;
+ break;
+ }
+
+ /* Respond with RIPv1 instead of RIPv2 if
+ * that is what we are broadcasting on the
+ * interface to keep the remote router from
+ * getting the wrong initial idea of the
+ * routes we send.
+ */
+ supply(from, aifp, OUT_UNICAST, 0,
+ (aifp->int_state & IS_NO_RIPV1_OUT)
+ ? RIPv2 : RIPv1,
+ ap != 0);
+ return;
+ }
+
+ /* Ignore authentication */
+ if (n->n_family == RIP_AF_AUTH)
+ continue;
+
+ if (n->n_family != RIP_AF_INET) {
+ msglim(&bad_router, FROM_NADDR,
+ "request from %s for unsupported"
+ " (af %d) %s",
+ naddr_ntoa(FROM_NADDR),
+ ntohs(n->n_family),
+ naddr_ntoa(n->n_dst));
+ return;
+ }
+
+ /* We are being asked about a specific destination.
+ */
+ dst = n->n_dst;
+ if (!check_dst(dst)) {
+ msglim(&bad_router, FROM_NADDR,
+ "bad queried destination %s from %s",
+ naddr_ntoa(dst),
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+
+ /* decide what mask was intended */
+ if (rip->rip_vers == RIPv1
+ || 0 == (mask = ntohl(n->n_mask))
+ || 0 != (ntohl(dst) & ~mask))
+ mask = ripv1_mask_host(dst, aifp);
+
+ /* try to find the answer */
+ rt = rtget(dst, mask);
+ if (!rt && dst != RIP_DEFAULT)
+ rt = rtfind(n->n_dst);
+
+ if (v12buf.buf->rip_vers != RIPv1)
+ v12buf.n->n_mask = mask;
+ if (rt == 0) {
+ /* we do not have the answer */
+ v12buf.n->n_metric = HOPCNT_INFINITY;
+ } else {
+ /* we have the answer, so compute the
+ * right metric and next hop.
+ */
+ v12buf.n->n_family = RIP_AF_INET;
+ v12buf.n->n_dst = dst;
+ j = rt->rt_metric+1;
+ if (!aifp)
+ ++j;
+ else
+ j += (aifp->int_metric
+ + aifp->int_adj_outmetric);
+ if (j < HOPCNT_INFINITY)
+ v12buf.n->n_metric = j;
+ else
+ v12buf.n->n_metric = HOPCNT_INFINITY;
+ if (v12buf.buf->rip_vers != RIPv1) {
+ v12buf.n->n_tag = rt->rt_tag;
+ v12buf.n->n_mask = mask;
+ if (aifp != 0
+ && on_net(rt->rt_gate,
+ aifp->int_net,
+ aifp->int_mask)
+ && rt->rt_gate != aifp->int_addr)
+ v12buf.n->n_nhop = rt->rt_gate;
+ }
+ }
+ v12buf.n->n_metric = htonl(v12buf.n->n_metric);
+
+ /* Stop paying attention if we fill the output buffer.
+ */
+ if (++v12buf.n >= v12buf.lim)
+ break;
+ } while (++n < lim);
+
+ /* Send the answer about specific routes.
+ */
+ if (ap != 0 && ap->type == RIP_AUTH_MD5)
+ end_md5_auth(&v12buf, ap);
+
+ if (from->sin_port != htons(RIP_PORT)) {
+ /* query */
+ (void)output(OUT_QUERY, from, aifp,
+ v12buf.buf,
+ ((char *)v12buf.n - (char*)v12buf.buf));
+ } else if (supplier) {
+ (void)output(OUT_UNICAST, from, aifp,
+ v12buf.buf,
+ ((char *)v12buf.n - (char*)v12buf.buf));
+ } else {
+ /* Only answer a router if we are a supplier
+ * to keep an unwary host that is just starting
+ * from picking us an a router.
+ */
+ ;
+ }
+ return;
+
+ case RIPCMD_TRACEON:
+ case RIPCMD_TRACEOFF:
+ /* Notice that trace messages are turned off for all possible
+ * abuse if _PATH_TRACE is undefined in pathnames.h.
+ * Notice also that because of the way the trace file is
+ * handled in trace.c, no abuse is plausible even if
+ * _PATH_TRACE_ is defined.
+ *
+ * First verify message came from a privileged port. */
+ if (ntohs(from->sin_port) > IPPORT_RESERVED) {
+ msglog("trace command from untrusted port on %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ if (aifp == 0) {
+ msglog("trace command from unknown router %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ if (rip->rip_cmd == RIPCMD_TRACEON) {
+ rip->rip_tracefile[cc-4] = '\0';
+ set_tracefile((char*)rip->rip_tracefile,
+ "trace command: %s\n", 0);
+ } else {
+ trace_off("tracing turned off by %s",
+ naddr_ntoa(FROM_NADDR));
+ }
+ return;
+
+ case RIPCMD_RESPONSE:
+ if (cc%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) {
+ msglim(&bad_len, FROM_NADDR,
+ "response of bad length (%d) from %s",
+ cc, naddr_ntoa(FROM_NADDR));
+ }
+
+ /* verify message came from a router */
+ if (from->sin_port != ntohs(RIP_PORT)) {
+ msglim(&bad_router, FROM_NADDR,
+ " discard RIP response from unknown port"
+ " %d on %s",
+ ntohs(from->sin_port), naddr_ntoa(FROM_NADDR));
+ return;
+ }
+
+ if (rip_sock < 0) {
+ trace_pkt(" discard response while RIP off");
+ return;
+ }
+
+ /* Are we talking to ourself or a remote gateway?
+ */
+ ifp1 = ifwithaddr(FROM_NADDR, 0, 1);
+ if (ifp1) {
+ if (ifp1->int_state & IS_REMOTE) {
+ /* remote gateway */
+ aifp = ifp1;
+ if (check_remote(aifp)) {
+ aifp->int_act_time = now.tv_sec;
+ (void)if_ok(aifp, "remote ");
+ }
+ } else {
+ trace_pkt(" discard our own RIP response");
+ return;
+ }
+ }
+
+ /* Accept routing packets from routers directly connected
+ * via broadcast or point-to-point networks, and from
+ * those listed in /etc/gateways.
+ */
+ if (aifp == 0) {
+ msglim(&unk_router, FROM_NADDR,
+ " discard response from %s"
+ " via unexpected interface",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ if (IS_RIP_IN_OFF(aifp->int_state)) {
+ trace_pkt(" discard RIPv%d response"
+ " via disabled interface %s",
+ rip->rip_vers, aifp->int_name);
+ return;
+ }
+
+ if (n >= lim) {
+ msglim(&bad_len, FROM_NADDR, "empty response from %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+
+ if (((aifp->int_state & IS_NO_RIPV1_IN)
+ && rip->rip_vers == RIPv1)
+ || ((aifp->int_state & IS_NO_RIPV2_IN)
+ && rip->rip_vers != RIPv1)) {
+ trace_pkt(" discard RIPv%d response",
+ rip->rip_vers);
+ return;
+ }
+
+ /* Ignore routes via dead interface.
+ */
+ if (aifp->int_state & IS_BROKE) {
+ trace_pkt("discard response via broken interface %s",
+ aifp->int_name);
+ return;
+ }
+
+ /* If the interface cares, ignore bad routers.
+ * Trace but do not log this problem, because where it
+ * happens, it happens frequently.
+ */
+ if (aifp->int_state & IS_DISTRUST) {
+ tg = tgates;
+ while (tg->tgate_addr != FROM_NADDR) {
+ tg = tg->tgate_next;
+ if (tg == 0) {
+ trace_pkt(" discard RIP response"
+ " from untrusted router %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ }
+ }
+
+ /* Authenticate the packet if we have a secret.
+ * If we do not have any secrets, ignore the error in
+ * RFC 1723 and accept it regardless.
+ */
+ if (aifp->int_auth[0].type != RIP_AUTH_NONE
+ && rip->rip_vers != RIPv1
+ && !ck_passwd(aifp,rip,lim,FROM_NADDR,&use_auth))
+ return;
+
+ do {
+ if (n->n_family == RIP_AF_AUTH)
+ continue;
+
+ n->n_metric = ntohl(n->n_metric);
+ dst = n->n_dst;
+ if (n->n_family != RIP_AF_INET
+ && (n->n_family != RIP_AF_UNSPEC
+ || dst != RIP_DEFAULT)) {
+ msglim(&bad_router, FROM_NADDR,
+ "route from %s to unsupported"
+ " address family=%d destination=%s",
+ naddr_ntoa(FROM_NADDR),
+ n->n_family,
+ naddr_ntoa(dst));
+ continue;
+ }
+ if (!check_dst(dst)) {
+ msglim(&bad_router, FROM_NADDR,
+ "bad destination %s from %s",
+ naddr_ntoa(dst),
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ if (n->n_metric == 0
+ || n->n_metric > HOPCNT_INFINITY) {
+ msglim(&bad_router, FROM_NADDR,
+ "bad metric %d from %s"
+ " for destination %s",
+ n->n_metric,
+ naddr_ntoa(FROM_NADDR),
+ naddr_ntoa(dst));
+ return;
+ }
+
+ /* Notice the next-hop.
+ */
+ gate = FROM_NADDR;
+ if (n->n_nhop != 0) {
+ if (rip->rip_vers == RIPv1) {
+ n->n_nhop = 0;
+ } else {
+ /* Use it only if it is valid. */
+ if (on_net(n->n_nhop,
+ aifp->int_net, aifp->int_mask)
+ && check_dst(n->n_nhop)) {
+ gate = n->n_nhop;
+ } else {
+ msglim(&bad_nhop, FROM_NADDR,
+ "router %s to %s"
+ " has bad next hop %s",
+ naddr_ntoa(FROM_NADDR),
+ naddr_ntoa(dst),
+ naddr_ntoa(n->n_nhop));
+ n->n_nhop = 0;
+ }
+ }
+ }
+
+ if (rip->rip_vers == RIPv1
+ || 0 == (mask = ntohl(n->n_mask))) {
+ mask = ripv1_mask_host(dst,aifp);
+ } else if ((ntohl(dst) & ~mask) != 0) {
+ msglim(&bad_mask, FROM_NADDR,
+ "router %s sent bad netmask"
+ " %#lx with %s",
+ naddr_ntoa(FROM_NADDR),
+ (u_long)mask,
+ naddr_ntoa(dst));
+ continue;
+ }
+ if (rip->rip_vers == RIPv1)
+ n->n_tag = 0;
+
+ /* Adjust metric according to incoming interface..
+ */
+ n->n_metric += (aifp->int_metric
+ + aifp->int_adj_inmetric);
+ if (n->n_metric > HOPCNT_INFINITY)
+ n->n_metric = HOPCNT_INFINITY;
+
+ /* Should we trust this route from this router? */
+ if (tg && (tn = tg->tgate_nets)->mask != 0) {
+ for (i = 0; i < MAX_TGATE_NETS; i++, tn++) {
+ if (on_net(dst, tn->net, tn->mask)
+ && tn->mask <= mask)
+ break;
+ }
+ if (i >= MAX_TGATE_NETS || tn->mask == 0) {
+ trace_pkt(" ignored unauthorized %s",
+ addrname(dst,mask,0));
+ continue;
+ }
+ }
+
+ /* Recognize and ignore a default route we faked
+ * which is being sent back to us by a machine with
+ * broken split-horizon.
+ * Be a little more paranoid than that, and reject
+ * default routes with the same metric we advertised.
+ */
+ if (aifp->int_d_metric != 0
+ && dst == RIP_DEFAULT
+ && (int)n->n_metric >= aifp->int_d_metric)
+ continue;
+
+ /* We can receive aggregated RIPv2 routes that must
+ * be broken down before they are transmitted by
+ * RIPv1 via an interface on a subnet.
+ * We might also receive the same routes aggregated
+ * via other RIPv2 interfaces.
+ * This could cause duplicate routes to be sent on
+ * the RIPv1 interfaces. "Longest matching variable
+ * length netmasks" lets RIPv2 listeners understand,
+ * but breaking down the aggregated routes for RIPv1
+ * listeners can produce duplicate routes.
+ *
+ * Breaking down aggregated routes here bloats
+ * the daemon table, but does not hurt the kernel
+ * table, since routes are always aggregated for
+ * the kernel.
+ *
+ * Notice that this does not break down network
+ * routes corresponding to subnets. This is part
+ * of the defense against RS_NET_SYN.
+ */
+ if (have_ripv1_out
+ && (((rt = rtget(dst,mask)) == 0
+ || !(rt->rt_state & RS_NET_SYN)))
+ && (v1_mask = ripv1_mask_net(dst,0)) > mask) {
+ ddst_h = v1_mask & -v1_mask;
+ i = (v1_mask & ~mask)/ddst_h;
+ if (i >= 511) {
+ /* Punt if we would have to generate
+ * an unreasonable number of routes.
+ */
+ if (TRACECONTENTS)
+ trace_misc("accept %s-->%s as 1"
+ " instead of %d routes",
+ addrname(dst,mask,0),
+ naddr_ntoa(FROM_NADDR),
+ i+1);
+ i = 0;
+ } else {
+ mask = v1_mask;
+ }
+ } else {
+ i = 0;
+ }
+
+ new.rts_gate = gate;
+ new.rts_router = FROM_NADDR;
+ new.rts_metric = n->n_metric;
+ new.rts_tag = n->n_tag;
+ new.rts_time = now.tv_sec;
+ new.rts_ifp = aifp;
+ new.rts_de_ag = i;
+ j = 0;
+ for (;;) {
+ input_route(dst, mask, &new, n);
+ if (++j > i)
+ break;
+ dst = htonl(ntohl(dst) + ddst_h);
+ }
+ } while (++n < lim);
+ break;
+ }
+#undef FROM_NADDR
+}
+
+
+/* Process a single input route.
+ */
+static void
+input_route(naddr dst, /* network order */
+ naddr mask,
+ struct rt_spare *new,
+ struct netinfo *n)
+{
+ int i;
+ struct rt_entry *rt;
+ struct rt_spare *rts, *rts0;
+ struct interface *ifp1;
+
+
+ /* See if the other guy is telling us to send our packets to him.
+ * Sometimes network routes arrive over a point-to-point link for
+ * the network containing the address(es) of the link.
+ *
+ * If our interface is broken, switch to using the other guy.
+ */
+ ifp1 = ifwithaddr(dst, 1, 1);
+ if (ifp1 != 0
+ && (!(ifp1->int_state & IS_BROKE)
+ || (ifp1->int_state & IS_PASSIVE)))
+ return;
+
+ /* Look for the route in our table.
+ */
+ rt = rtget(dst, mask);
+
+ /* Consider adding the route if we do not already have it.
+ */
+ if (rt == 0) {
+ /* Ignore unknown routes being poisoned.
+ */
+ if (new->rts_metric == HOPCNT_INFINITY)
+ return;
+
+ /* Ignore the route if it points to us */
+ if (n->n_nhop != 0
+ && 0 != ifwithaddr(n->n_nhop, 1, 0))
+ return;
+
+ /* If something has not gone crazy and tried to fill
+ * our memory, accept the new route.
+ */
+ if (total_routes < MAX_ROUTES)
+ rtadd(dst, mask, 0, new);
+ return;
+ }
+
+ /* We already know about the route. Consider this update.
+ *
+ * If (rt->rt_state & RS_NET_SYN), then this route
+ * is the same as a network route we have inferred
+ * for subnets we know, in order to tell RIPv1 routers
+ * about the subnets.
+ *
+ * It is impossible to tell if the route is coming
+ * from a distant RIPv2 router with the standard
+ * netmask because that router knows about the entire
+ * network, or if it is a round-about echo of a
+ * synthetic, RIPv1 network route of our own.
+ * The worst is that both kinds of routes might be
+ * received, and the bad one might have the smaller
+ * metric. Partly solve this problem by never
+ * aggregating into such a route. Also keep it
+ * around as long as the interface exists.
+ */
+
+ rts0 = rt->rt_spares;
+ for (rts = rts0, i = NUM_SPARES; i != 0; i--, rts++) {
+ if (rts->rts_router == new->rts_router)
+ break;
+ /* Note the worst slot to reuse,
+ * other than the current slot.
+ */
+ if (rts0 == rt->rt_spares
+ || BETTER_LINK(rt, rts0, rts))
+ rts0 = rts;
+ }
+ if (i != 0) {
+ /* Found a route from the router already in the table.
+ */
+
+ /* If the new route is a route broken down from an
+ * aggregated route, and if the previous route is either
+ * not a broken down route or was broken down from a finer
+ * netmask, and if the previous route is current,
+ * then forget this one.
+ */
+ if (new->rts_de_ag > rts->rts_de_ag
+ && now_stale <= rts->rts_time)
+ return;
+
+ /* Keep poisoned routes around only long enough to pass
+ * the poison on. Use a new timestamp for good routes.
+ */
+ if (rts->rts_metric == HOPCNT_INFINITY
+ && new->rts_metric == HOPCNT_INFINITY)
+ new->rts_time = rts->rts_time;
+
+ /* If this is an update for the router we currently prefer,
+ * then note it.
+ */
+ if (i == NUM_SPARES) {
+ rtchange(rt, rt->rt_state, new, 0);
+ /* If the route got worse, check for something better.
+ */
+ if (new->rts_metric > rts->rts_metric)
+ rtswitch(rt, 0);
+ return;
+ }
+
+ /* This is an update for a spare route.
+ * Finished if the route is unchanged.
+ */
+ if (rts->rts_gate == new->rts_gate
+ && rts->rts_metric == new->rts_metric
+ && rts->rts_tag == new->rts_tag) {
+ trace_upslot(rt, rts, new);
+ *rts = *new;
+ return;
+ }
+ /* Forget it if it has gone bad.
+ */
+ if (new->rts_metric == HOPCNT_INFINITY) {
+ rts_delete(rt, rts);
+ return;
+ }
+
+ } else {
+ /* The update is for a route we know about,
+ * but not from a familiar router.
+ *
+ * Ignore the route if it points to us.
+ */
+ if (n->n_nhop != 0
+ && 0 != ifwithaddr(n->n_nhop, 1, 0))
+ return;
+
+ /* the loop above set rts0=worst spare */
+ rts = rts0;
+
+ /* Save the route as a spare only if it has
+ * a better metric than our worst spare.
+ * This also ignores poisoned routes (those
+ * received with metric HOPCNT_INFINITY).
+ */
+ if (new->rts_metric >= rts->rts_metric)
+ return;
+ }
+
+ trace_upslot(rt, rts, new);
+ *rts = *new;
+
+ /* try to switch to a better route */
+ rtswitch(rt, rts);
+}
+
+
+static int /* 0 if bad */
+ck_passwd(struct interface *aifp,
+ struct rip *rip,
+ void *lim,
+ naddr from,
+ struct msg_limit *use_authp)
+{
+# define NA (rip->rip_auths)
+ struct netauth *na2;
+ struct auth *ap;
+ MD5_CTX md5_ctx;
+ u_char hash[RIP_AUTH_PW_LEN];
+ int i, len;
+
+
+ if ((void *)NA >= lim || NA->a_family != RIP_AF_AUTH) {
+ msglim(use_authp, from, "missing password from %s",
+ naddr_ntoa(from));
+ return 0;
+ }
+
+ /* accept any current (+/- 24 hours) password
+ */
+ for (ap = aifp->int_auth, i = 0; i < MAX_AUTH_KEYS; i++, ap++) {
+ if (ap->type != NA->a_type
+ || (u_long)ap->start > (u_long)clk.tv_sec+DAY
+ || (u_long)ap->end+DAY < (u_long)clk.tv_sec)
+ continue;
+
+ if (NA->a_type == RIP_AUTH_PW) {
+ if (!memcmp(NA->au.au_pw, ap->key, RIP_AUTH_PW_LEN))
+ return 1;
+
+ } else {
+ /* accept MD5 secret with the right key ID
+ */
+ if (NA->au.a_md5.md5_keyid != ap->keyid)
+ continue;
+
+ len = ntohs(NA->au.a_md5.md5_pkt_len);
+ if ((len-sizeof(*rip)) % sizeof(*NA) != 0
+ || len != (char *)lim-(char*)rip-(int)sizeof(*NA)) {
+ msglim(use_authp, from,
+ "wrong MD5 RIPv2 packet length of %d"
+ " instead of %d from %s",
+ len, (int)((char *)lim-(char *)rip
+ -sizeof(*NA)),
+ naddr_ntoa(from));
+ return 0;
+ }
+ na2 = (struct netauth *)((char *)rip+len);
+
+ /* Given a good hash value, these are not security
+ * problems so be generous and accept the routes,
+ * after complaining.
+ */
+ if (TRACEPACKETS) {
+ if (NA->au.a_md5.md5_auth_len
+ != RIP_AUTH_MD5_HASH_LEN)
+ msglim(use_authp, from,
+ "unknown MD5 RIPv2 auth len %#x"
+ " instead of %#x from %s",
+ NA->au.a_md5.md5_auth_len,
+ RIP_AUTH_MD5_HASH_LEN,
+ naddr_ntoa(from));
+ if (na2->a_family != RIP_AF_AUTH)
+ msglim(use_authp, from,
+ "unknown MD5 RIPv2 family %#x"
+ " instead of %#x from %s",
+ na2->a_family, RIP_AF_AUTH,
+ naddr_ntoa(from));
+ if (na2->a_type != ntohs(1))
+ msglim(use_authp, from,
+ "MD5 RIPv2 hash has %#x"
+ " instead of %#x from %s",
+ na2->a_type, ntohs(1),
+ naddr_ntoa(from));
+ }
+
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, (u_char *)rip,
+ len + RIP_AUTH_MD5_HASH_XTRA);
+ MD5Update(&md5_ctx, ap->key, RIP_AUTH_MD5_KEY_LEN);
+ MD5Final(hash, &md5_ctx);
+ if (!memcmp(hash, na2->au.au_pw, sizeof(hash)))
+ return 1;
+ }
+ }
+
+ msglim(use_authp, from, "bad password from %s",
+ naddr_ntoa(from));
+ return 0;
+#undef NA
+}
diff --git a/sbin/routed/main.c b/sbin/routed/main.c
new file mode 100644
index 0000000..722061e
--- /dev/null
+++ b/sbin/routed/main.c
@@ -0,0 +1,959 @@
+/*
+ * Copyright (c) 1983, 1988, 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.
+ * 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+#include "pathnames.h"
+#ifdef sgi
+#include "math.h"
+#endif
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/file.h>
+
+__COPYRIGHT("@(#) Copyright (c) 1983, 1988, 1993\n"
+ "The Regents of the University of California."
+ " All rights reserved.\n");
+#ifdef __NetBSD__
+__RCSID("$NetBSD$");
+#include <util.h>
+#elif defined(__FreeBSD__)
+__RCSID("$FreeBSD$");
+#else
+__RCSID("$Revision: 2.27 $");
+#ident "$Revision: 2.27 $"
+#endif
+
+pid_t mypid;
+
+naddr myaddr; /* system address */
+char myname[MAXHOSTNAMELEN+1];
+
+int verbose;
+
+int supplier; /* supply or broadcast updates */
+int supplier_set;
+int ipforwarding = 1; /* kernel forwarding on */
+
+int default_gateway; /* 1=advertise default */
+int background = 1;
+int ridhosts; /* 1=reduce host routes */
+int mhome; /* 1=want multi-homed host route */
+int advertise_mhome; /* 1=must continue advertising it */
+int auth_ok = 1; /* 1=ignore auth if we do not care */
+
+struct timeval epoch; /* when started */
+struct timeval clk, prev_clk;
+static int usec_fudge;
+struct timeval now; /* current idea of time */
+time_t now_stale;
+time_t now_expire;
+time_t now_garbage;
+
+struct timeval next_bcast; /* next general broadcast */
+struct timeval no_flash = { /* inhibit flash update */
+ EPOCH+SUPPLY_INTERVAL, 0
+};
+
+struct timeval flush_kern_timer;
+
+fd_set fdbits;
+int sock_max;
+int rip_sock = -1; /* RIP socket */
+struct interface *rip_sock_mcast; /* current multicast interface */
+int rt_sock; /* routing socket */
+int rt_sock_seqno;
+
+
+static int get_rip_sock(naddr, int);
+static void timevalsub(struct timeval *, struct timeval *, struct timeval *);
+
+int
+main(int argc,
+ char *argv[])
+{
+ int n, mib[4], off;
+ size_t len;
+ char *p, *q;
+ const char *cp;
+ struct timeval wtime, t2;
+ time_t dt;
+ fd_set ibits;
+ naddr p_net, p_mask;
+ struct interface *ifp;
+ struct parm parm;
+ char *tracename = 0;
+
+
+ /* Some shells are badly broken and send SIGHUP to backgrounded
+ * processes.
+ */
+ signal(SIGHUP, SIG_IGN);
+
+ openlog("routed", LOG_PID, LOG_DAEMON);
+ ftrace = stdout;
+
+ gettimeofday(&clk, 0);
+ prev_clk = clk;
+ epoch = clk;
+ epoch.tv_sec -= EPOCH;
+ now.tv_sec = EPOCH;
+ now_stale = EPOCH - STALE_TIME;
+ now_expire = EPOCH - EXPIRE_TIME;
+ now_garbage = EPOCH - GARBAGE_TIME;
+ wtime.tv_sec = 0;
+
+ (void)gethostname(myname, sizeof(myname)-1);
+ (void)gethost(myname, &myaddr);
+
+ while ((n = getopt(argc, argv, "sqdghmpAtvT:F:P:")) != -1) {
+ switch (n) {
+ case 's':
+ supplier = 1;
+ supplier_set = 1;
+ break;
+
+ case 'q':
+ supplier = 0;
+ supplier_set = 1;
+ break;
+
+ case 'd':
+ background = 0;
+ break;
+
+ case 'g':
+ memset(&parm, 0, sizeof(parm));
+ parm.parm_d_metric = 1;
+ cp = check_parms(&parm);
+ if (cp != 0)
+ msglog("bad -g: %s", cp);
+ else
+ default_gateway = 1;
+ break;
+
+ case 'h': /* suppress extra host routes */
+ ridhosts = 1;
+ break;
+
+ case 'm': /* advertise host route */
+ mhome = 1; /* on multi-homed hosts */
+ break;
+
+ case 'A':
+ /* Ignore authentication if we do not care.
+ * Crazy as it is, that is what RFC 1723 requires.
+ */
+ auth_ok = 0;
+ break;
+
+ case 't':
+ new_tracelevel++;
+ break;
+
+ case 'T':
+ tracename = optarg;
+ break;
+
+ case 'F': /* minimal routes for SLIP */
+ n = FAKE_METRIC;
+ p = strchr(optarg,',');
+ if (p && *p != '\0') {
+ n = (int)strtoul(p+1, &q, 0);
+ if (*q == '\0'
+ && n <= HOPCNT_INFINITY-1
+ && n >= 1)
+ *p = '\0';
+ }
+ if (!getnet(optarg, &p_net, &p_mask)) {
+ msglog("bad network; \"-F %s\"",
+ optarg);
+ break;
+ }
+ memset(&parm, 0, sizeof(parm));
+ parm.parm_net = p_net;
+ parm.parm_mask = p_mask;
+ parm.parm_d_metric = n;
+ cp = check_parms(&parm);
+ if (cp != 0)
+ msglog("bad -F: %s", cp);
+ break;
+
+ case 'P':
+ /* handle arbitrary parameters.
+ */
+ q = strdup(optarg);
+ cp = parse_parms(q, 0);
+ if (cp != 0)
+ msglog("%s in \"-P %s\"", cp, optarg);
+ free(q);
+ break;
+
+ case 'v':
+ /* display version */
+ verbose++;
+ msglog("version 2.25");
+ break;
+
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (tracename == 0 && argc >= 1) {
+ tracename = *argv++;
+ argc--;
+ }
+ if (tracename != 0 && tracename[0] == '\0')
+ goto usage;
+ if (argc != 0) {
+usage:
+ logbad(0, "usage: routed [-sqdghmpAtv] [-T tracefile]"
+ " [-F net[,metric]] [-P parms]");
+ }
+ if (geteuid() != 0) {
+ if (verbose)
+ exit(0);
+ logbad(0, "requires UID 0");
+ }
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_INET;
+ mib[2] = IPPROTO_IP;
+ mib[3] = IPCTL_FORWARDING;
+ len = sizeof(ipforwarding);
+ if (sysctl(mib, 4, &ipforwarding, &len, 0, 0) < 0)
+ LOGERR("sysctl(IPCTL_FORWARDING)");
+
+ if (!ipforwarding) {
+ if (supplier)
+ msglog("-s incompatible with ipforwarding=0");
+ if (default_gateway) {
+ msglog("-g incompatible with ipforwarding=0");
+ default_gateway = 0;
+ }
+ supplier = 0;
+ supplier_set = 1;
+ }
+ if (default_gateway) {
+ if (supplier_set && !supplier) {
+ msglog("-g and -q incompatible");
+ } else {
+ supplier = 1;
+ supplier_set = 1;
+ }
+ }
+
+
+ signal(SIGALRM, sigalrm);
+ if (!background)
+ signal(SIGHUP, sigterm); /* SIGHUP fatal during debugging */
+ signal(SIGTERM, sigterm);
+ signal(SIGINT, sigterm);
+ signal(SIGUSR1, sigtrace_on);
+ signal(SIGUSR2, sigtrace_off);
+
+ /* get into the background */
+#ifdef sgi
+ if (0 > _daemonize(background ? 0 : (_DF_NOCHDIR|_DF_NOFORK),
+ STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO))
+ BADERR(0, "_daemonize()");
+#else
+ if (background && daemon(0, 1) < 0)
+ BADERR(0,"daemon()");
+#endif
+
+#if defined(__NetBSD__)
+ pidfile(0);
+#endif
+ mypid = getpid();
+#ifdef __FreeBSD__
+ srandomdev();
+#else
+ srandom((int)(clk.tv_sec ^ clk.tv_usec ^ mypid));
+#endif
+
+ /* prepare socket connected to the kernel.
+ */
+ rt_sock = socket(AF_ROUTE, SOCK_RAW, 0);
+ if (rt_sock < 0)
+ BADERR(1,"rt_sock = socket()");
+ if (fcntl(rt_sock, F_SETFL, O_NONBLOCK) == -1)
+ logbad(1, "fcntl(rt_sock) O_NONBLOCK: %s", strerror(errno));
+ off = 0;
+ if (setsockopt(rt_sock, SOL_SOCKET,SO_USELOOPBACK,
+ &off,sizeof(off)) < 0)
+ LOGERR("setsockopt(SO_USELOOPBACK,0)");
+
+ fix_select();
+
+
+ if (tracename != 0) {
+ strncpy(inittracename, tracename, sizeof(inittracename)-1);
+ set_tracefile(inittracename, "%s", -1);
+ } else {
+ tracelevel_msg("%s", -1); /* turn on tracing to stdio */
+ }
+
+ bufinit();
+
+ /* initialize radix tree */
+ rtinit();
+
+ /* Pick a random part of the second for our output to minimize
+ * collisions.
+ *
+ * Start broadcasting after hearing from other routers, and
+ * at a random time so a bunch of systems do not get synchronized
+ * after a power failure.
+ */
+ intvl_random(&next_bcast, EPOCH+MIN_WAITTIME, EPOCH+SUPPLY_INTERVAL);
+ age_timer.tv_usec = next_bcast.tv_usec;
+ age_timer.tv_sec = EPOCH+MIN_WAITTIME;
+ rdisc_timer = next_bcast;
+ ifinit_timer.tv_usec = next_bcast.tv_usec;
+
+ /* Collect an initial view of the world by checking the interface
+ * configuration and the kludge file.
+ */
+ gwkludge();
+ ifinit();
+
+ /* Ask for routes */
+ rip_query();
+ rdisc_sol();
+
+ /* Now turn off stdio if not tracing */
+ if (new_tracelevel == 0)
+ trace_close(background);
+
+ /* Loop forever, listening and broadcasting.
+ */
+ for (;;) {
+ prev_clk = clk;
+ gettimeofday(&clk, 0);
+ if (prev_clk.tv_sec == clk.tv_sec
+ && prev_clk.tv_usec == clk.tv_usec+usec_fudge) {
+ /* Much of `routed` depends on time always advancing.
+ * On systems that do not guarantee that gettimeofday()
+ * produces unique timestamps even if called within
+ * a single tick, use trickery like that in classic
+ * BSD kernels.
+ */
+ clk.tv_usec += ++usec_fudge;
+
+ } else {
+ usec_fudge = 0;
+
+ timevalsub(&t2, &clk, &prev_clk);
+ if (t2.tv_sec < 0
+ || t2.tv_sec > wtime.tv_sec + 5) {
+ /* Deal with time changes before other
+ * housekeeping to keep everything straight.
+ */
+ dt = t2.tv_sec;
+ if (dt > 0)
+ dt -= wtime.tv_sec;
+ trace_act("time changed by %d sec", (int)dt);
+ epoch.tv_sec += dt;
+ }
+ }
+ timevalsub(&now, &clk, &epoch);
+ now_stale = now.tv_sec - STALE_TIME;
+ now_expire = now.tv_sec - EXPIRE_TIME;
+ now_garbage = now.tv_sec - GARBAGE_TIME;
+
+ /* deal with signals that should affect tracing */
+ set_tracelevel();
+
+ if (stopint != 0) {
+ rip_bcast(0);
+ rdisc_adv();
+ trace_off("exiting with signal %d", stopint);
+ exit(stopint | 128);
+ }
+
+ /* look for new or dead interfaces */
+ timevalsub(&wtime, &ifinit_timer, &now);
+ if (wtime.tv_sec <= 0) {
+ wtime.tv_sec = 0;
+ ifinit();
+ rip_query();
+ continue;
+ }
+
+ /* Check the kernel table occassionally for mysteriously
+ * evaporated routes
+ */
+ timevalsub(&t2, &flush_kern_timer, &now);
+ if (t2.tv_sec <= 0) {
+ flush_kern();
+ flush_kern_timer.tv_sec = (now.tv_sec
+ + CHECK_QUIET_INTERVAL);
+ continue;
+ }
+ if (timercmp(&t2, &wtime, <))
+ wtime = t2;
+
+ /* If it is time, then broadcast our routes.
+ */
+ if (supplier || advertise_mhome) {
+ timevalsub(&t2, &next_bcast, &now);
+ if (t2.tv_sec <= 0) {
+ /* Synchronize the aging and broadcast
+ * timers to minimize awakenings
+ */
+ age(0);
+
+ rip_bcast(0);
+
+ /* It is desirable to send routing updates
+ * regularly. So schedule the next update
+ * 30 seconds after the previous one was
+ * scheduled, instead of 30 seconds after
+ * the previous update was finished.
+ * Even if we just started after discovering
+ * a 2nd interface or were otherwise delayed,
+ * pick a 30-second aniversary of the
+ * original broadcast time.
+ */
+ n = 1 + (0-t2.tv_sec)/SUPPLY_INTERVAL;
+ next_bcast.tv_sec += n*SUPPLY_INTERVAL;
+
+ continue;
+ }
+
+ if (timercmp(&t2, &wtime, <))
+ wtime = t2;
+ }
+
+ /* If we need a flash update, either do it now or
+ * set the delay to end when it is time.
+ *
+ * If we are within MIN_WAITTIME seconds of a full update,
+ * do not bother.
+ */
+ if (need_flash
+ && supplier
+ && no_flash.tv_sec+MIN_WAITTIME < next_bcast.tv_sec) {
+ /* accurate to the millisecond */
+ if (!timercmp(&no_flash, &now, >))
+ rip_bcast(1);
+ timevalsub(&t2, &no_flash, &now);
+ if (timercmp(&t2, &wtime, <))
+ wtime = t2;
+ }
+
+ /* trigger the main aging timer.
+ */
+ timevalsub(&t2, &age_timer, &now);
+ if (t2.tv_sec <= 0) {
+ age(0);
+ continue;
+ }
+ if (timercmp(&t2, &wtime, <))
+ wtime = t2;
+
+ /* update the kernel routing table
+ */
+ timevalsub(&t2, &need_kern, &now);
+ if (t2.tv_sec <= 0) {
+ age(0);
+ continue;
+ }
+ if (timercmp(&t2, &wtime, <))
+ wtime = t2;
+
+ /* take care of router discovery,
+ * but do it in the correct the millisecond
+ */
+ if (!timercmp(&rdisc_timer, &now, >)) {
+ rdisc_age(0);
+ continue;
+ }
+ timevalsub(&t2, &rdisc_timer, &now);
+ if (timercmp(&t2, &wtime, <))
+ wtime = t2;
+
+
+ /* wait for input or a timer to expire.
+ */
+ trace_flush();
+ ibits = fdbits;
+ n = select(sock_max, &ibits, 0, 0, &wtime);
+ if (n <= 0) {
+ if (n < 0 && errno != EINTR && errno != EAGAIN)
+ BADERR(1,"select");
+ continue;
+ }
+
+ if (FD_ISSET(rt_sock, &ibits)) {
+ read_rt();
+ n--;
+ }
+ if (rdisc_sock >= 0 && FD_ISSET(rdisc_sock, &ibits)) {
+ read_d();
+ n--;
+ }
+ if (rip_sock >= 0 && FD_ISSET(rip_sock, &ibits)) {
+ read_rip(rip_sock, 0);
+ n--;
+ }
+
+ for (ifp = ifnet; n > 0 && 0 != ifp; ifp = ifp->int_next) {
+ if (ifp->int_rip_sock >= 0
+ && FD_ISSET(ifp->int_rip_sock, &ibits)) {
+ read_rip(ifp->int_rip_sock, ifp);
+ n--;
+ }
+ }
+ }
+}
+
+
+/* ARGSUSED */
+void
+sigalrm(int s UNUSED)
+{
+ /* Historically, SIGALRM would cause the daemon to check for
+ * new and broken interfaces.
+ */
+ ifinit_timer.tv_sec = now.tv_sec;
+ trace_act("SIGALRM");
+}
+
+
+/* watch for fatal signals */
+void
+sigterm(int sig)
+{
+ stopint = sig;
+ (void)signal(sig, SIG_DFL); /* catch it only once */
+}
+
+
+void
+fix_select(void)
+{
+ struct interface *ifp;
+
+
+ FD_ZERO(&fdbits);
+ sock_max = 0;
+
+ FD_SET(rt_sock, &fdbits);
+ if (sock_max <= rt_sock)
+ sock_max = rt_sock+1;
+ if (rip_sock >= 0) {
+ FD_SET(rip_sock, &fdbits);
+ if (sock_max <= rip_sock)
+ sock_max = rip_sock+1;
+ }
+ for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) {
+ if (ifp->int_rip_sock >= 0) {
+ FD_SET(ifp->int_rip_sock, &fdbits);
+ if (sock_max <= ifp->int_rip_sock)
+ sock_max = ifp->int_rip_sock+1;
+ }
+ }
+ if (rdisc_sock >= 0) {
+ FD_SET(rdisc_sock, &fdbits);
+ if (sock_max <= rdisc_sock)
+ sock_max = rdisc_sock+1;
+ }
+}
+
+
+void
+fix_sock(int sock,
+ const char *name)
+{
+ int on;
+#define MIN_SOCKBUF (4*1024)
+ static int rbuf;
+
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1)
+ logbad(1, "fcntl(%s) O_NONBLOCK: %s",
+ name, strerror(errno));
+ on = 1;
+ if (setsockopt(sock, SOL_SOCKET,SO_BROADCAST, &on,sizeof(on)) < 0)
+ msglog("setsockopt(%s,SO_BROADCAST): %s",
+ name, strerror(errno));
+#ifdef USE_PASSIFNAME
+ on = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_PASSIFNAME, &on,sizeof(on)) < 0)
+ msglog("setsockopt(%s,SO_PASSIFNAME): %s",
+ name, strerror(errno));
+#endif
+
+ if (rbuf >= MIN_SOCKBUF) {
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+ &rbuf, sizeof(rbuf)) < 0)
+ msglog("setsockopt(%s,SO_RCVBUF=%d): %s",
+ name, rbuf, strerror(errno));
+ } else {
+ for (rbuf = 60*1024; ; rbuf -= 4096) {
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+ &rbuf, sizeof(rbuf)) == 0) {
+ trace_act("RCVBUF=%d", rbuf);
+ break;
+ }
+ if (rbuf < MIN_SOCKBUF) {
+ msglog("setsockopt(%s,SO_RCVBUF = %d): %s",
+ name, rbuf, strerror(errno));
+ break;
+ }
+ }
+ }
+}
+
+
+/* get a rip socket
+ */
+static int /* <0 or file descriptor */
+get_rip_sock(naddr addr,
+ int serious) /* 1=failure to bind is serious */
+{
+ struct sockaddr_in rsin;
+ unsigned char ttl;
+ int s;
+
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ BADERR(1,"rip_sock = socket()");
+
+ memset(&rsin, 0, sizeof(rsin));
+#ifdef _HAVE_SIN_LEN
+ rsin.sin_len = sizeof(rsin);
+#endif
+ rsin.sin_family = AF_INET;
+ rsin.sin_port = htons(RIP_PORT);
+ rsin.sin_addr.s_addr = addr;
+ if (bind(s, (struct sockaddr *)&rsin, sizeof(rsin)) < 0) {
+ if (serious)
+ BADERR(errno != EADDRINUSE, "bind(rip_sock)");
+ return -1;
+ }
+ fix_sock(s,"rip_sock");
+
+ ttl = 1;
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL,
+ &ttl, sizeof(ttl)) < 0)
+ DBGERR(1,"rip_sock setsockopt(IP_MULTICAST_TTL)");
+
+ return s;
+}
+
+
+/* turn off main RIP socket */
+void
+rip_off(void)
+{
+ struct interface *ifp;
+ naddr addr;
+
+
+ if (rip_sock >= 0 && !mhome) {
+ trace_act("turn off RIP");
+
+ (void)close(rip_sock);
+ rip_sock = -1;
+
+ /* get non-broadcast sockets to listen to queries.
+ */
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
+ if (ifp->int_state & IS_REMOTE)
+ continue;
+ if (ifp->int_rip_sock < 0) {
+ addr = ((ifp->int_if_flags & IFF_POINTOPOINT)
+ ? ifp->int_dstaddr
+ : ifp->int_addr);
+ ifp->int_rip_sock = get_rip_sock(addr, 0);
+ }
+ }
+
+ fix_select();
+
+ age(0);
+ }
+}
+
+
+/* turn on RIP multicast input via an interface
+ */
+static void
+rip_mcast_on(struct interface *ifp)
+{
+ struct ip_mreq m;
+
+ if (!IS_RIP_IN_OFF(ifp->int_state)
+ && (ifp->int_if_flags & IFF_MULTICAST)
+#ifdef MCAST_PPP_BUG
+ && !(ifp->int_if_flags & IFF_POINTOPOINT)
+#endif
+ && !(ifp->int_state & IS_ALIAS)) {
+ m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP);
+#ifdef MCAST_IFINDEX
+ m.imr_interface.s_addr = htonl(ifp->int_index);
+#else
+ m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT)
+ ? ifp->int_dstaddr
+ : ifp->int_addr);
+#endif
+ if (setsockopt(rip_sock,IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &m, sizeof(m)) < 0)
+ LOGERR("setsockopt(IP_ADD_MEMBERSHIP RIP)");
+ }
+}
+
+
+/* Prepare socket used for RIP.
+ */
+void
+rip_on(struct interface *ifp)
+{
+ /* If the main RIP socket is already alive, only start receiving
+ * multicasts for this interface.
+ */
+ if (rip_sock >= 0) {
+ if (ifp != 0)
+ rip_mcast_on(ifp);
+ return;
+ }
+
+ /* If the main RIP socket is off and it makes sense to turn it on,
+ * then turn it on for all of the interfaces.
+ * It makes sense if either router discovery is off, or if
+ * router discover is on and at most one interface is doing RIP.
+ */
+ if (rip_interfaces > 0 && (!rdisc_ok || rip_interfaces > 1)) {
+ trace_act("turn on RIP");
+
+ /* Close all of the query sockets so that we can open
+ * the main socket. SO_REUSEPORT is not a solution,
+ * since that would let two daemons bind to the broadcast
+ * socket.
+ */
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
+ if (ifp->int_rip_sock >= 0) {
+ (void)close(ifp->int_rip_sock);
+ ifp->int_rip_sock = -1;
+ }
+ }
+
+ rip_sock = get_rip_sock(INADDR_ANY, 1);
+ rip_sock_mcast = 0;
+
+ /* Do not advertise anything until we have heard something
+ */
+ if (next_bcast.tv_sec < now.tv_sec+MIN_WAITTIME)
+ next_bcast.tv_sec = now.tv_sec+MIN_WAITTIME;
+
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
+ ifp->int_query_time = NEVER;
+ rip_mcast_on(ifp);
+ }
+ ifinit_timer.tv_sec = now.tv_sec;
+
+ } else if (ifp != 0
+ && !(ifp->int_state & IS_REMOTE)
+ && ifp->int_rip_sock < 0) {
+ /* RIP is off, so ensure there are sockets on which
+ * to listen for queries.
+ */
+ ifp->int_rip_sock = get_rip_sock(ifp->int_addr, 0);
+ }
+
+ fix_select();
+}
+
+
+/* die if malloc(3) fails
+ */
+void *
+rtmalloc(size_t size,
+ const char *msg)
+{
+ void *p = malloc(size);
+ if (p == 0)
+ logbad(1,"malloc(%lu) failed in %s", (u_long)size, msg);
+ return p;
+}
+
+
+/* get a random instant in an interval
+ */
+void
+intvl_random(struct timeval *tp, /* put value here */
+ u_long lo, /* value is after this second */
+ u_long hi) /* and before this */
+{
+ tp->tv_sec = (time_t)(hi == lo
+ ? lo
+ : (lo + random() % ((hi - lo))));
+ tp->tv_usec = random() % 1000000;
+}
+
+
+void
+timevaladd(struct timeval *t1,
+ struct timeval *t2)
+{
+
+ t1->tv_sec += t2->tv_sec;
+ if ((t1->tv_usec += t2->tv_usec) >= 1000000) {
+ t1->tv_sec++;
+ t1->tv_usec -= 1000000;
+ }
+}
+
+
+/* t1 = t2 - t3
+ */
+static void
+timevalsub(struct timeval *t1,
+ struct timeval *t2,
+ struct timeval *t3)
+{
+ t1->tv_sec = t2->tv_sec - t3->tv_sec;
+ if ((t1->tv_usec = t2->tv_usec - t3->tv_usec) < 0) {
+ t1->tv_sec--;
+ t1->tv_usec += 1000000;
+ }
+}
+
+
+/* put a message into the system log
+ */
+void
+msglog(const char *p, ...)
+{
+ va_list args;
+
+ trace_flush();
+
+ va_start(args, p);
+ vsyslog(LOG_ERR, p, args);
+
+ if (ftrace != 0) {
+ if (ftrace == stdout)
+ (void)fputs("routed: ", ftrace);
+ (void)vfprintf(ftrace, p, args);
+ (void)fputc('\n', ftrace);
+ }
+ va_end(args);
+}
+
+
+/* Put a message about a bad system into the system log if
+ * we have not complained about it recently.
+ *
+ * It is desirable to complain about all bad systems, but not too often.
+ * In the worst case, it is not practical to keep track of all bad systems.
+ * For example, there can be many systems with the wrong password.
+ */
+void
+msglim(struct msg_limit *lim, naddr addr, const char *p, ...)
+{
+ va_list args;
+ int i;
+ struct msg_sub *ms1, *ms;
+ const char *p1;
+
+ va_start(args, p);
+
+ /* look for the oldest slot in the table
+ * or the slot for the bad router.
+ */
+ ms = ms1 = lim->subs;
+ for (i = MSG_SUBJECT_N; ; i--, ms1++) {
+ if (i == 0) {
+ /* Reuse a slot at most once every 10 minutes.
+ */
+ if (lim->reuse > now.tv_sec) {
+ ms = 0;
+ } else {
+ ms = ms1;
+ lim->reuse = now.tv_sec + 10*60;
+ }
+ break;
+ }
+ if (ms->addr == addr) {
+ /* Repeat a complaint about a given system at
+ * most once an hour.
+ */
+ if (ms->until > now.tv_sec)
+ ms = 0;
+ break;
+ }
+ if (ms->until < ms1->until)
+ ms = ms1;
+ }
+ if (ms != 0) {
+ ms->addr = addr;
+ ms->until = now.tv_sec + 60*60; /* 60 minutes */
+
+ trace_flush();
+ for (p1 = p; *p1 == ' '; p1++)
+ continue;
+ vsyslog(LOG_ERR, p1, args);
+ }
+
+ /* always display the message if tracing */
+ if (ftrace != 0) {
+ (void)vfprintf(ftrace, p, args);
+ (void)fputc('\n', ftrace);
+ }
+ va_end(args);
+}
+
+
+void
+logbad(int dump, const char *p, ...)
+{
+ va_list args;
+
+ trace_flush();
+
+ va_start(args, p);
+ vsyslog(LOG_ERR, p, args);
+
+ (void)fputs("routed: ", stderr);
+ (void)vfprintf(stderr, p, args);
+ (void)fputs("; giving up\n",stderr);
+ (void)fflush(stderr);
+ va_end(args);
+
+ if (dump)
+ abort();
+ exit(1);
+}
diff --git a/sbin/routed/output.c b/sbin/routed/output.c
new file mode 100644
index 0000000..c9db470
--- /dev/null
+++ b/sbin/routed/output.c
@@ -0,0 +1,982 @@
+/*
+ * Copyright (c) 1983, 1988, 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.
+ * 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+
+#ifdef __NetBSD__
+__RCSID("$NetBSD$");
+#elif defined(__FreeBSD__)
+__RCSID("$FreeBSD$");
+#else
+__RCSID("$Revision: 2.27 $");
+#ident "$Revision: 2.27 $"
+#endif
+
+
+u_int update_seqno;
+
+
+/* walk the tree of routes with this for output
+ */
+struct {
+ struct sockaddr_in to;
+ naddr to_mask;
+ naddr to_net;
+ naddr to_std_mask;
+ naddr to_std_net;
+ struct interface *ifp; /* usually output interface */
+ struct auth *a;
+ char metric; /* adjust metrics by interface */
+ int npackets;
+ int gen_limit;
+ u_int state;
+#define WS_ST_FLASH 0x001 /* send only changed routes */
+#define WS_ST_RIP2_ALL 0x002 /* send full featured RIPv2 */
+#define WS_ST_AG 0x004 /* ok to aggregate subnets */
+#define WS_ST_SUPER_AG 0x008 /* ok to aggregate networks */
+#define WS_ST_QUERY 0x010 /* responding to a query */
+#define WS_ST_TO_ON_NET 0x020 /* sending onto one of our nets */
+#define WS_ST_DEFAULT 0x040 /* faking a default */
+} ws;
+
+/* A buffer for what can be heard by both RIPv1 and RIPv2 listeners */
+struct ws_buf v12buf;
+union pkt_buf ripv12_buf;
+
+/* Another for only RIPv2 listeners */
+struct ws_buf v2buf;
+union pkt_buf rip_v2_buf;
+
+
+
+void
+bufinit(void)
+{
+ ripv12_buf.rip.rip_cmd = RIPCMD_RESPONSE;
+ v12buf.buf = &ripv12_buf.rip;
+ v12buf.base = &v12buf.buf->rip_nets[0];
+
+ rip_v2_buf.rip.rip_cmd = RIPCMD_RESPONSE;
+ rip_v2_buf.rip.rip_vers = RIPv2;
+ v2buf.buf = &rip_v2_buf.rip;
+ v2buf.base = &v2buf.buf->rip_nets[0];
+}
+
+
+/* Send the contents of the global buffer via the non-multicast socket
+ */
+int /* <0 on failure */
+output(enum output_type type,
+ struct sockaddr_in *dst, /* send to here */
+ struct interface *ifp,
+ struct rip *buf,
+ int size) /* this many bytes */
+{
+ struct sockaddr_in osin;
+ int flags;
+ const char *msg;
+ int res;
+ naddr tgt_mcast;
+ int soc;
+ int serrno;
+
+ osin = *dst;
+ if (osin.sin_port == 0)
+ osin.sin_port = htons(RIP_PORT);
+#ifdef _HAVE_SIN_LEN
+ if (osin.sin_len == 0)
+ osin.sin_len = sizeof(osin);
+#endif
+
+ soc = rip_sock;
+ flags = 0;
+
+ switch (type) {
+ case OUT_QUERY:
+ msg = "Answer Query";
+ if (soc < 0)
+ soc = ifp->int_rip_sock;
+ break;
+ case OUT_UNICAST:
+ msg = "Send";
+ if (soc < 0)
+ soc = ifp->int_rip_sock;
+ flags = MSG_DONTROUTE;
+ break;
+ case OUT_BROADCAST:
+ if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ msg = "Send";
+ } else {
+ msg = "Send bcast";
+ }
+ flags = MSG_DONTROUTE;
+ break;
+ case OUT_MULTICAST:
+ if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ msg = "Send pt-to-pt";
+ } else if (ifp->int_state & IS_DUP) {
+ trace_act("abort multicast output via %s"
+ " with duplicate address",
+ ifp->int_name);
+ return 0;
+ } else {
+ msg = "Send mcast";
+ if (rip_sock_mcast != ifp) {
+#ifdef MCAST_IFINDEX
+ /* specify ifindex */
+ tgt_mcast = htonl(ifp->int_index);
+#else
+#ifdef MCAST_PPP_BUG
+ /* Do not specify the primary interface
+ * explicitly if we have the multicast
+ * point-to-point kernel bug, since the
+ * kernel will do the wrong thing if the
+ * local address of a point-to-point link
+ * is the same as the address of an ordinary
+ * interface.
+ */
+ if (ifp->int_addr == myaddr) {
+ tgt_mcast = 0;
+ } else
+#endif
+ tgt_mcast = ifp->int_addr;
+#endif
+ if (0 > setsockopt(rip_sock,
+ IPPROTO_IP, IP_MULTICAST_IF,
+ &tgt_mcast,
+ sizeof(tgt_mcast))) {
+ serrno = errno;
+ LOGERR("setsockopt(rip_sock,"
+ "IP_MULTICAST_IF)");
+ errno = serrno;
+ ifp = 0;
+ return -1;
+ }
+ rip_sock_mcast = ifp;
+ }
+ osin.sin_addr.s_addr = htonl(INADDR_RIP_GROUP);
+ }
+ break;
+
+ case NO_OUT_MULTICAST:
+ case NO_OUT_RIPV2:
+ default:
+#ifdef DEBUG
+ abort();
+#endif
+ return -1;
+ }
+
+ trace_rip(msg, "to", &osin, ifp, buf, size);
+
+ res = sendto(soc, buf, size, flags,
+ (struct sockaddr *)&osin, sizeof(osin));
+ if (res < 0
+ && (ifp == 0 || !(ifp->int_state & IS_BROKE))) {
+ serrno = errno;
+ msglog("%s sendto(%s%s%s.%d): %s", msg,
+ ifp != 0 ? ifp->int_name : "",
+ ifp != 0 ? ", " : "",
+ inet_ntoa(osin.sin_addr),
+ ntohs(osin.sin_port),
+ strerror(errno));
+ errno = serrno;
+ }
+
+ return res;
+}
+
+
+/* Find the first key for a packet to send.
+ * Try for a key that is eligible and has not expired, but settle for
+ * the last key if they have all expired.
+ * If no key is ready yet, give up.
+ */
+struct auth *
+find_auth(struct interface *ifp)
+{
+ struct auth *ap, *res;
+ int i;
+
+
+ if (ifp == 0)
+ return 0;
+
+ res = 0;
+ ap = ifp->int_auth;
+ for (i = 0; i < MAX_AUTH_KEYS; i++, ap++) {
+ /* stop looking after the last key */
+ if (ap->type == RIP_AUTH_NONE)
+ break;
+
+ /* ignore keys that are not ready yet */
+ if ((u_long)ap->start > (u_long)clk.tv_sec)
+ continue;
+
+ if ((u_long)ap->end < (u_long)clk.tv_sec) {
+ /* note best expired password as a fall-back */
+ if (res == 0 || (u_long)ap->end > (u_long)res->end)
+ res = ap;
+ continue;
+ }
+
+ /* note key with the best future */
+ if (res == 0 || (u_long)res->end < (u_long)ap->end)
+ res = ap;
+ }
+ return res;
+}
+
+
+void
+clr_ws_buf(struct ws_buf *wb,
+ struct auth *ap)
+{
+ struct netauth *na;
+
+ wb->lim = wb->base + NETS_LEN;
+ wb->n = wb->base;
+ memset(wb->n, 0, NETS_LEN*sizeof(*wb->n));
+
+ /* (start to) install authentication if appropriate
+ */
+ if (ap == 0)
+ return;
+
+ na = (struct netauth*)wb->n;
+ if (ap->type == RIP_AUTH_PW) {
+ na->a_family = RIP_AF_AUTH;
+ na->a_type = RIP_AUTH_PW;
+ memcpy(na->au.au_pw, ap->key, sizeof(na->au.au_pw));
+ wb->n++;
+
+ } else if (ap->type == RIP_AUTH_MD5) {
+ na->a_family = RIP_AF_AUTH;
+ na->a_type = RIP_AUTH_MD5;
+ na->au.a_md5.md5_keyid = ap->keyid;
+ na->au.a_md5.md5_auth_len = RIP_AUTH_MD5_KEY_LEN;
+ na->au.a_md5.md5_seqno = htonl(clk.tv_sec);
+ wb->n++;
+ wb->lim--; /* make room for trailer */
+ }
+}
+
+
+void
+end_md5_auth(struct ws_buf *wb,
+ struct auth *ap)
+{
+ struct netauth *na, *na2;
+ MD5_CTX md5_ctx;
+ int len;
+
+
+ na = (struct netauth*)wb->base;
+ na2 = (struct netauth*)wb->n;
+ len = (char *)na2-(char *)wb->buf;
+ na2->a_family = RIP_AF_AUTH;
+ na2->a_type = htons(1);
+ na->au.a_md5.md5_pkt_len = htons(len);
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, (u_char *)wb->buf, len + RIP_AUTH_MD5_HASH_XTRA);
+ MD5Update(&md5_ctx, ap->key, RIP_AUTH_MD5_KEY_LEN);
+ MD5Final(na2->au.au_pw, &md5_ctx);
+ wb->n++;
+}
+
+
+/* Send the buffer
+ */
+static void
+supply_write(struct ws_buf *wb)
+{
+ /* Output multicast only if legal.
+ * If we would multicast and it would be illegal, then discard the
+ * packet.
+ */
+ switch (wb->type) {
+ case NO_OUT_MULTICAST:
+ trace_pkt("skip multicast to %s because impossible",
+ naddr_ntoa(ws.to.sin_addr.s_addr));
+ break;
+ case NO_OUT_RIPV2:
+ break;
+ default:
+ if (ws.a != 0 && ws.a->type == RIP_AUTH_MD5)
+ end_md5_auth(wb,ws.a);
+ if (output(wb->type, &ws.to, ws.ifp, wb->buf,
+ ((char *)wb->n - (char*)wb->buf)) < 0
+ && ws.ifp != 0)
+ if_sick(ws.ifp);
+ ws.npackets++;
+ break;
+ }
+
+ clr_ws_buf(wb,ws.a);
+}
+
+
+/* put an entry into the packet
+ */
+static void
+supply_out(struct ag_info *ag)
+{
+ int i;
+ naddr mask, v1_mask, dst_h, ddst_h = 0;
+ struct ws_buf *wb;
+
+
+ /* Skip this route if doing a flash update and it and the routes
+ * it aggregates have not changed recently.
+ */
+ if (ag->ag_seqno < update_seqno
+ && (ws.state & WS_ST_FLASH))
+ return;
+
+ dst_h = ag->ag_dst_h;
+ mask = ag->ag_mask;
+ v1_mask = ripv1_mask_host(htonl(dst_h),
+ (ws.state & WS_ST_TO_ON_NET) ? ws.ifp : 0);
+ i = 0;
+
+ /* If we are sending RIPv2 packets that cannot (or must not) be
+ * heard by RIPv1 listeners, do not worry about sub- or supernets.
+ * Subnets (from other networks) can only be sent via multicast.
+ * A pair of subnet routes might have been promoted so that they
+ * are legal to send by RIPv1.
+ * If RIPv1 is off, use the multicast buffer.
+ */
+ if ((ws.state & WS_ST_RIP2_ALL)
+ || ((ag->ag_state & AGS_RIPV2) && v1_mask != mask)) {
+ /* use the RIPv2-only buffer */
+ wb = &v2buf;
+
+ } else {
+ /* use the RIPv1-or-RIPv2 buffer */
+ wb = &v12buf;
+
+ /* Convert supernet route into corresponding set of network
+ * routes for RIPv1, but leave non-contiguous netmasks
+ * to ag_check().
+ */
+ if (v1_mask > mask
+ && mask + (mask & -mask) == 0) {
+ ddst_h = v1_mask & -v1_mask;
+ i = (v1_mask & ~mask)/ddst_h;
+
+ if (i > ws.gen_limit) {
+ /* Punt if we would have to generate an
+ * unreasonable number of routes.
+ */
+ if (TRACECONTENTS)
+ trace_misc("sending %s-->%s as 1"
+ " instead of %d routes",
+ addrname(htonl(dst_h), mask,
+ 1),
+ naddr_ntoa(ws.to.sin_addr
+ .s_addr),
+ i+1);
+ i = 0;
+
+ } else {
+ mask = v1_mask;
+ ws.gen_limit -= i;
+ }
+ }
+ }
+
+ do {
+ wb->n->n_family = RIP_AF_INET;
+ wb->n->n_dst = htonl(dst_h);
+ /* If the route is from router-discovery or we are
+ * shutting down, admit only a bad metric.
+ */
+ wb->n->n_metric = ((stopint || ag->ag_metric < 1)
+ ? HOPCNT_INFINITY
+ : ag->ag_metric);
+ wb->n->n_metric = htonl(wb->n->n_metric);
+ /* Any non-zero bits in the supposedly unused RIPv1 fields
+ * cause the old `routed` to ignore the route.
+ * That means the mask and so forth cannot be sent
+ * in the hybrid RIPv1/RIPv2 mode.
+ */
+ if (ws.state & WS_ST_RIP2_ALL) {
+ if (ag->ag_nhop != 0
+ && ((ws.state & WS_ST_QUERY)
+ || (ag->ag_nhop != ws.ifp->int_addr
+ && on_net(ag->ag_nhop,
+ ws.ifp->int_net,
+ ws.ifp->int_mask))))
+ wb->n->n_nhop = ag->ag_nhop;
+ wb->n->n_mask = htonl(mask);
+ wb->n->n_tag = ag->ag_tag;
+ }
+ dst_h += ddst_h;
+
+ if (++wb->n >= wb->lim)
+ supply_write(wb);
+ } while (i-- != 0);
+}
+
+
+/* supply one route from the table
+ */
+/* ARGSUSED */
+static int
+walk_supply(struct radix_node *rn,
+ struct walkarg *argp UNUSED)
+{
+#define RT ((struct rt_entry *)rn)
+ u_short ags;
+ char metric, pref;
+ naddr dst, nhop;
+ struct rt_spare *rts;
+ int i;
+
+
+ /* Do not advertise external remote interfaces or passive interfaces.
+ */
+ if ((RT->rt_state & RS_IF)
+ && RT->rt_ifp != 0
+ && (RT->rt_ifp->int_state & IS_PASSIVE)
+ && !(RT->rt_state & RS_MHOME))
+ return 0;
+
+ /* If being quiet about our ability to forward, then
+ * do not say anything unless responding to a query,
+ * except about our main interface.
+ */
+ if (!supplier && !(ws.state & WS_ST_QUERY)
+ && !(RT->rt_state & RS_MHOME))
+ return 0;
+
+ dst = RT->rt_dst;
+
+ /* do not collide with the fake default route */
+ if (dst == RIP_DEFAULT
+ && (ws.state & WS_ST_DEFAULT))
+ return 0;
+
+ if (RT->rt_state & RS_NET_SYN) {
+ if (RT->rt_state & RS_NET_INT) {
+ /* Do not send manual synthetic network routes
+ * into the subnet.
+ */
+ if (on_net(ws.to.sin_addr.s_addr,
+ ntohl(dst), RT->rt_mask))
+ return 0;
+
+ } else {
+ /* Do not send automatic synthetic network routes
+ * if they are not needed because no RIPv1 listeners
+ * can hear them.
+ */
+ if (ws.state & WS_ST_RIP2_ALL)
+ return 0;
+
+ /* Do not send automatic synthetic network routes to
+ * the real subnet.
+ */
+ if (on_net(ws.to.sin_addr.s_addr,
+ ntohl(dst), RT->rt_mask))
+ return 0;
+ }
+ nhop = 0;
+
+ } else {
+ /* Advertise the next hop if this is not a route for one
+ * of our interfaces and the next hop is on the same
+ * network as the target.
+ * The final determination is made by supply_out().
+ */
+ if (!(RT->rt_state & RS_IF)
+ && RT->rt_gate != myaddr
+ && RT->rt_gate != loopaddr)
+ nhop = RT->rt_gate;
+ else
+ nhop = 0;
+ }
+
+ metric = RT->rt_metric;
+ ags = 0;
+
+ if (RT->rt_state & RS_MHOME) {
+ /* retain host route of multi-homed servers */
+ ;
+
+ } else if (RT_ISHOST(RT)) {
+ /* We should always suppress (into existing network routes)
+ * the host routes for the local end of our point-to-point
+ * links.
+ * If we are suppressing host routes in general, then do so.
+ * Avoid advertising host routes onto their own network,
+ * where they should be handled by proxy-ARP.
+ */
+ if ((RT->rt_state & RS_LOCAL)
+ || ridhosts
+ || on_net(dst, ws.to_net, ws.to_mask))
+ ags |= AGS_SUPPRESS;
+
+ /* Aggregate stray host routes into network routes if allowed.
+ * We cannot aggregate host routes into small network routes
+ * without confusing RIPv1 listeners into thinking the
+ * network routes are host routes.
+ */
+ if ((ws.state & WS_ST_AG) && (ws.state & WS_ST_RIP2_ALL))
+ ags |= AGS_AGGREGATE;
+
+ } else {
+ /* Always suppress network routes into other, existing
+ * network routes
+ */
+ ags |= AGS_SUPPRESS;
+
+ /* Generate supernets if allowed.
+ * If we can be heard by RIPv1 systems, we will
+ * later convert back to ordinary nets.
+ * This unifies dealing with received supernets.
+ */
+ if ((ws.state & WS_ST_AG)
+ && ((RT->rt_state & RS_SUBNET)
+ || (ws.state & WS_ST_SUPER_AG)))
+ ags |= AGS_AGGREGATE;
+ }
+
+ /* Do not send RIPv1 advertisements of subnets to other
+ * networks. If possible, multicast them by RIPv2.
+ */
+ if ((RT->rt_state & RS_SUBNET)
+ && !(ws.state & WS_ST_RIP2_ALL)
+ && !on_net(dst, ws.to_std_net, ws.to_std_mask))
+ ags |= AGS_RIPV2 | AGS_AGGREGATE;
+
+
+ /* Do not send a route back to where it came from, except in
+ * response to a query. This is "split-horizon". That means not
+ * advertising back to the same network and so via the same interface.
+ *
+ * We want to suppress routes that might have been fragmented
+ * from this route by a RIPv1 router and sent back to us, and so we
+ * cannot forget this route here. Let the split-horizon route
+ * suppress the fragmented routes and then itself be forgotten.
+ *
+ * Include the routes for both ends of point-to-point interfaces
+ * among those suppressed by split-horizon, since the other side
+ * should knows them as well as we do.
+ *
+ * Notice spare routes with the same metric that we are about to
+ * advertise, to split the horizon on redundant, inactive paths.
+ *
+ * Do not suppress advertisements of interface-related addresses on
+ * non-point-to-point interfaces. This ensures that we have something
+ * to say every 30 seconds to help detect broken Ethernets or
+ * other interfaces where one packet every 30 seconds costs nothing.
+ */
+ if (ws.ifp != 0
+ && !(ws.state & WS_ST_QUERY)
+ && (ws.state & WS_ST_TO_ON_NET)
+ && (!(RT->rt_state & RS_IF)
+ || ws.ifp->int_if_flags & IFF_POINTOPOINT)) {
+ for (rts = RT->rt_spares, i = NUM_SPARES; i != 0; i--, rts++) {
+ if (rts->rts_metric > metric
+ || rts->rts_ifp != ws.ifp)
+ continue;
+
+ /* If we do not mark the route with AGS_SPLIT_HZ here,
+ * it will be poisoned-reverse, or advertised back
+ * toward its source with an infinite metric.
+ * If we have recently advertised the route with a
+ * better metric than we now have, then we should
+ * poison-reverse the route before suppressing it for
+ * split-horizon.
+ *
+ * In almost all cases, if there is no spare for the
+ * route then it is either old and dead or a brand
+ * new route. If it is brand new, there is no need
+ * for poison-reverse. If it is old and dead, it
+ * is already poisoned.
+ */
+ if (RT->rt_poison_time < now_expire
+ || RT->rt_poison_metric >= metric
+ || RT->rt_spares[1].rts_gate == 0) {
+ ags |= AGS_SPLIT_HZ;
+ ags &= ~AGS_SUPPRESS;
+ }
+ metric = HOPCNT_INFINITY;
+ break;
+ }
+ }
+
+ /* Keep track of the best metric with which the
+ * route has been advertised recently.
+ */
+ if (RT->rt_poison_metric >= metric
+ || RT->rt_poison_time < now_expire) {
+ RT->rt_poison_time = now.tv_sec;
+ RT->rt_poison_metric = metric;
+ }
+
+ /* Adjust the outgoing metric by the cost of the link.
+ * Avoid aggregation when a route is counting to infinity.
+ */
+ pref = RT->rt_poison_metric + ws.metric;
+ metric += ws.metric;
+
+ /* Do not advertise stable routes that will be ignored,
+ * unless we are answering a query.
+ * If the route recently was advertised with a metric that
+ * would have been less than infinity through this interface,
+ * we need to continue to advertise it in order to poison it.
+ */
+ if (metric >= HOPCNT_INFINITY) {
+ if (!(ws.state & WS_ST_QUERY)
+ && (pref >= HOPCNT_INFINITY
+ || RT->rt_poison_time < now_garbage))
+ return 0;
+
+ metric = HOPCNT_INFINITY;
+ }
+
+ ag_check(dst, RT->rt_mask, 0, nhop, metric, pref,
+ RT->rt_seqno, RT->rt_tag, ags, supply_out);
+ return 0;
+#undef RT
+}
+
+
+/* Supply dst with the contents of the routing tables.
+ * If this won't fit in one packet, chop it up into several.
+ */
+void
+supply(struct sockaddr_in *dst,
+ struct interface *ifp, /* output interface */
+ enum output_type type,
+ int flash, /* 1=flash update */
+ int vers, /* RIP version */
+ int passwd_ok) /* OK to include cleartext password */
+{
+ struct rt_entry *rt;
+ int def_metric;
+
+
+ ws.state = 0;
+ ws.gen_limit = 1024;
+
+ ws.to = *dst;
+ ws.to_std_mask = std_mask(ws.to.sin_addr.s_addr);
+ ws.to_std_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_std_mask;
+
+ if (ifp != 0) {
+ ws.to_mask = ifp->int_mask;
+ ws.to_net = ifp->int_net;
+ if (on_net(ws.to.sin_addr.s_addr, ws.to_net, ws.to_mask))
+ ws.state |= WS_ST_TO_ON_NET;
+
+ } else {
+ ws.to_mask = ripv1_mask_net(ws.to.sin_addr.s_addr, 0);
+ ws.to_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_mask;
+ rt = rtfind(dst->sin_addr.s_addr);
+ if (rt)
+ ifp = rt->rt_ifp;
+ }
+
+ ws.npackets = 0;
+ if (flash)
+ ws.state |= WS_ST_FLASH;
+
+ if ((ws.ifp = ifp) == 0) {
+ ws.metric = 1;
+ } else {
+ /* Adjust the advertised metric by the outgoing interface
+ * metric.
+ */
+ ws.metric = ifp->int_metric + 1 + ifp->int_adj_outmetric;
+ }
+
+ ripv12_buf.rip.rip_vers = vers;
+
+ switch (type) {
+ case OUT_MULTICAST:
+ if (ifp->int_if_flags & IFF_MULTICAST)
+ v2buf.type = OUT_MULTICAST;
+ else
+ v2buf.type = NO_OUT_MULTICAST;
+ v12buf.type = OUT_BROADCAST;
+ break;
+
+ case OUT_QUERY:
+ ws.state |= WS_ST_QUERY;
+ /* FALLTHROUGH */
+ case OUT_BROADCAST:
+ case OUT_UNICAST:
+ v2buf.type = (vers == RIPv2) ? type : NO_OUT_RIPV2;
+ v12buf.type = type;
+ break;
+
+ case NO_OUT_MULTICAST:
+ case NO_OUT_RIPV2:
+ break; /* no output */
+ }
+
+ if (vers == RIPv2) {
+ /* full RIPv2 only if cannot be heard by RIPv1 listeners */
+ if (type != OUT_BROADCAST)
+ ws.state |= WS_ST_RIP2_ALL;
+ if ((ws.state & WS_ST_QUERY)
+ || !(ws.state & WS_ST_TO_ON_NET)) {
+ ws.state |= (WS_ST_AG | WS_ST_SUPER_AG);
+ } else if (ifp == 0 || !(ifp->int_state & IS_NO_AG)) {
+ ws.state |= WS_ST_AG;
+ if (type != OUT_BROADCAST
+ && (ifp == 0
+ || !(ifp->int_state & IS_NO_SUPER_AG)))
+ ws.state |= WS_ST_SUPER_AG;
+ }
+ }
+
+ ws.a = (vers == RIPv2) ? find_auth(ifp) : 0;
+ if (!passwd_ok && ws.a != 0 && ws.a->type == RIP_AUTH_PW)
+ ws.a = 0;
+ clr_ws_buf(&v12buf,ws.a);
+ clr_ws_buf(&v2buf,ws.a);
+
+ /* Fake a default route if asked and if there is not already
+ * a better, real default route.
+ */
+ if (supplier && (def_metric = ifp->int_d_metric) != 0) {
+ if (0 == (rt = rtget(RIP_DEFAULT, 0))
+ || rt->rt_metric+ws.metric >= def_metric) {
+ ws.state |= WS_ST_DEFAULT;
+ ag_check(0, 0, 0, 0, def_metric, def_metric,
+ 0, 0, 0, supply_out);
+ } else {
+ def_metric = rt->rt_metric+ws.metric;
+ }
+
+ /* If both RIPv2 and the poor-man's router discovery
+ * kludge are on, arrange to advertise an extra
+ * default route via RIPv1.
+ */
+ if ((ws.state & WS_ST_RIP2_ALL)
+ && (ifp->int_state & IS_PM_RDISC)) {
+ ripv12_buf.rip.rip_vers = RIPv1;
+ v12buf.n->n_family = RIP_AF_INET;
+ v12buf.n->n_dst = htonl(RIP_DEFAULT);
+ v12buf.n->n_metric = htonl(def_metric);
+ v12buf.n++;
+ }
+ }
+
+ (void)rn_walktree(rhead, walk_supply, 0);
+ ag_flush(0,0,supply_out);
+
+ /* Flush the packet buffers, provided they are not empty and
+ * do not contain only the password.
+ */
+ if (v12buf.n != v12buf.base
+ && (v12buf.n > v12buf.base+1
+ || v12buf.base->n_family != RIP_AF_AUTH))
+ supply_write(&v12buf);
+ if (v2buf.n != v2buf.base
+ && (v2buf.n > v2buf.base+1
+ || v2buf.base->n_family != RIP_AF_AUTH))
+ supply_write(&v2buf);
+
+ /* If we sent nothing and this is an answer to a query, send
+ * an empty buffer.
+ */
+ if (ws.npackets == 0
+ && (ws.state & WS_ST_QUERY))
+ supply_write(&v12buf);
+}
+
+
+/* send all of the routing table or just do a flash update
+ */
+void
+rip_bcast(int flash)
+{
+#ifdef _HAVE_SIN_LEN
+ static struct sockaddr_in dst = {sizeof(dst), AF_INET, 0, {0}, {0}};
+#else
+ static struct sockaddr_in dst = {AF_INET};
+#endif
+ struct interface *ifp;
+ enum output_type type;
+ int vers;
+ struct timeval rtime;
+
+
+ need_flash = 0;
+ intvl_random(&rtime, MIN_WAITTIME, MAX_WAITTIME);
+ no_flash = rtime;
+ timevaladd(&no_flash, &now);
+
+ if (rip_sock < 0)
+ return;
+
+ trace_act("send %s and inhibit dynamic updates for %.3f sec",
+ flash ? "dynamic update" : "all routes",
+ rtime.tv_sec + ((float)rtime.tv_usec)/1000000.0);
+
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
+ /* Skip interfaces not doing RIP.
+ * Do try broken interfaces to see if they have healed.
+ */
+ if (IS_RIP_OUT_OFF(ifp->int_state))
+ continue;
+
+ /* skip turned off interfaces */
+ if (!iff_up(ifp->int_if_flags))
+ continue;
+
+ vers = (ifp->int_state & IS_NO_RIPV1_OUT) ? RIPv2 : RIPv1;
+
+ if (ifp->int_if_flags & IFF_BROADCAST) {
+ /* ordinary, hardware interface */
+ dst.sin_addr.s_addr = ifp->int_brdaddr;
+
+ if (vers == RIPv2
+ && !(ifp->int_state & IS_NO_RIP_MCAST)) {
+ type = OUT_MULTICAST;
+ } else {
+ type = OUT_BROADCAST;
+ }
+
+ } else if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ /* point-to-point hardware interface */
+ dst.sin_addr.s_addr = ifp->int_dstaddr;
+ type = OUT_UNICAST;
+
+ } else if (ifp->int_state & IS_REMOTE) {
+ /* remote interface */
+ dst.sin_addr.s_addr = ifp->int_addr;
+ type = OUT_UNICAST;
+
+ } else {
+ /* ATM, HIPPI, etc. */
+ continue;
+ }
+
+ supply(&dst, ifp, type, flash, vers, 1);
+ }
+
+ update_seqno++; /* all routes are up to date */
+}
+
+
+/* Ask for routes
+ * Do it only once to an interface, and not even after the interface
+ * was broken and recovered.
+ */
+void
+rip_query(void)
+{
+#ifdef _HAVE_SIN_LEN
+ static struct sockaddr_in dst = {sizeof(dst), AF_INET, 0, {0}, {0}};
+#else
+ static struct sockaddr_in dst = {AF_INET};
+#endif
+ struct interface *ifp;
+ struct rip buf;
+ enum output_type type;
+
+
+ if (rip_sock < 0)
+ return;
+
+ memset(&buf, 0, sizeof(buf));
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ /* Skip interfaces those already queried.
+ * Do not ask via interfaces through which we don't
+ * accept input. Do not ask via interfaces that cannot
+ * send RIP packets.
+ * Do try broken interfaces to see if they have healed.
+ */
+ if (IS_RIP_IN_OFF(ifp->int_state)
+ || ifp->int_query_time != NEVER)
+ continue;
+
+ /* skip turned off interfaces */
+ if (!iff_up(ifp->int_if_flags))
+ continue;
+
+ buf.rip_vers = (ifp->int_state&IS_NO_RIPV1_OUT) ? RIPv2:RIPv1;
+ buf.rip_cmd = RIPCMD_REQUEST;
+ buf.rip_nets[0].n_family = RIP_AF_UNSPEC;
+ buf.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY);
+
+ /* Send a RIPv1 query only if allowed and if we will
+ * listen to RIPv1 routers.
+ */
+ if ((ifp->int_state & IS_NO_RIPV1_OUT)
+ || (ifp->int_state & IS_NO_RIPV1_IN)) {
+ buf.rip_vers = RIPv2;
+ } else {
+ buf.rip_vers = RIPv1;
+ }
+
+ if (ifp->int_if_flags & IFF_BROADCAST) {
+ /* ordinary, hardware interface */
+ dst.sin_addr.s_addr = ifp->int_brdaddr;
+
+ /* Broadcast RIPv1 queries and RIPv2 queries
+ * when the hardware cannot multicast.
+ */
+ if (buf.rip_vers == RIPv2
+ && (ifp->int_if_flags & IFF_MULTICAST)
+ && !(ifp->int_state & IS_NO_RIP_MCAST)) {
+ type = OUT_MULTICAST;
+ } else {
+ type = OUT_BROADCAST;
+ }
+
+ } else if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ /* point-to-point hardware interface */
+ dst.sin_addr.s_addr = ifp->int_dstaddr;
+ type = OUT_UNICAST;
+
+ } else if (ifp->int_state & IS_REMOTE) {
+ /* remote interface */
+ dst.sin_addr.s_addr = ifp->int_addr;
+ type = OUT_UNICAST;
+
+ } else {
+ /* ATM, HIPPI, etc. */
+ continue;
+ }
+
+ ifp->int_query_time = now.tv_sec+SUPPLY_INTERVAL;
+ if (output(type, &dst, ifp, &buf, sizeof(buf)) < 0)
+ if_sick(ifp);
+ }
+}
diff --git a/sbin/routed/parms.c b/sbin/routed/parms.c
new file mode 100644
index 0000000..356ac8e
--- /dev/null
+++ b/sbin/routed/parms.c
@@ -0,0 +1,1052 @@
+/*
+ * 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.
+ * 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+#include "pathnames.h"
+#include <sys/stat.h>
+
+#ifdef __NetBSD__
+__RCSID("$NetBSD$");
+#elif defined(__FreeBSD__)
+__RCSID("$FreeBSD$");
+#else
+__RCSID("$Revision: 2.26 $");
+#ident "$Revision: 2.26 $"
+#endif
+
+
+struct parm *parms;
+struct intnet *intnets;
+struct r1net *r1nets;
+struct tgate *tgates;
+
+
+/* use configured parameters
+ */
+void
+get_parms(struct interface *ifp)
+{
+ static int warned_auth_in, warned_auth_out;
+ struct parm *parmp;
+ int i, num_passwds = 0;
+
+ /* get all relevant parameters
+ */
+ for (parmp = parms; parmp != 0; parmp = parmp->parm_next) {
+ if (parmp->parm_name[0] == '\0'
+ || !strcmp(ifp->int_name, parmp->parm_name)
+ || (parmp->parm_name[0] == '\n'
+ && on_net(ifp->int_addr,
+ parmp->parm_net, parmp->parm_mask))) {
+
+ /* This group of parameters is relevant,
+ * so get its settings
+ */
+ ifp->int_state |= parmp->parm_int_state;
+ for (i = 0; i < MAX_AUTH_KEYS; i++) {
+ if (parmp->parm_auth[0].type == RIP_AUTH_NONE
+ || num_passwds >= MAX_AUTH_KEYS)
+ break;
+ memcpy(&ifp->int_auth[num_passwds++],
+ &parmp->parm_auth[i],
+ sizeof(ifp->int_auth[0]));
+ }
+ if (parmp->parm_rdisc_pref != 0)
+ ifp->int_rdisc_pref = parmp->parm_rdisc_pref;
+ if (parmp->parm_rdisc_int != 0)
+ ifp->int_rdisc_int = parmp->parm_rdisc_int;
+ if (parmp->parm_adj_inmetric != 0)
+ ifp->int_adj_inmetric = parmp->parm_adj_inmetric;
+ if (parmp->parm_adj_outmetric != 0)
+ ifp->int_adj_outmetric = parmp->parm_adj_outmetric;
+ }
+ }
+
+ /* Set general defaults.
+ *
+ * Default poor-man's router discovery to a metric that will
+ * be heard by old versions of `routed`. They ignored received
+ * routes with metric 15.
+ */
+ if ((ifp->int_state & IS_PM_RDISC)
+ && ifp->int_d_metric == 0)
+ ifp->int_d_metric = FAKE_METRIC;
+
+ if (ifp->int_rdisc_int == 0)
+ ifp->int_rdisc_int = DefMaxAdvertiseInterval;
+
+ if (!(ifp->int_if_flags & IFF_MULTICAST)
+ && !(ifp->int_state & IS_REMOTE))
+ ifp->int_state |= IS_BCAST_RDISC;
+
+ if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ ifp->int_state |= IS_BCAST_RDISC;
+ /* By default, point-to-point links should be passive
+ * about router-discovery for the sake of demand-dialing.
+ */
+ if (0 == (ifp->int_state & GROUP_IS_SOL_OUT))
+ ifp->int_state |= IS_NO_SOL_OUT;
+ if (0 == (ifp->int_state & GROUP_IS_ADV_OUT))
+ ifp->int_state |= IS_NO_ADV_OUT;
+ }
+
+ if (0 != (ifp->int_state & (IS_PASSIVE | IS_REMOTE)))
+ ifp->int_state |= IS_NO_RDISC;
+ if (ifp->int_state & IS_PASSIVE)
+ ifp->int_state |= IS_NO_RIP;
+
+ if (!IS_RIP_IN_OFF(ifp->int_state)
+ && ifp->int_auth[0].type != RIP_AUTH_NONE
+ && !(ifp->int_state & IS_NO_RIPV1_IN)
+ && !warned_auth_in) {
+ msglog("Warning: RIPv1 input via %s"
+ " will be accepted without authentication",
+ ifp->int_name);
+ warned_auth_in = 1;
+ }
+ if (!IS_RIP_OUT_OFF(ifp->int_state)
+ && ifp->int_auth[0].type != RIP_AUTH_NONE
+ && !(ifp->int_state & IS_NO_RIPV1_OUT)) {
+ if (!warned_auth_out) {
+ msglog("Warning: RIPv1 output via %s"
+ " will be sent without authentication",
+ ifp->int_name);
+ warned_auth_out = 1;
+ }
+ }
+}
+
+
+/* Read a list of gateways from /etc/gateways and add them to our tables.
+ *
+ * This file contains a list of "remote" gateways. That is usually
+ * a gateway which we cannot immediately determine if it is present or
+ * not as we can do for those provided by directly connected hardware.
+ *
+ * If a gateway is marked "passive" in the file, then we assume it
+ * does not understand RIP and assume it is always present. Those
+ * not marked passive are treated as if they were directly connected
+ * and assumed to be broken if they do not send us advertisements.
+ * All remote interfaces are added to our list, and those not marked
+ * passive are sent routing updates.
+ *
+ * A passive interface can also be local, hardware interface exempt
+ * from RIP.
+ */
+void
+gwkludge(void)
+{
+ FILE *fp;
+ char *p, *lptr;
+ const char *cp;
+ char lbuf[200], net_host[5], dname[64+1+64+1];
+ char gname[GNAME_LEN+1], qual[9];
+ struct interface *ifp;
+ naddr dst, netmask, gate;
+ int metric, n, lnum;
+ struct stat sb;
+ u_int state;
+ const char *type;
+
+
+ fp = fopen(_PATH_GATEWAYS, "r");
+ if (fp == 0)
+ return;
+
+ if (0 > fstat(fileno(fp), &sb)) {
+ msglog("could not stat() "_PATH_GATEWAYS);
+ (void)fclose(fp);
+ return;
+ }
+
+ for (lnum = 1; ; lnum++) {
+ if (0 == fgets(lbuf, sizeof(lbuf), fp))
+ break;
+ lptr = lbuf;
+ while (*lptr == ' ')
+ lptr++;
+ p = lptr+strlen(lptr)-1;
+ while (*p == '\n'
+ || (*p == ' ' && (p == lptr+1 || *(p-1) != '\\')))
+ *p-- = '\0';
+ if (*lptr == '\0' /* ignore null and comment lines */
+ || *lptr == '#')
+ continue;
+
+ /* notice newfangled parameter lines
+ */
+ if (strncasecmp("net", lptr, 3)
+ && strncasecmp("host", lptr, 4)) {
+ cp = parse_parms(lptr,
+ (sb.st_uid == 0
+ && !(sb.st_mode&(S_IRWXG|S_IRWXO))));
+ if (cp != 0)
+ msglog("%s in line %d of "_PATH_GATEWAYS,
+ cp, lnum);
+ continue;
+ }
+
+/* {net | host} XX[/M] XX gateway XX metric DD [passive | external]\n */
+ qual[0] = '\0';
+ /* the '64' here must be GNAME_LEN */
+ n = sscanf(lptr, "%4s %129[^ \t] gateway"
+ " %64[^ / \t] metric %u %8s\n",
+ net_host, dname, gname, &metric, qual);
+ if (n != 4 && n != 5) {
+ msglog("bad "_PATH_GATEWAYS" entry \"%s\"; %d values",
+ lptr, n);
+ continue;
+ }
+ if (metric >= HOPCNT_INFINITY) {
+ msglog("bad metric in "_PATH_GATEWAYS" entry \"%s\"",
+ lptr);
+ continue;
+ }
+ if (!strcasecmp(net_host, "host")) {
+ if (!gethost(dname, &dst)) {
+ msglog("bad host \"%s\" in "_PATH_GATEWAYS
+ " entry \"%s\"", dname, lptr);
+ continue;
+ }
+ netmask = HOST_MASK;
+ } else if (!strcasecmp(net_host, "net")) {
+ if (!getnet(dname, &dst, &netmask)) {
+ msglog("bad net \"%s\" in "_PATH_GATEWAYS
+ " entry \"%s\"", dname, lptr);
+ continue;
+ }
+ if (dst == RIP_DEFAULT) {
+ msglog("bad net \"%s\" in "_PATH_GATEWAYS
+ " entry \"%s\"--cannot be default",
+ dname, lptr);
+ continue;
+ }
+ /* Turn network # into IP address. */
+ dst = htonl(dst);
+ } else {
+ msglog("bad \"%s\" in "_PATH_GATEWAYS
+ " entry \"%s\"", net_host, lptr);
+ continue;
+ }
+
+ if (!gethost(gname, &gate)) {
+ msglog("bad gateway \"%s\" in "_PATH_GATEWAYS
+ " entry \"%s\"", gname, lptr);
+ continue;
+ }
+
+ if (!strcasecmp(qual, type = "passive")) {
+ /* Passive entries are not placed in our tables,
+ * only the kernel's, so we don't copy all of the
+ * external routing information within a net.
+ * Internal machines should use the default
+ * route to a suitable gateway (like us).
+ */
+ state = IS_REMOTE | IS_PASSIVE;
+ if (metric == 0)
+ metric = 1;
+
+ } else if (!strcasecmp(qual, type = "external")) {
+ /* External entries are handled by other means
+ * such as EGP, and are placed only in the daemon
+ * tables to prevent overriding them with something
+ * else.
+ */
+ strcpy(qual,"external");
+ state = IS_REMOTE | IS_PASSIVE | IS_EXTERNAL;
+ if (metric == 0)
+ metric = 1;
+
+ } else if (!strcasecmp(qual, "active")
+ || qual[0] == '\0') {
+ if (metric != 0) {
+ /* Entries that are neither "passive" nor
+ * "external" are "remote" and must behave
+ * like physical interfaces. If they are not
+ * heard from regularly, they are deleted.
+ */
+ state = IS_REMOTE;
+ type = "remote";
+ } else {
+ /* "remote" entries with a metric of 0
+ * are aliases for our own interfaces
+ */
+ state = IS_REMOTE | IS_PASSIVE | IS_ALIAS;
+ type = "alias";
+ }
+
+ } else {
+ msglog("bad "_PATH_GATEWAYS" entry \"%s\";"
+ " unknown type %s", lptr, qual);
+ continue;
+ }
+
+ if (0 != (state & (IS_PASSIVE | IS_REMOTE)))
+ state |= IS_NO_RDISC;
+ if (state & IS_PASSIVE)
+ state |= IS_NO_RIP;
+
+ ifp = check_dup(gate,dst,netmask,state);
+ if (ifp != 0) {
+ msglog("duplicate "_PATH_GATEWAYS" entry \"%s\"",lptr);
+ continue;
+ }
+
+ ifp = (struct interface *)rtmalloc(sizeof(*ifp), "gwkludge()");
+ memset(ifp, 0, sizeof(*ifp));
+
+ ifp->int_state = state;
+ if (netmask == HOST_MASK)
+ ifp->int_if_flags = IFF_POINTOPOINT | IFF_UP;
+ else
+ ifp->int_if_flags = IFF_UP;
+ ifp->int_act_time = NEVER;
+ ifp->int_addr = gate;
+ ifp->int_dstaddr = dst;
+ ifp->int_mask = netmask;
+ ifp->int_ripv1_mask = netmask;
+ ifp->int_std_mask = std_mask(gate);
+ ifp->int_net = ntohl(dst);
+ ifp->int_std_net = ifp->int_net & ifp->int_std_mask;
+ ifp->int_std_addr = htonl(ifp->int_std_net);
+ ifp->int_metric = metric;
+ if (!(state & IS_EXTERNAL)
+ && ifp->int_mask != ifp->int_std_mask)
+ ifp->int_state |= IS_SUBNET;
+ (void)sprintf(ifp->int_name, "%s(%s)", type, gname);
+ ifp->int_index = -1;
+
+ if_link(ifp);
+ }
+
+ /* After all of the parameter lines have been read,
+ * apply them to any remote interfaces.
+ */
+ for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) {
+ get_parms(ifp);
+
+ tot_interfaces++;
+ if (!IS_RIP_OFF(ifp->int_state))
+ rip_interfaces++;
+
+ trace_if("Add", ifp);
+ }
+
+ (void)fclose(fp);
+}
+
+
+/* like strtok(), but honoring backslash and not changing the source string
+ */
+static int /* 0=ok, -1=bad */
+parse_quote(char **linep, /* look here */
+ const char *delims, /* for these delimiters */
+ char *delimp, /* 0 or put found delimiter here */
+ char *buf, /* copy token to here */
+ int lim) /* at most this many bytes */
+{
+ char c = '\0', *pc;
+ const char *p;
+
+
+ pc = *linep;
+ if (*pc == '\0')
+ return -1;
+
+ while (lim != 0) {
+ c = *pc++;
+ if (c == '\0')
+ break;
+
+ if (c == '\\' && *pc != '\0') {
+ if ((c = *pc++) == 'n') {
+ c = '\n';
+ } else if (c == 'r') {
+ c = '\r';
+ } else if (c == 't') {
+ c = '\t';
+ } else if (c == 'b') {
+ c = '\b';
+ } else if (c >= '0' && c <= '7') {
+ c -= '0';
+ if (*pc >= '0' && *pc <= '7') {
+ c = (c<<3)+(*pc++ - '0');
+ if (*pc >= '0' && *pc <= '7')
+ c = (c<<3)+(*pc++ - '0');
+ }
+ }
+
+ } else {
+ for (p = delims; *p != '\0'; ++p) {
+ if (*p == c)
+ goto exit;
+ }
+ }
+
+ *buf++ = c;
+ --lim;
+ }
+exit:
+ if (lim == 0)
+ return -1;
+
+ *buf = '\0'; /* terminate copy of token */
+ if (delimp != 0)
+ *delimp = c; /* return delimiter */
+ *linep = pc-1; /* say where we ended */
+ return 0;
+}
+
+
+/* Parse password timestamp
+ */
+static char *
+parse_ts(time_t *tp,
+ char **valp,
+ char *val0,
+ char *delimp,
+ char *buf,
+ u_int bufsize)
+{
+ struct tm tm;
+#if defined(sgi) || defined(__NetBSD__)
+ char *ptr;
+#endif
+
+ if (0 > parse_quote(valp, "| ,\n\r", delimp,
+ buf,bufsize)
+ || buf[bufsize-1] != '\0'
+ || buf[bufsize-2] != '\0') {
+ sprintf(buf,"bad timestamp %.25s", val0);
+ return buf;
+ }
+ strcat(buf,"\n");
+ memset(&tm, 0, sizeof(tm));
+#if defined(sgi) || defined(__NetBSD__)
+ ptr = strptime(buf, "%y/%m/%d@%H:%M\n", &tm);
+ if (ptr == NULL || *ptr != '\0') {
+ sprintf(buf,"bad timestamp %.25s", val0);
+ return buf;
+ }
+#else
+ if (5 != sscanf(buf, "%u/%u/%u@%u:%u\n",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min)
+ || tm.tm_mon < 1 || tm.tm_mon > 12
+ || tm.tm_mday < 1 || tm.tm_mday > 31) {
+ sprintf(buf,"bad timestamp %.25s", val0);
+ return buf;
+ }
+ tm.tm_mon--;
+ if (tm.tm_year <= 37) /* assume small years are in the */
+ tm.tm_year += 100; /* 3rd millenium */
+#endif
+
+ if ((*tp = mktime(&tm)) == -1) {
+ sprintf(buf,"bad timestamp %.25s", val0);
+ return buf;
+ }
+
+ return 0;
+}
+
+
+/* Get a password, key ID, and expiration date in the format
+ * passwd|keyID|year/mon/day@hour:min|year/mon/day@hour:min
+ */
+static const char * /* 0 or error message */
+get_passwd(char *tgt,
+ char *val,
+ struct parm *parmp,
+ u_int16_t type,
+ int safe) /* 1=from secure file */
+{
+ static char buf[80];
+ char *val0, *p, delim;
+ struct auth k, *ap, *ap2;
+ int i;
+ u_long l;
+
+
+ if (!safe)
+ return "ignore unsafe password";
+
+ for (ap = parmp->parm_auth, i = 0;
+ ap->type != RIP_AUTH_NONE; i++, ap++) {
+ if (i >= MAX_AUTH_KEYS)
+ return "too many passwords";
+ }
+
+ memset(&k, 0, sizeof(k));
+ k.type = type;
+ k.end = -1-DAY;
+
+ val0 = val;
+ if (0 > parse_quote(&val, "| ,\n\r", &delim,
+ (char *)k.key, sizeof(k.key)))
+ return tgt;
+
+ if (delim != '|') {
+ if (type == RIP_AUTH_MD5)
+ return "missing Keyid";
+ } else {
+ val0 = ++val;
+ buf[sizeof(buf)-1] = '\0';
+ if (0 > parse_quote(&val, "| ,\n\r", &delim, buf,sizeof(buf))
+ || buf[sizeof(buf)-1] != '\0'
+ || (l = strtoul(buf,&p,0)) > 255
+ || *p != '\0') {
+ sprintf(buf,"bad KeyID \"%.20s\"", val0);
+ return buf;
+ }
+ for (ap2 = parmp->parm_auth; ap2 < ap; ap2++) {
+ if (ap2->keyid == l) {
+ sprintf(buf,"duplicate KeyID \"%.20s\"", val0);
+ return buf;
+ }
+ }
+ k.keyid = (int)l;
+
+ if (delim == '|') {
+ val0 = ++val;
+ if (0 != (p = parse_ts(&k.start,&val,val0,&delim,
+ buf,sizeof(buf))))
+ return p;
+ if (delim != '|')
+ return "missing second timestamp";
+ val0 = ++val;
+ if (0 != (p = parse_ts(&k.end,&val,val0,&delim,
+ buf,sizeof(buf))))
+ return p;
+ if ((u_long)k.start > (u_long)k.end) {
+ sprintf(buf,"out of order timestamp %.30s",
+ val0);
+ return buf;
+ }
+ }
+ }
+ if (delim != '\0')
+ return tgt;
+
+ memmove(ap, &k, sizeof(*ap));
+ return 0;
+}
+
+
+static const char *
+bad_str(const char *estr)
+{
+ static char buf[100+8];
+
+ sprintf(buf, "bad \"%.100s\"", estr);
+ return buf;
+}
+
+
+/* Parse a set of parameters for an interface.
+ */
+const char * /* 0 or error message */
+parse_parms(char *line,
+ int safe) /* 1=from secure file */
+{
+#define PARS(str) (!strcasecmp(tgt, str))
+#define PARSEQ(str) (!strncasecmp(tgt, str"=", sizeof(str)))
+#define CKF(g,b) {if (0 != (parm.parm_int_state & ((g) & ~(b)))) break; \
+ parm.parm_int_state |= (b);}
+ struct parm parm;
+ struct intnet *intnetp;
+ struct r1net *r1netp;
+ struct tgate *tg;
+ naddr addr, mask;
+ char delim, *val0 = 0, *tgt, *val, *p;
+ const char *msg;
+ char buf[BUFSIZ], buf2[BUFSIZ];
+ int i;
+
+
+ /* "subnet=x.y.z.u/mask[,metric]" must be alone on the line */
+ if (!strncasecmp(line, "subnet=", sizeof("subnet=")-1)
+ && *(val = &line[sizeof("subnet=")-1]) != '\0') {
+ if (0 > parse_quote(&val, ",", &delim, buf, sizeof(buf)))
+ return bad_str(line);
+ intnetp = (struct intnet*)rtmalloc(sizeof(*intnetp),
+ "parse_parms subnet");
+ intnetp->intnet_metric = 1;
+ if (delim == ',') {
+ intnetp->intnet_metric = (int)strtol(val+1,&p,0);
+ if (*p != '\0'
+ || intnetp->intnet_metric <= 0
+ || intnetp->intnet_metric >= HOPCNT_INFINITY)
+ return bad_str(line);
+ }
+ if (!getnet(buf, &intnetp->intnet_addr, &intnetp->intnet_mask)
+ || intnetp->intnet_mask == HOST_MASK
+ || intnetp->intnet_addr == RIP_DEFAULT) {
+ free(intnetp);
+ return bad_str(line);
+ }
+ intnetp->intnet_addr = htonl(intnetp->intnet_addr);
+ intnetp->intnet_next = intnets;
+ intnets = intnetp;
+ return 0;
+ }
+
+ /* "ripv1_mask=x.y.z.u/mask1,mask2" must be alone on the line.
+ * This requires that x.y.z.u/mask1 be considered a subnet of
+ * x.y.z.u/mask2, as if x.y.z.u/mask2 were a class-full network.
+ */
+ if (!strncasecmp(line, "ripv1_mask=", sizeof("ripv1_mask=")-1)
+ && *(val = &line[sizeof("ripv1_mask=")-1]) != '\0') {
+ if (0 > parse_quote(&val, ",", &delim, buf, sizeof(buf))
+ || delim == '\0')
+ return bad_str(line);
+ if ((i = (int)strtol(val+1, &p, 0)) <= 0
+ || i > 32 || *p != '\0')
+ return bad_str(line);
+ r1netp = (struct r1net *)rtmalloc(sizeof(*r1netp),
+ "parse_parms ripv1_mask");
+ r1netp->r1net_mask = HOST_MASK << (32-i);
+ if (!getnet(buf, &r1netp->r1net_net, &r1netp->r1net_match)
+ || r1netp->r1net_net == RIP_DEFAULT
+ || r1netp->r1net_mask > r1netp->r1net_match) {
+ free(r1netp);
+ return bad_str(line);
+ }
+ r1netp->r1net_next = r1nets;
+ r1nets = r1netp;
+ return 0;
+ }
+
+ memset(&parm, 0, sizeof(parm));
+
+ for (;;) {
+ tgt = line + strspn(line, " ,\n\r");
+ if (*tgt == '\0' || *tgt == '#')
+ break;
+ line = tgt+strcspn(tgt, "= #,\n\r");
+ delim = *line;
+ if (delim == '=') {
+ val0 = ++line;
+ if (0 > parse_quote(&line, " #,\n\r",&delim,
+ buf,sizeof(buf)))
+ return bad_str(tgt);
+ }
+ if (delim != '\0') {
+ for (;;) {
+ *line = '\0';
+ if (delim == '#')
+ break;
+ ++line;
+ if (delim != ' '
+ || (delim = *line) != ' ')
+ break;
+ }
+ }
+
+ if (PARSEQ("if")) {
+ if (parm.parm_name[0] != '\0'
+ || strlen(buf) > IF_NAME_LEN)
+ return bad_str(tgt);
+ strcpy(parm.parm_name, buf);
+
+ } else if (PARSEQ("addr")) {
+ /* This is a bad idea, because the address based
+ * sets of parameters cannot be checked for
+ * consistency with the interface name parameters.
+ * The parm_net stuff is needed to allow several
+ * -F settings.
+ */
+ if (!getnet(val0, &addr, &mask)
+ || parm.parm_name[0] != '\0')
+ return bad_str(tgt);
+ parm.parm_net = addr;
+ parm.parm_mask = mask;
+ parm.parm_name[0] = '\n';
+
+ } else if (PARSEQ("passwd")) {
+ /* since cleartext passwords are so weak allow
+ * them anywhere
+ */
+ msg = get_passwd(tgt,val0,&parm,RIP_AUTH_PW,1);
+ if (msg) {
+ *val0 = '\0';
+ return bad_str(msg);
+ }
+
+ } else if (PARSEQ("md5_passwd")) {
+ msg = get_passwd(tgt,val0,&parm,RIP_AUTH_MD5,safe);
+ if (msg) {
+ *val0 = '\0';
+ return bad_str(msg);
+ }
+
+ } else if (PARS("no_ag")) {
+ parm.parm_int_state |= (IS_NO_AG | IS_NO_SUPER_AG);
+
+ } else if (PARS("no_super_ag")) {
+ parm.parm_int_state |= IS_NO_SUPER_AG;
+
+ } else if (PARS("no_rip_out")) {
+ parm.parm_int_state |= IS_NO_RIP_OUT;
+
+ } else if (PARS("no_ripv1_in")) {
+ parm.parm_int_state |= IS_NO_RIPV1_IN;
+
+ } else if (PARS("no_ripv2_in")) {
+ parm.parm_int_state |= IS_NO_RIPV2_IN;
+
+ } else if (PARS("ripv2_out")) {
+ if (parm.parm_int_state & IS_NO_RIPV2_OUT)
+ return bad_str(tgt);
+ parm.parm_int_state |= IS_NO_RIPV1_OUT;
+
+ } else if (PARS("ripv2")) {
+ if ((parm.parm_int_state & IS_NO_RIPV2_OUT)
+ || (parm.parm_int_state & IS_NO_RIPV2_IN))
+ return bad_str(tgt);
+ parm.parm_int_state |= (IS_NO_RIPV1_IN
+ | IS_NO_RIPV1_OUT);
+
+ } else if (PARS("no_rip")) {
+ CKF(IS_PM_RDISC, IS_NO_RIP);
+
+ } else if (PARS("no_rip_mcast")) {
+ parm.parm_int_state |= IS_NO_RIP_MCAST;
+
+ } else if (PARS("no_rdisc")) {
+ CKF((GROUP_IS_SOL_OUT|GROUP_IS_ADV_OUT), IS_NO_RDISC);
+
+ } else if (PARS("no_solicit")) {
+ CKF(GROUP_IS_SOL_OUT, IS_NO_SOL_OUT);
+
+ } else if (PARS("send_solicit")) {
+ CKF(GROUP_IS_SOL_OUT, IS_SOL_OUT);
+
+ } else if (PARS("no_rdisc_adv")) {
+ CKF(GROUP_IS_ADV_OUT, IS_NO_ADV_OUT);
+
+ } else if (PARS("rdisc_adv")) {
+ CKF(GROUP_IS_ADV_OUT, IS_ADV_OUT);
+
+ } else if (PARS("bcast_rdisc")) {
+ parm.parm_int_state |= IS_BCAST_RDISC;
+
+ } else if (PARS("passive")) {
+ CKF((GROUP_IS_SOL_OUT|GROUP_IS_ADV_OUT), IS_NO_RDISC);
+ parm.parm_int_state |= IS_NO_RIP | IS_PASSIVE;
+
+ } else if (PARSEQ("rdisc_pref")) {
+ if (parm.parm_rdisc_pref != 0
+ || (parm.parm_rdisc_pref = (int)strtol(buf,&p,0),
+ *p != '\0'))
+ return bad_str(tgt);
+
+ } else if (PARS("pm_rdisc")) {
+ if (IS_RIP_OUT_OFF(parm.parm_int_state))
+ return bad_str(tgt);
+ parm.parm_int_state |= IS_PM_RDISC;
+
+ } else if (PARSEQ("rdisc_interval")) {
+ if (parm.parm_rdisc_int != 0
+ || (parm.parm_rdisc_int = (int)strtoul(buf,&p,0),
+ *p != '\0')
+ || parm.parm_rdisc_int < MinMaxAdvertiseInterval
+ || parm.parm_rdisc_int > MaxMaxAdvertiseInterval)
+ return bad_str(tgt);
+
+ } else if (PARSEQ("fake_default")) {
+ if (parm.parm_d_metric != 0
+ || IS_RIP_OUT_OFF(parm.parm_int_state)
+ || (i = strtoul(buf,&p,0), *p != '\0')
+ || i > HOPCNT_INFINITY-1)
+ return bad_str(tgt);
+ parm.parm_d_metric = i;
+
+ } else if (PARSEQ("adj_inmetric")) {
+ if (parm.parm_adj_inmetric != 0
+ || (i = strtoul(buf,&p,0), *p != '\0')
+ || i > HOPCNT_INFINITY-1)
+ return bad_str(tgt);
+ parm.parm_adj_inmetric = i;
+
+ } else if (PARSEQ("adj_outmetric")) {
+ if (parm.parm_adj_outmetric != 0
+ || (i = strtoul(buf,&p,0), *p != '\0')
+ || i > HOPCNT_INFINITY-1)
+ return bad_str(tgt);
+ parm.parm_adj_outmetric = i;
+
+ } else if (PARSEQ("trust_gateway")) {
+ /* look for trust_gateway=x.y.z|net/mask|...) */
+ p = buf;
+ if (0 > parse_quote(&p, "|", &delim,
+ buf2, sizeof(buf2))
+ || !gethost(buf2,&addr))
+ return bad_str(tgt);
+ tg = (struct tgate *)rtmalloc(sizeof(*tg),
+ "parse_parms"
+ "trust_gateway");
+ memset(tg, 0, sizeof(*tg));
+ tg->tgate_addr = addr;
+ i = 0;
+ /* The default is to trust all routes. */
+ while (delim == '|') {
+ p++;
+ if (i >= MAX_TGATE_NETS
+ || 0 > parse_quote(&p, "|", &delim,
+ buf2, sizeof(buf2))
+ || !getnet(buf2, &tg->tgate_nets[i].net,
+ &tg->tgate_nets[i].mask)
+ || tg->tgate_nets[i].net == RIP_DEFAULT
+ || tg->tgate_nets[i].mask == 0)
+ return bad_str(tgt);
+ i++;
+ }
+ tg->tgate_next = tgates;
+ tgates = tg;
+ parm.parm_int_state |= IS_DISTRUST;
+
+ } else if (PARS("redirect_ok")) {
+ parm.parm_int_state |= IS_REDIRECT_OK;
+
+ } else {
+ return bad_str(tgt); /* error */
+ }
+ }
+
+ return check_parms(&parm);
+#undef PARS
+#undef PARSEQ
+}
+
+
+/* check for duplicate parameter specifications */
+const char * /* 0 or error message */
+check_parms(struct parm *new)
+{
+ struct parm *parmp, **parmpp;
+ int i, num_passwds;
+
+ /* set implicit values
+ */
+ if (new->parm_int_state & IS_NO_ADV_IN)
+ new->parm_int_state |= IS_NO_SOL_OUT;
+ if (new->parm_int_state & IS_NO_SOL_OUT)
+ new->parm_int_state |= IS_NO_ADV_IN;
+
+ for (i = num_passwds = 0; i < MAX_AUTH_KEYS; i++) {
+ if (new->parm_auth[i].type != RIP_AUTH_NONE)
+ num_passwds++;
+ }
+
+ /* compare with existing sets of parameters
+ */
+ for (parmpp = &parms;
+ (parmp = *parmpp) != 0;
+ parmpp = &parmp->parm_next) {
+ if (strcmp(new->parm_name, parmp->parm_name))
+ continue;
+ if (!on_net(htonl(parmp->parm_net),
+ new->parm_net, new->parm_mask)
+ && !on_net(htonl(new->parm_net),
+ parmp->parm_net, parmp->parm_mask))
+ continue;
+
+ for (i = 0; i < MAX_AUTH_KEYS; i++) {
+ if (parmp->parm_auth[i].type != RIP_AUTH_NONE)
+ num_passwds++;
+ }
+ if (num_passwds > MAX_AUTH_KEYS)
+ return "too many conflicting passwords";
+
+ if ((0 != (new->parm_int_state & GROUP_IS_SOL_OUT)
+ && 0 != (parmp->parm_int_state & GROUP_IS_SOL_OUT)
+ && 0 != ((new->parm_int_state ^ parmp->parm_int_state)
+ && GROUP_IS_SOL_OUT))
+ || (0 != (new->parm_int_state & GROUP_IS_ADV_OUT)
+ && 0 != (parmp->parm_int_state & GROUP_IS_ADV_OUT)
+ && 0 != ((new->parm_int_state ^ parmp->parm_int_state)
+ && GROUP_IS_ADV_OUT))
+ || (new->parm_rdisc_pref != 0
+ && parmp->parm_rdisc_pref != 0
+ && new->parm_rdisc_pref != parmp->parm_rdisc_pref)
+ || (new->parm_rdisc_int != 0
+ && parmp->parm_rdisc_int != 0
+ && new->parm_rdisc_int != parmp->parm_rdisc_int)) {
+ return ("conflicting, duplicate router discovery"
+ " parameters");
+
+ }
+
+ if (new->parm_d_metric != 0
+ && parmp->parm_d_metric != 0
+ && new->parm_d_metric != parmp->parm_d_metric) {
+ return ("conflicting, duplicate poor man's router"
+ " discovery or fake default metric");
+ }
+
+ if (new->parm_adj_inmetric != 0
+ && parmp->parm_adj_inmetric != 0
+ && new->parm_adj_inmetric != parmp->parm_adj_inmetric) {
+ return ("conflicting interface input "
+ "metric adjustments");
+ }
+
+ if (new->parm_adj_outmetric != 0
+ && parmp->parm_adj_outmetric != 0
+ && new->parm_adj_outmetric != parmp->parm_adj_outmetric) {
+ return ("conflicting interface output "
+ "metric adjustments");
+ }
+ }
+
+ /* link new entry on the list so that when the entries are scanned,
+ * they affect the result in the order the operator specified.
+ */
+ parmp = (struct parm*)rtmalloc(sizeof(*parmp), "check_parms");
+ memcpy(parmp, new, sizeof(*parmp));
+ *parmpp = parmp;
+
+ return 0;
+}
+
+
+/* get a network number as a name or a number, with an optional "/xx"
+ * netmask.
+ */
+int /* 0=bad */
+getnet(char *name,
+ naddr *netp, /* network in host byte order */
+ naddr *maskp) /* masks are always in host order */
+{
+ int i;
+ struct netent *np;
+ naddr mask; /* in host byte order */
+ struct in_addr in; /* a network and so host byte order */
+ char hname[MAXHOSTNAMELEN+1];
+ char *mname, *p;
+
+
+ /* Detect and separate "1.2.3.4/24"
+ */
+ if (0 != (mname = strrchr(name,'/'))) {
+ i = (int)(mname - name);
+ if (i > (int)sizeof(hname)-1) /* name too long */
+ return 0;
+ memmove(hname, name, i);
+ hname[i] = '\0';
+ mname++;
+ name = hname;
+ }
+
+ np = getnetbyname(name);
+ if (np != 0) {
+ in.s_addr = (naddr)np->n_net;
+ if (0 == (in.s_addr & 0xff000000))
+ in.s_addr <<= 8;
+ if (0 == (in.s_addr & 0xff000000))
+ in.s_addr <<= 8;
+ if (0 == (in.s_addr & 0xff000000))
+ in.s_addr <<= 8;
+ } else if (inet_aton(name, &in) == 1) {
+ in.s_addr = ntohl(in.s_addr);
+ } else if (!mname && !strcasecmp(name,"default")) {
+ in.s_addr = RIP_DEFAULT;
+ } else {
+ return 0;
+ }
+
+ if (!mname) {
+ /* we cannot use the interfaces here because we have not
+ * looked at them yet.
+ */
+ mask = std_mask(htonl(in.s_addr));
+ if ((~mask & in.s_addr) != 0)
+ mask = HOST_MASK;
+ } else {
+ mask = (naddr)strtoul(mname, &p, 0);
+ if (*p != '\0' || mask > 32)
+ return 0;
+ if (mask != 0)
+ mask = HOST_MASK << (32-mask);
+ }
+
+ /* must have mask of 0 with default */
+ if (mask != 0 && in.s_addr == RIP_DEFAULT)
+ return 0;
+ /* no host bits allowed in a network number */
+ if ((~mask & in.s_addr) != 0)
+ return 0;
+ /* require non-zero network number */
+ if ((mask & in.s_addr) == 0 && in.s_addr != RIP_DEFAULT)
+ return 0;
+ if (in.s_addr>>24 == 0 && in.s_addr != RIP_DEFAULT)
+ return 0;
+ if (in.s_addr>>24 == 0xff)
+ return 0;
+
+ *netp = in.s_addr;
+ *maskp = mask;
+ return 1;
+}
+
+
+int /* 0=bad */
+gethost(char *name,
+ naddr *addrp)
+{
+ struct hostent *hp;
+ struct in_addr in;
+
+
+ /* Try for a number first, even in IRIX where gethostbyname()
+ * is smart. This avoids hitting the name server which
+ * might be sick because routing is.
+ */
+ if (inet_aton(name, &in) == 1) {
+ /* get a good number, but check that it it makes some
+ * sense.
+ */
+ if (ntohl(in.s_addr)>>24 == 0
+ || ntohl(in.s_addr)>>24 == 0xff)
+ return 0;
+ *addrp = in.s_addr;
+ return 1;
+ }
+
+ hp = gethostbyname(name);
+ if (hp) {
+ memcpy(addrp, hp->h_addr, sizeof(*addrp));
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/sbin/routed/pathnames.h b/sbin/routed/pathnames.h
new file mode 100644
index 0000000..c45405e
--- /dev/null
+++ b/sbin/routed/pathnames.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ * 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 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.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+#include <paths.h>
+
+#define _PATH_GATEWAYS "/etc/gateways"
+
+/* All remotely requested trace files must either start with this prefix
+ * or be the same as the tracefile specified when the daemon was started.
+ * If this is a directory, routed will create log files in it. That
+ * might be a security problem. However, if bad guys can write in the
+ * default value, /etc, you have far worse security problems than anything
+ * this might do. In other words, it makes no sense to turn this off.
+ *
+ * Leave this undefined, and only the trace file originally specified
+ * when routed was started, if any, will be appended to.
+ */
+#ifndef __NetBSD__
+#define _PATH_TRACE "/etc/routed.trace"
+#else
+#undef _PATH_TRACE
+#endif
diff --git a/sbin/routed/radix.c b/sbin/routed/radix.c
new file mode 100644
index 0000000..7f37688
--- /dev/null
+++ b/sbin/routed/radix.c
@@ -0,0 +1,893 @@
+/*
+ * Copyright (c) 1988, 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.
+ * 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 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.
+ *
+ * @(#)radix.c 8.4 (Berkeley) 11/2/94
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Routines to build and maintain radix trees for routing lookups.
+ */
+
+#include "defs.h"
+
+#ifdef __NetBSD__
+__RCSID("$NetBSD$");
+#elif defined(__FreeBSD__)
+__RCSID("$FreeBSD$");
+#else
+__RCSID("$Revision: 2.23 $");
+#ident "$Revision: 2.23 $"
+#endif
+
+#define log(x, msg) syslog(x, msg)
+#define panic(s) {log(LOG_ERR,s); exit(1);}
+#define min(a,b) (((a)<(b))?(a):(b))
+
+int max_keylen;
+struct radix_mask *rn_mkfreelist;
+struct radix_node_head *mask_rnhead;
+static char *addmask_key;
+static char normal_chars[] = {0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, -1};
+static char *rn_zeros, *rn_ones;
+
+#define rn_masktop (mask_rnhead->rnh_treetop)
+#undef Bcmp
+#define Bcmp(a, b, l) (l == 0 ? 0 \
+ : memcmp((caddr_t)(a), (caddr_t)(b), (size_t)l))
+
+static int rn_satisfies_leaf(char *, struct radix_node *, int);
+
+/*
+ * The data structure for the keys is a radix tree with one way
+ * branching removed. The index rn_b at an internal node n represents a bit
+ * position to be tested. The tree is arranged so that all descendants
+ * of a node n have keys whose bits all agree up to position rn_b - 1.
+ * (We say the index of n is rn_b.)
+ *
+ * There is at least one descendant which has a one bit at position rn_b,
+ * and at least one with a zero there.
+ *
+ * A route is determined by a pair of key and mask. We require that the
+ * bit-wise logical and of the key and mask to be the key.
+ * We define the index of a route to associated with the mask to be
+ * the first bit number in the mask where 0 occurs (with bit number 0
+ * representing the highest order bit).
+ *
+ * We say a mask is normal if every bit is 0, past the index of the mask.
+ * If a node n has a descendant (k, m) with index(m) == index(n) == rn_b,
+ * and m is a normal mask, then the route applies to every descendant of n.
+ * If the index(m) < rn_b, this implies the trailing last few bits of k
+ * before bit b are all 0, (and hence consequently true of every descendant
+ * of n), so the route applies to all descendants of the node as well.
+ *
+ * Similar logic shows that a non-normal mask m such that
+ * index(m) <= index(n) could potentially apply to many children of n.
+ * Thus, for each non-host route, we attach its mask to a list at an internal
+ * node as high in the tree as we can go.
+ *
+ * The present version of the code makes use of normal routes in short-
+ * circuiting an explict mask and compare operation when testing whether
+ * a key satisfies a normal route, and also in remembering the unique leaf
+ * that governs a subtree.
+ */
+
+struct radix_node *
+rn_search(void *v_arg,
+ struct radix_node *head)
+{
+ struct radix_node *x;
+ caddr_t v;
+
+ for (x = head, v = v_arg; x->rn_b >= 0;) {
+ if (x->rn_bmask & v[x->rn_off])
+ x = x->rn_r;
+ else
+ x = x->rn_l;
+ }
+ return (x);
+}
+
+struct radix_node *
+rn_search_m(void *v_arg,
+ struct radix_node *head,
+ void *m_arg)
+{
+ struct radix_node *x;
+ caddr_t v = v_arg, m = m_arg;
+
+ for (x = head; x->rn_b >= 0;) {
+ if ((x->rn_bmask & m[x->rn_off]) &&
+ (x->rn_bmask & v[x->rn_off]))
+ x = x->rn_r;
+ else
+ x = x->rn_l;
+ }
+ return x;
+}
+
+int
+rn_refines(void* m_arg, void *n_arg)
+{
+ caddr_t m = m_arg, n = n_arg;
+ caddr_t lim, lim2 = lim = n + *(u_char *)n;
+ int longer = (*(u_char *)n++) - (int)(*(u_char *)m++);
+ int masks_are_equal = 1;
+
+ if (longer > 0)
+ lim -= longer;
+ while (n < lim) {
+ if (*n & ~(*m))
+ return 0;
+ if (*n++ != *m++)
+ masks_are_equal = 0;
+ }
+ while (n < lim2)
+ if (*n++)
+ return 0;
+ if (masks_are_equal && (longer < 0))
+ for (lim2 = m - longer; m < lim2; )
+ if (*m++)
+ return 1;
+ return (!masks_are_equal);
+}
+
+struct radix_node *
+rn_lookup(void *v_arg, void *m_arg, struct radix_node_head *head)
+{
+ struct radix_node *x;
+ caddr_t netmask = 0;
+
+ if (m_arg) {
+ if ((x = rn_addmask(m_arg, 1, head->rnh_treetop->rn_off)) == 0)
+ return (0);
+ netmask = x->rn_key;
+ }
+ x = rn_match(v_arg, head);
+ if (x && netmask) {
+ while (x && x->rn_mask != netmask)
+ x = x->rn_dupedkey;
+ }
+ return x;
+}
+
+static int
+rn_satisfies_leaf(char *trial,
+ struct radix_node *leaf,
+ int skip)
+{
+ char *cp = trial, *cp2 = leaf->rn_key, *cp3 = leaf->rn_mask;
+ char *cplim;
+ int length = min(*(u_char *)cp, *(u_char *)cp2);
+
+ if (cp3 == 0)
+ cp3 = rn_ones;
+ else
+ length = min(length, *(u_char *)cp3);
+ cplim = cp + length; cp3 += skip; cp2 += skip;
+ for (cp += skip; cp < cplim; cp++, cp2++, cp3++)
+ if ((*cp ^ *cp2) & *cp3)
+ return 0;
+ return 1;
+}
+
+struct radix_node *
+rn_match(void *v_arg,
+ struct radix_node_head *head)
+{
+ caddr_t v = v_arg;
+ struct radix_node *t = head->rnh_treetop, *x;
+ caddr_t cp = v, cp2;
+ caddr_t cplim;
+ struct radix_node *saved_t, *top = t;
+ int off = t->rn_off, vlen = *(u_char *)cp, matched_off;
+ int test, b, rn_b;
+
+ /*
+ * Open code rn_search(v, top) to avoid overhead of extra
+ * subroutine call.
+ */
+ for (; t->rn_b >= 0; ) {
+ if (t->rn_bmask & cp[t->rn_off])
+ t = t->rn_r;
+ else
+ t = t->rn_l;
+ }
+ /*
+ * See if we match exactly as a host destination
+ * or at least learn how many bits match, for normal mask finesse.
+ *
+ * It doesn't hurt us to limit how many bytes to check
+ * to the length of the mask, since if it matches we had a genuine
+ * match and the leaf we have is the most specific one anyway;
+ * if it didn't match with a shorter length it would fail
+ * with a long one. This wins big for class B&C netmasks which
+ * are probably the most common case...
+ */
+ if (t->rn_mask)
+ vlen = *(u_char *)t->rn_mask;
+ cp += off; cp2 = t->rn_key + off; cplim = v + vlen;
+ for (; cp < cplim; cp++, cp2++)
+ if (*cp != *cp2)
+ goto on1;
+ /*
+ * This extra grot is in case we are explicitly asked
+ * to look up the default. Ugh!
+ * Or 255.255.255.255
+ *
+ * In this case, we have a complete match of the key. Unless
+ * the node is one of the roots, we are finished.
+ * If it is the zeros root, then take what we have, prefering
+ * any real data.
+ * If it is the ones root, then pretend the target key was followed
+ * by a byte of zeros.
+ */
+ if (!(t->rn_flags & RNF_ROOT))
+ return t; /* not a root */
+ if (t->rn_dupedkey) {
+ t = t->rn_dupedkey;
+ return t; /* have some real data */
+ }
+ if (*(cp-1) == 0)
+ return t; /* not the ones root */
+ b = 0; /* fake a zero after 255.255.255.255 */
+ goto on2;
+on1:
+ test = (*cp ^ *cp2) & 0xff; /* find first bit that differs */
+ for (b = 7; (test >>= 1) > 0;)
+ b--;
+on2:
+ matched_off = cp - v;
+ b += matched_off << 3;
+ rn_b = -1 - b;
+ /*
+ * If there is a host route in a duped-key chain, it will be first.
+ */
+ if ((saved_t = t)->rn_mask == 0)
+ t = t->rn_dupedkey;
+ for (; t; t = t->rn_dupedkey) {
+ /*
+ * Even if we don't match exactly as a host,
+ * we may match if the leaf we wound up at is
+ * a route to a net.
+ */
+ if (t->rn_flags & RNF_NORMAL) {
+ if (rn_b <= t->rn_b)
+ return t;
+ } else if (rn_satisfies_leaf(v, t, matched_off)) {
+ return t;
+ }
+ }
+ t = saved_t;
+ /* start searching up the tree */
+ do {
+ struct radix_mask *m;
+ t = t->rn_p;
+ if ((m = t->rn_mklist)) {
+ /*
+ * If non-contiguous masks ever become important
+ * we can restore the masking and open coding of
+ * the search and satisfaction test and put the
+ * calculation of "off" back before the "do".
+ */
+ do {
+ if (m->rm_flags & RNF_NORMAL) {
+ if (rn_b <= m->rm_b)
+ return (m->rm_leaf);
+ } else {
+ off = min(t->rn_off, matched_off);
+ x = rn_search_m(v, t, m->rm_mask);
+ while (x && x->rn_mask != m->rm_mask)
+ x = x->rn_dupedkey;
+ if (x && rn_satisfies_leaf(v, x, off))
+ return x;
+ }
+ } while ((m = m->rm_mklist));
+ }
+ } while (t != top);
+ return 0;
+}
+
+#ifdef RN_DEBUG
+int rn_nodenum;
+struct radix_node *rn_clist;
+int rn_saveinfo;
+int rn_debug = 1;
+#endif
+
+struct radix_node *
+rn_newpair(void *v, int b, struct radix_node nodes[2])
+{
+ struct radix_node *tt = nodes, *t = tt + 1;
+ t->rn_b = b; t->rn_bmask = 0x80 >> (b & 7);
+ t->rn_l = tt; t->rn_off = b >> 3;
+ tt->rn_b = -1; tt->rn_key = (caddr_t)v; tt->rn_p = t;
+ tt->rn_flags = t->rn_flags = RNF_ACTIVE;
+#ifdef RN_DEBUG
+ tt->rn_info = rn_nodenum++; t->rn_info = rn_nodenum++;
+ tt->rn_twin = t; tt->rn_ybro = rn_clist; rn_clist = tt;
+#endif
+ return t;
+}
+
+struct radix_node *
+rn_insert(void* v_arg,
+ struct radix_node_head *head,
+ int *dupentry,
+ struct radix_node nodes[2])
+{
+ caddr_t v = v_arg;
+ struct radix_node *top = head->rnh_treetop;
+ int head_off = top->rn_off, vlen = (int)*((u_char *)v);
+ struct radix_node *t = rn_search(v_arg, top);
+ caddr_t cp = v + head_off;
+ int b;
+ struct radix_node *tt;
+
+ /*
+ * Find first bit at which v and t->rn_key differ
+ */
+ {
+ caddr_t cp2 = t->rn_key + head_off;
+ int cmp_res;
+ caddr_t cplim = v + vlen;
+
+ while (cp < cplim)
+ if (*cp2++ != *cp++)
+ goto on1;
+ /* handle adding 255.255.255.255 */
+ if (!(t->rn_flags & RNF_ROOT) || *(cp2-1) == 0) {
+ *dupentry = 1;
+ return t;
+ }
+on1:
+ *dupentry = 0;
+ cmp_res = (cp[-1] ^ cp2[-1]) & 0xff;
+ for (b = (cp - v) << 3; cmp_res; b--)
+ cmp_res >>= 1;
+ }
+ {
+ struct radix_node *p, *x = top;
+ cp = v;
+ do {
+ p = x;
+ if (cp[x->rn_off] & x->rn_bmask)
+ x = x->rn_r;
+ else x = x->rn_l;
+ } while ((unsigned)b > (unsigned)x->rn_b);
+#ifdef RN_DEBUG
+ if (rn_debug)
+ log(LOG_DEBUG, "rn_insert: Going In:\n"), traverse(p);
+#endif
+ t = rn_newpair(v_arg, b, nodes); tt = t->rn_l;
+ if ((cp[p->rn_off] & p->rn_bmask) == 0)
+ p->rn_l = t;
+ else
+ p->rn_r = t;
+ x->rn_p = t; t->rn_p = p; /* frees x, p as temp vars below */
+ if ((cp[t->rn_off] & t->rn_bmask) == 0) {
+ t->rn_r = x;
+ } else {
+ t->rn_r = tt; t->rn_l = x;
+ }
+#ifdef RN_DEBUG
+ if (rn_debug)
+ log(LOG_DEBUG, "rn_insert: Coming Out:\n"), traverse(p);
+#endif
+ }
+ return (tt);
+}
+
+struct radix_node *
+rn_addmask(void *n_arg, int search, int skip)
+{
+ caddr_t netmask = (caddr_t)n_arg;
+ struct radix_node *x;
+ caddr_t cp, cplim;
+ int b = 0, mlen, j;
+ int maskduplicated, m0, isnormal;
+ struct radix_node *saved_x;
+ static int last_zeroed = 0;
+
+ if ((mlen = *(u_char *)netmask) > max_keylen)
+ mlen = max_keylen;
+ if (skip == 0)
+ skip = 1;
+ if (mlen <= skip)
+ return (mask_rnhead->rnh_nodes);
+ if (skip > 1)
+ Bcopy(rn_ones + 1, addmask_key + 1, skip - 1);
+ if ((m0 = mlen) > skip)
+ Bcopy(netmask + skip, addmask_key + skip, mlen - skip);
+ /*
+ * Trim trailing zeroes.
+ */
+ for (cp = addmask_key + mlen; (cp > addmask_key) && cp[-1] == 0;)
+ cp--;
+ mlen = cp - addmask_key;
+ if (mlen <= skip) {
+ if (m0 >= last_zeroed)
+ last_zeroed = mlen;
+ return (mask_rnhead->rnh_nodes);
+ }
+ if (m0 < last_zeroed)
+ Bzero(addmask_key + m0, last_zeroed - m0);
+ *addmask_key = last_zeroed = mlen;
+ x = rn_search(addmask_key, rn_masktop);
+ if (Bcmp(addmask_key, x->rn_key, mlen) != 0)
+ x = 0;
+ if (x || search)
+ return (x);
+ x = (struct radix_node *)rtmalloc(max_keylen + 2*sizeof(*x),
+ "rn_addmask");
+ saved_x = x;
+ Bzero(x, max_keylen + 2 * sizeof (*x));
+ netmask = cp = (caddr_t)(x + 2);
+ Bcopy(addmask_key, cp, mlen);
+ x = rn_insert(cp, mask_rnhead, &maskduplicated, x);
+ if (maskduplicated) {
+ log(LOG_ERR, "rn_addmask: mask impossibly already in tree");
+ Free(saved_x);
+ return (x);
+ }
+ /*
+ * Calculate index of mask, and check for normalcy.
+ */
+ cplim = netmask + mlen; isnormal = 1;
+ for (cp = netmask + skip; (cp < cplim) && *(u_char *)cp == 0xff;)
+ cp++;
+ if (cp != cplim) {
+ for (j = 0x80; (j & *cp) != 0; j >>= 1)
+ b++;
+ if (*cp != normal_chars[b] || cp != (cplim - 1))
+ isnormal = 0;
+ }
+ b += (cp - netmask) << 3;
+ x->rn_b = -1 - b;
+ if (isnormal)
+ x->rn_flags |= RNF_NORMAL;
+ return (x);
+}
+
+static int /* XXX: arbitrary ordering for non-contiguous masks */
+rn_lexobetter(void *m_arg, void *n_arg)
+{
+ u_char *mp = m_arg, *np = n_arg, *lim;
+
+ if (*mp > *np)
+ return 1; /* not really, but need to check longer one first */
+ if (*mp == *np)
+ for (lim = mp + *mp; mp < lim;)
+ if (*mp++ > *np++)
+ return 1;
+ return 0;
+}
+
+static struct radix_mask *
+rn_new_radix_mask(struct radix_node *tt,
+ struct radix_mask *next)
+{
+ struct radix_mask *m;
+
+ MKGet(m);
+ if (m == 0) {
+ log(LOG_ERR, "Mask for route not entered\n");
+ return (0);
+ }
+ Bzero(m, sizeof *m);
+ m->rm_b = tt->rn_b;
+ m->rm_flags = tt->rn_flags;
+ if (tt->rn_flags & RNF_NORMAL)
+ m->rm_leaf = tt;
+ else
+ m->rm_mask = tt->rn_mask;
+ m->rm_mklist = next;
+ tt->rn_mklist = m;
+ return m;
+}
+
+struct radix_node *
+rn_addroute(void *v_arg,
+ void *n_arg,
+ struct radix_node_head *head,
+ struct radix_node treenodes[2])
+{
+ caddr_t v = (caddr_t)v_arg, netmask = (caddr_t)n_arg;
+ struct radix_node *t, *x = 0, *tt;
+ struct radix_node *saved_tt, *top = head->rnh_treetop;
+ short b = 0, b_leaf = 0;
+ int keyduplicated;
+ caddr_t mmask;
+ struct radix_mask *m, **mp;
+
+ /*
+ * In dealing with non-contiguous masks, there may be
+ * many different routes which have the same mask.
+ * We will find it useful to have a unique pointer to
+ * the mask to speed avoiding duplicate references at
+ * nodes and possibly save time in calculating indices.
+ */
+ if (netmask) {
+ if ((x = rn_addmask(netmask, 0, top->rn_off)) == 0)
+ return (0);
+ b_leaf = x->rn_b;
+ b = -1 - x->rn_b;
+ netmask = x->rn_key;
+ }
+ /*
+ * Deal with duplicated keys: attach node to previous instance
+ */
+ saved_tt = tt = rn_insert(v, head, &keyduplicated, treenodes);
+ if (keyduplicated) {
+ for (t = tt; tt; t = tt, tt = tt->rn_dupedkey) {
+ if (tt->rn_mask == netmask)
+ return (0);
+ if (netmask == 0 ||
+ (tt->rn_mask &&
+ ((b_leaf < tt->rn_b) || /* index(netmask) > node */
+ rn_refines(netmask, tt->rn_mask) ||
+ rn_lexobetter(netmask, tt->rn_mask))))
+ break;
+ }
+ /*
+ * If the mask is not duplicated, we wouldn't
+ * find it among possible duplicate key entries
+ * anyway, so the above test doesn't hurt.
+ *
+ * We sort the masks for a duplicated key the same way as
+ * in a masklist -- most specific to least specific.
+ * This may require the unfortunate nuisance of relocating
+ * the head of the list.
+ */
+ if (tt == saved_tt) {
+ struct radix_node *xx = x;
+ /* link in at head of list */
+ (tt = treenodes)->rn_dupedkey = t;
+ tt->rn_flags = t->rn_flags;
+ tt->rn_p = x = t->rn_p;
+ if (x->rn_l == t) x->rn_l = tt; else x->rn_r = tt;
+ saved_tt = tt; x = xx;
+ } else {
+ (tt = treenodes)->rn_dupedkey = t->rn_dupedkey;
+ t->rn_dupedkey = tt;
+ }
+#ifdef RN_DEBUG
+ t=tt+1; tt->rn_info = rn_nodenum++; t->rn_info = rn_nodenum++;
+ tt->rn_twin = t; tt->rn_ybro = rn_clist; rn_clist = tt;
+#endif
+ tt->rn_key = (caddr_t) v;
+ tt->rn_b = -1;
+ tt->rn_flags = RNF_ACTIVE;
+ }
+ /*
+ * Put mask in tree.
+ */
+ if (netmask) {
+ tt->rn_mask = netmask;
+ tt->rn_b = x->rn_b;
+ tt->rn_flags |= x->rn_flags & RNF_NORMAL;
+ }
+ t = saved_tt->rn_p;
+ if (keyduplicated)
+ goto on2;
+ b_leaf = -1 - t->rn_b;
+ if (t->rn_r == saved_tt) x = t->rn_l; else x = t->rn_r;
+ /* Promote general routes from below */
+ if (x->rn_b < 0) {
+ for (mp = &t->rn_mklist; x; x = x->rn_dupedkey)
+ if (x->rn_mask && (x->rn_b >= b_leaf) && x->rn_mklist == 0) {
+ if ((*mp = m = rn_new_radix_mask(x, 0)))
+ mp = &m->rm_mklist;
+ }
+ } else if (x->rn_mklist) {
+ /*
+ * Skip over masks whose index is > that of new node
+ */
+ for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist)
+ if (m->rm_b >= b_leaf)
+ break;
+ t->rn_mklist = m; *mp = 0;
+ }
+on2:
+ /* Add new route to highest possible ancestor's list */
+ if ((netmask == 0) || (b > t->rn_b ))
+ return tt; /* can't lift at all */
+ b_leaf = tt->rn_b;
+ do {
+ x = t;
+ t = t->rn_p;
+ } while (b <= t->rn_b && x != top);
+ /*
+ * Search through routes associated with node to
+ * insert new route according to index.
+ * Need same criteria as when sorting dupedkeys to avoid
+ * double loop on deletion.
+ */
+ for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) {
+ if (m->rm_b < b_leaf)
+ continue;
+ if (m->rm_b > b_leaf)
+ break;
+ if (m->rm_flags & RNF_NORMAL) {
+ mmask = m->rm_leaf->rn_mask;
+ if (tt->rn_flags & RNF_NORMAL) {
+ log(LOG_ERR,
+ "Non-unique normal route, mask not entered");
+ return tt;
+ }
+ } else
+ mmask = m->rm_mask;
+ if (mmask == netmask) {
+ m->rm_refs++;
+ tt->rn_mklist = m;
+ return tt;
+ }
+ if (rn_refines(netmask, mmask) || rn_lexobetter(netmask, mmask))
+ break;
+ }
+ *mp = rn_new_radix_mask(tt, *mp);
+ return tt;
+}
+
+struct radix_node *
+rn_delete(void *v_arg,
+ void *netmask_arg,
+ struct radix_node_head *head)
+{
+ struct radix_node *t, *p, *x, *tt;
+ struct radix_mask *m, *saved_m, **mp;
+ struct radix_node *dupedkey, *saved_tt, *top;
+ caddr_t v, netmask;
+ int b, head_off, vlen;
+
+ v = v_arg;
+ netmask = netmask_arg;
+ x = head->rnh_treetop;
+ tt = rn_search(v, x);
+ head_off = x->rn_off;
+ vlen = *(u_char *)v;
+ saved_tt = tt;
+ top = x;
+ if (tt == 0 ||
+ Bcmp(v + head_off, tt->rn_key + head_off, vlen - head_off))
+ return (0);
+ /*
+ * Delete our route from mask lists.
+ */
+ if (netmask) {
+ if ((x = rn_addmask(netmask, 1, head_off)) == 0)
+ return (0);
+ netmask = x->rn_key;
+ while (tt->rn_mask != netmask)
+ if ((tt = tt->rn_dupedkey) == 0)
+ return (0);
+ }
+ if (tt->rn_mask == 0 || (saved_m = m = tt->rn_mklist) == 0)
+ goto on1;
+ if (tt->rn_flags & RNF_NORMAL) {
+ if (m->rm_leaf != tt || m->rm_refs > 0) {
+ log(LOG_ERR, "rn_delete: inconsistent annotation\n");
+ return 0; /* dangling ref could cause disaster */
+ }
+ } else {
+ if (m->rm_mask != tt->rn_mask) {
+ log(LOG_ERR, "rn_delete: inconsistent annotation\n");
+ goto on1;
+ }
+ if (--m->rm_refs >= 0)
+ goto on1;
+ }
+ b = -1 - tt->rn_b;
+ t = saved_tt->rn_p;
+ if (b > t->rn_b)
+ goto on1; /* Wasn't lifted at all */
+ do {
+ x = t;
+ t = t->rn_p;
+ } while (b <= t->rn_b && x != top);
+ for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist)
+ if (m == saved_m) {
+ *mp = m->rm_mklist;
+ MKFree(m);
+ break;
+ }
+ if (m == 0) {
+ log(LOG_ERR, "rn_delete: couldn't find our annotation\n");
+ if (tt->rn_flags & RNF_NORMAL)
+ return (0); /* Dangling ref to us */
+ }
+on1:
+ /*
+ * Eliminate us from tree
+ */
+ if (tt->rn_flags & RNF_ROOT)
+ return (0);
+#ifdef RN_DEBUG
+ /* Get us out of the creation list */
+ for (t = rn_clist; t && t->rn_ybro != tt; t = t->rn_ybro) {}
+ if (t) t->rn_ybro = tt->rn_ybro;
+#endif
+ t = tt->rn_p;
+ if ((dupedkey = saved_tt->rn_dupedkey)) {
+ if (tt == saved_tt) {
+ x = dupedkey; x->rn_p = t;
+ if (t->rn_l == tt) t->rn_l = x; else t->rn_r = x;
+ } else {
+ for (x = p = saved_tt; p && p->rn_dupedkey != tt;)
+ p = p->rn_dupedkey;
+ if (p) p->rn_dupedkey = tt->rn_dupedkey;
+ else log(LOG_ERR, "rn_delete: couldn't find us\n");
+ }
+ t = tt + 1;
+ if (t->rn_flags & RNF_ACTIVE) {
+#ifndef RN_DEBUG
+ *++x = *t; p = t->rn_p;
+#else
+ b = t->rn_info; *++x = *t; t->rn_info = b; p = t->rn_p;
+#endif
+ if (p->rn_l == t) p->rn_l = x; else p->rn_r = x;
+ x->rn_l->rn_p = x; x->rn_r->rn_p = x;
+ }
+ goto out;
+ }
+ if (t->rn_l == tt) x = t->rn_r; else x = t->rn_l;
+ p = t->rn_p;
+ if (p->rn_r == t) p->rn_r = x; else p->rn_l = x;
+ x->rn_p = p;
+ /*
+ * Demote routes attached to us.
+ */
+ if (t->rn_mklist) {
+ if (x->rn_b >= 0) {
+ for (mp = &x->rn_mklist; (m = *mp);)
+ mp = &m->rm_mklist;
+ *mp = t->rn_mklist;
+ } else {
+ /* If there are any key,mask pairs in a sibling
+ duped-key chain, some subset will appear sorted
+ in the same order attached to our mklist */
+ for (m = t->rn_mklist; m && x; x = x->rn_dupedkey)
+ if (m == x->rn_mklist) {
+ struct radix_mask *mm = m->rm_mklist;
+ x->rn_mklist = 0;
+ if (--(m->rm_refs) < 0)
+ MKFree(m);
+ m = mm;
+ }
+ if (m)
+ syslog(LOG_ERR, "%s 0x%lx at 0x%lx\n",
+ "rn_delete: Orphaned Mask",
+ (unsigned long)m,
+ (unsigned long)x);
+ }
+ }
+ /*
+ * We may be holding an active internal node in the tree.
+ */
+ x = tt + 1;
+ if (t != x) {
+#ifndef RN_DEBUG
+ *t = *x;
+#else
+ b = t->rn_info; *t = *x; t->rn_info = b;
+#endif
+ t->rn_l->rn_p = t; t->rn_r->rn_p = t;
+ p = x->rn_p;
+ if (p->rn_l == x) p->rn_l = t; else p->rn_r = t;
+ }
+out:
+ tt->rn_flags &= ~RNF_ACTIVE;
+ tt[1].rn_flags &= ~RNF_ACTIVE;
+ return (tt);
+}
+
+int
+rn_walktree(struct radix_node_head *h,
+ int (*f)(struct radix_node *, struct walkarg *),
+ struct walkarg *w)
+{
+ int error;
+ struct radix_node *base, *next;
+ struct radix_node *rn = h->rnh_treetop;
+ /*
+ * This gets complicated because we may delete the node
+ * while applying the function f to it, so we need to calculate
+ * the successor node in advance.
+ */
+ /* First time through node, go left */
+ while (rn->rn_b >= 0)
+ rn = rn->rn_l;
+ for (;;) {
+ base = rn;
+ /* If at right child go back up, otherwise, go right */
+ while (rn->rn_p->rn_r == rn && (rn->rn_flags & RNF_ROOT) == 0)
+ rn = rn->rn_p;
+ /* Find the next *leaf* since next node might vanish, too */
+ for (rn = rn->rn_p->rn_r; rn->rn_b >= 0;)
+ rn = rn->rn_l;
+ next = rn;
+ /* Process leaves */
+ while ((rn = base)) {
+ base = rn->rn_dupedkey;
+ if (!(rn->rn_flags & RNF_ROOT) && (error = (*f)(rn, w)))
+ return (error);
+ }
+ rn = next;
+ if (rn->rn_flags & RNF_ROOT)
+ return (0);
+ }
+ /* NOTREACHED */
+}
+
+int
+rn_inithead(void **head, int off)
+{
+ struct radix_node_head *rnh;
+ struct radix_node *t, *tt, *ttt;
+ if (*head)
+ return (1);
+ rnh = (struct radix_node_head *)rtmalloc(sizeof(*rnh), "rn_inithead");
+ Bzero(rnh, sizeof (*rnh));
+ *head = rnh;
+ t = rn_newpair(rn_zeros, off, rnh->rnh_nodes);
+ ttt = rnh->rnh_nodes + 2;
+ t->rn_r = ttt;
+ t->rn_p = t;
+ tt = t->rn_l;
+ tt->rn_flags = t->rn_flags = RNF_ROOT | RNF_ACTIVE;
+ tt->rn_b = -1 - off;
+ *ttt = *tt;
+ ttt->rn_key = rn_ones;
+ rnh->rnh_addaddr = rn_addroute;
+ rnh->rnh_deladdr = rn_delete;
+ rnh->rnh_matchaddr = rn_match;
+ rnh->rnh_lookup = rn_lookup;
+ rnh->rnh_walktree = rn_walktree;
+ rnh->rnh_treetop = t;
+ return (1);
+}
+
+void
+rn_init(void)
+{
+ char *cp, *cplim;
+ if (max_keylen == 0) {
+ printf("rn_init: radix functions require max_keylen be set\n");
+ return;
+ }
+ rn_zeros = (char *)rtmalloc(3 * max_keylen, "rn_init");
+ Bzero(rn_zeros, 3 * max_keylen);
+ rn_ones = cp = rn_zeros + max_keylen;
+ addmask_key = cplim = rn_ones + max_keylen;
+ while (cp < cplim)
+ *cp++ = -1;
+ if (rn_inithead((void **)&mask_rnhead, 0) == 0)
+ panic("rn_init 2");
+}
+
diff --git a/sbin/routed/radix.h b/sbin/routed/radix.h
new file mode 100644
index 0000000..6cf196f
--- /dev/null
+++ b/sbin/routed/radix.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1988, 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.
+ * 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 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.
+ *
+ * @(#)radix.h 8.2 (Berkeley) 10/31/94
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __RADIX_H_
+#define __RADIX_H_
+
+#include <sys/cdefs.h>
+struct walkarg;
+
+/*
+ * Radix search tree node layout.
+ */
+
+struct radix_node {
+ struct radix_mask *rn_mklist; /* list of masks contained in subtree */
+ struct radix_node *rn_p; /* parent */
+ short rn_b; /* bit offset; -1-index(netmask) */
+ char rn_bmask; /* node: mask for bit test*/
+ u_char rn_flags; /* enumerated next */
+#define RNF_NORMAL 1 /* leaf contains normal route */
+#define RNF_ROOT 2 /* leaf is root leaf for tree */
+#define RNF_ACTIVE 4 /* This node is alive (for rtfree) */
+ union {
+ struct { /* leaf only data: */
+ caddr_t rn_Key; /* object of search */
+ caddr_t rn_Mask; /* netmask, if present */
+ struct radix_node *rn_Dupedkey;
+ } rn_leaf;
+ struct { /* node only data: */
+ int rn_Off; /* where to start compare */
+ struct radix_node *rn_L;/* progeny */
+ struct radix_node *rn_R;/* progeny */
+ }rn_node;
+ } rn_u;
+#ifdef RN_DEBUG
+ int rn_info;
+ struct radix_node *rn_twin;
+ struct radix_node *rn_ybro;
+#endif
+};
+
+#define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey
+#define rn_key rn_u.rn_leaf.rn_Key
+#define rn_mask rn_u.rn_leaf.rn_Mask
+#define rn_off rn_u.rn_node.rn_Off
+#define rn_l rn_u.rn_node.rn_L
+#define rn_r rn_u.rn_node.rn_R
+
+/*
+ * Annotations to tree concerning potential routes applying to subtrees.
+ */
+
+extern struct radix_mask {
+ short rm_b; /* bit offset; -1-index(netmask) */
+ char rm_unused; /* cf. rn_bmask */
+ u_char rm_flags; /* cf. rn_flags */
+ struct radix_mask *rm_mklist; /* more masks to try */
+ union {
+ caddr_t rmu_mask; /* the mask */
+ struct radix_node *rmu_leaf; /* for normal routes */
+ } rm_rmu;
+ int rm_refs; /* # of references to this struct */
+} *rn_mkfreelist;
+
+#define rm_mask rm_rmu.rmu_mask
+#define rm_leaf rm_rmu.rmu_leaf /* extra field would make 32 bytes */
+
+#define MKGet(m) {\
+ if (rn_mkfreelist) {\
+ m = rn_mkfreelist; \
+ rn_mkfreelist = (m)->rm_mklist; \
+ } else \
+ m = (struct radix_mask *)rtmalloc(sizeof(*(m)), "MKGet"); }\
+
+#define MKFree(m) { (m)->rm_mklist = rn_mkfreelist; rn_mkfreelist = (m);}
+
+struct radix_node_head {
+ struct radix_node *rnh_treetop;
+ int rnh_addrsize; /* permit, but not require fixed keys */
+ int rnh_pktsize; /* permit, but not require fixed keys */
+ struct radix_node *(*rnh_addaddr) /* add based on sockaddr */
+ (void *v, void *mask,
+ struct radix_node_head *head, struct radix_node nodes[]);
+ struct radix_node *(*rnh_addpkt) /* add based on packet hdr */
+ (void *v, void *mask,
+ struct radix_node_head *head, struct radix_node nodes[]);
+ struct radix_node *(*rnh_deladdr) /* remove based on sockaddr */
+ (void *v, void *mask, struct radix_node_head *head);
+ struct radix_node *(*rnh_delpkt) /* remove based on packet hdr */
+ (void *v, void *mask, struct radix_node_head *head);
+ struct radix_node *(*rnh_matchaddr) /* locate based on sockaddr */
+ (void *v, struct radix_node_head *head);
+ struct radix_node *(*rnh_lookup) /* locate based on sockaddr */
+ (void *v, void *mask, struct radix_node_head *head);
+ struct radix_node *(*rnh_matchpkt) /* locate based on packet hdr */
+ (void *v, struct radix_node_head *head);
+ int (*rnh_walktree) /* traverse tree */
+ (struct radix_node_head *head,
+ int (*f)(struct radix_node *, struct walkarg *),
+ struct walkarg *w);
+ struct radix_node rnh_nodes[3]; /* empty tree for common case */
+};
+
+
+#define Bcmp(a, b, n) memcmp(((void *)(a)), ((void *)(b)), (n))
+#define Bcopy(a, b, n) memmove(((void *)(b)), ((void *)(a)), (size_t)(n))
+#define Bzero(p, n) memset((void *)(p), 0, (size_t)(n));
+#define Free(p) free((void *)p);
+
+void rn_init(void);
+int rn_inithead(void **, int);
+int rn_refines(void *, void *);
+int rn_walktree(struct radix_node_head *,
+ int (*)(struct radix_node *, struct walkarg *),
+ struct walkarg *);
+
+struct radix_node
+ *rn_addmask(void *, int, int),
+ *rn_addroute(void *, void *, struct radix_node_head *,
+ struct radix_node [2]),
+ *rn_delete(void *, void *, struct radix_node_head *),
+ *rn_insert(void *, struct radix_node_head *, int *,
+ struct radix_node [2]),
+ *rn_match(void *, struct radix_node_head *),
+ *rn_newpair(void *, int, struct radix_node[2]),
+ *rn_search(void *, struct radix_node *),
+ *rn_search_m(void *, struct radix_node *, void *);
+
+struct radix_node *rn_lookup(void *, void *, struct radix_node_head *);
+
+#endif /* __RADIX_H_ */
diff --git a/sbin/routed/rdisc.c b/sbin/routed/rdisc.c
new file mode 100644
index 0000000..047b3d1
--- /dev/null
+++ b/sbin/routed/rdisc.c
@@ -0,0 +1,1076 @@
+/*
+ * Copyright (c) 1995
+ * 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.
+ * 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#ifdef __NetBSD__
+__RCSID("$NetBSD$");
+#elif defined(__FreeBSD__)
+__RCSID("$FreeBSD$");
+#else
+__RCSID("$Revision: 2.27 $");
+#ident "$Revision: 2.27 $"
+#endif
+
+/* router advertisement ICMP packet */
+struct icmp_ad {
+ u_int8_t icmp_type; /* type of message */
+ u_int8_t icmp_code; /* type sub code */
+ u_int16_t icmp_cksum; /* ones complement cksum of struct */
+ u_int8_t icmp_ad_num; /* # of following router addresses */
+ u_int8_t icmp_ad_asize; /* 2--words in each advertisement */
+ u_int16_t icmp_ad_life; /* seconds of validity */
+ struct icmp_ad_info {
+ n_long icmp_ad_addr;
+ n_long icmp_ad_pref;
+ } icmp_ad_info[1];
+};
+
+/* router solicitation ICMP packet */
+struct icmp_so {
+ u_int8_t icmp_type; /* type of message */
+ u_int8_t icmp_code; /* type sub code */
+ u_int16_t icmp_cksum; /* ones complement cksum of struct */
+ n_long icmp_so_rsvd;
+};
+
+union ad_u {
+ struct icmp icmp;
+ struct icmp_ad ad;
+ struct icmp_so so;
+};
+
+
+int rdisc_sock = -1; /* router-discovery raw socket */
+struct interface *rdisc_sock_mcast; /* current multicast interface */
+
+struct timeval rdisc_timer;
+int rdisc_ok; /* using solicited route */
+
+
+#define MAX_ADS 16 /* at least one per interface */
+struct dr { /* accumulated advertisements */
+ struct interface *dr_ifp;
+ naddr dr_gate; /* gateway */
+ time_t dr_ts; /* when received */
+ time_t dr_life; /* lifetime in host byte order */
+ n_long dr_recv_pref; /* received but biased preference */
+ n_long dr_pref; /* preference adjusted by metric */
+} *cur_drp, drs[MAX_ADS];
+
+/* convert between signed, balanced around zero,
+ * and unsigned zero-based preferences */
+#define SIGN_PREF(p) ((p) ^ MIN_PreferenceLevel)
+#define UNSIGN_PREF(p) SIGN_PREF(p)
+/* adjust unsigned preference by interface metric,
+ * without driving it to infinity */
+#define PREF(p, ifp) ((int)(p) <= ((ifp)->int_metric+(ifp)->int_adj_outmetric)\
+ ? ((p) != 0 ? 1 : 0) \
+ : (p) - ((ifp)->int_metric+(ifp)->int_adj_outmetric))
+
+static void rdisc_sort(void);
+
+
+/* dump an ICMP Router Discovery Advertisement Message
+ */
+static void
+trace_rdisc(const char *act,
+ naddr from,
+ naddr to,
+ struct interface *ifp,
+ union ad_u *p,
+ u_int len)
+{
+ int i;
+ n_long *wp, *lim;
+
+
+ if (!TRACEPACKETS || ftrace == 0)
+ return;
+
+ lastlog();
+
+ if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
+ (void)fprintf(ftrace, "%s Router Ad"
+ " from %s to %s via %s life=%d\n",
+ act, naddr_ntoa(from), naddr_ntoa(to),
+ ifp ? ifp->int_name : "?",
+ ntohs(p->ad.icmp_ad_life));
+ if (!TRACECONTENTS)
+ return;
+
+ wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
+ lim = &wp[(len - sizeof(p->ad)) / sizeof(*wp)];
+ for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) {
+ (void)fprintf(ftrace, "\t%s preference=%d",
+ naddr_ntoa(wp[0]), (int)ntohl(wp[1]));
+ wp += p->ad.icmp_ad_asize;
+ }
+ (void)fputc('\n',ftrace);
+
+ } else {
+ trace_act("%s Router Solic. from %s to %s via %s value=%#x",
+ act, naddr_ntoa(from), naddr_ntoa(to),
+ ifp ? ifp->int_name : "?",
+ (int)ntohl(p->so.icmp_so_rsvd));
+ }
+}
+
+/* prepare Router Discovery socket.
+ */
+static void
+get_rdisc_sock(void)
+{
+ if (rdisc_sock < 0) {
+ rdisc_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (rdisc_sock < 0)
+ BADERR(1,"rdisc_sock = socket()");
+ fix_sock(rdisc_sock,"rdisc_sock");
+ fix_select();
+ }
+}
+
+
+/* Pick multicast group for router-discovery socket
+ */
+void
+set_rdisc_mg(struct interface *ifp,
+ int on) /* 0=turn it off */
+{
+ struct ip_mreq m;
+
+ if (rdisc_sock < 0) {
+ /* Create the raw socket so that we can hear at least
+ * broadcast router discovery packets.
+ */
+ if ((ifp->int_state & IS_NO_RDISC) == IS_NO_RDISC
+ || !on)
+ return;
+ get_rdisc_sock();
+ }
+
+ if (!(ifp->int_if_flags & IFF_MULTICAST)) {
+ ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS);
+ return;
+ }
+
+#ifdef MCAST_PPP_BUG
+ if (ifp->int_if_flags & IFF_POINTOPOINT)
+ return;
+#endif
+ memset(&m, 0, sizeof(m));
+#ifdef MCAST_IFINDEX
+ m.imr_interface.s_addr = htonl(ifp->int_index);
+#else
+ m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT)
+ ? ifp->int_dstaddr
+ : ifp->int_addr);
+#endif
+ if (supplier
+ || (ifp->int_state & IS_NO_ADV_IN)
+ || !on) {
+ /* stop listening to advertisements
+ */
+ if (ifp->int_state & IS_ALL_HOSTS) {
+ m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+ if (setsockopt(rdisc_sock, IPPROTO_IP,
+ IP_DROP_MEMBERSHIP,
+ &m, sizeof(m)) < 0)
+ LOGERR("IP_DROP_MEMBERSHIP ALLHOSTS");
+ ifp->int_state &= ~IS_ALL_HOSTS;
+ }
+
+ } else if (!(ifp->int_state & IS_ALL_HOSTS)) {
+ /* start listening to advertisements
+ */
+ m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+ if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &m, sizeof(m)) < 0) {
+ LOGERR("IP_ADD_MEMBERSHIP ALLHOSTS");
+ } else {
+ ifp->int_state |= IS_ALL_HOSTS;
+ }
+ }
+
+ if (!supplier
+ || (ifp->int_state & IS_NO_ADV_OUT)
+ || !on) {
+ /* stop listening to solicitations
+ */
+ if (ifp->int_state & IS_ALL_ROUTERS) {
+ m.imr_multiaddr.s_addr=htonl(INADDR_ALLROUTERS_GROUP);
+ if (setsockopt(rdisc_sock, IPPROTO_IP,
+ IP_DROP_MEMBERSHIP,
+ &m, sizeof(m)) < 0)
+ LOGERR("IP_DROP_MEMBERSHIP ALLROUTERS");
+ ifp->int_state &= ~IS_ALL_ROUTERS;
+ }
+
+ } else if (!(ifp->int_state & IS_ALL_ROUTERS)) {
+ /* start hearing solicitations
+ */
+ m.imr_multiaddr.s_addr=htonl(INADDR_ALLROUTERS_GROUP);
+ if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &m, sizeof(m)) < 0) {
+ LOGERR("IP_ADD_MEMBERSHIP ALLROUTERS");
+ } else {
+ ifp->int_state |= IS_ALL_ROUTERS;
+ }
+ }
+}
+
+
+/* start supplying routes
+ */
+void
+set_supplier(void)
+{
+ struct interface *ifp;
+ struct dr *drp;
+
+ if (supplier_set)
+ return;
+
+ trace_act("start supplying routes");
+
+ /* Forget discovered routes.
+ */
+ for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
+ drp->dr_recv_pref = 0;
+ drp->dr_life = 0;
+ }
+ rdisc_age(0);
+
+ supplier_set = 1;
+ supplier = 1;
+
+ /* Do not start advertising until we have heard some RIP routes */
+ LIM_SEC(rdisc_timer, now.tv_sec+MIN_WAITTIME);
+
+ /* Switch router discovery multicast groups from soliciting
+ * to advertising.
+ */
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_state & IS_BROKE)
+ continue;
+ ifp->int_rdisc_cnt = 0;
+ ifp->int_rdisc_timer.tv_usec = rdisc_timer.tv_usec;
+ ifp->int_rdisc_timer.tv_sec = now.tv_sec+MIN_WAITTIME;
+ set_rdisc_mg(ifp, 1);
+ }
+
+ /* get rid of any redirects */
+ del_redirects(0,0);
+}
+
+
+/* age discovered routes and find the best one
+ */
+void
+rdisc_age(naddr bad_gate)
+{
+ time_t sec;
+ struct dr *drp;
+
+
+ /* If only advertising, then do only that. */
+ if (supplier) {
+ /* If switching from client to server, get rid of old
+ * default routes.
+ */
+ if (cur_drp != 0)
+ rdisc_sort();
+ rdisc_adv();
+ return;
+ }
+
+ /* If we are being told about a bad router,
+ * then age the discovered default route, and if there is
+ * no alternative, solicit a replacement.
+ */
+ if (bad_gate != 0) {
+ /* Look for the bad discovered default route.
+ * Age it and note its interface.
+ */
+ for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
+ if (drp->dr_ts == 0)
+ continue;
+
+ /* When we find the bad router, then age the route
+ * to at most SUPPLY_INTERVAL.
+ * This is contrary to RFC 1256, but defends against
+ * black holes.
+ */
+ if (drp->dr_gate == bad_gate) {
+ sec = (now.tv_sec - drp->dr_life
+ + SUPPLY_INTERVAL);
+ if (drp->dr_ts > sec) {
+ trace_act("age 0.0.0.0 --> %s via %s",
+ naddr_ntoa(drp->dr_gate),
+ drp->dr_ifp->int_name);
+ drp->dr_ts = sec;
+ }
+ break;
+ }
+ }
+ }
+
+ rdisc_sol();
+ rdisc_sort();
+
+ /* Delete old redirected routes to keep the kernel table small,
+ * and to prevent black holes. Check that the kernel table
+ * matches the daemon table (i.e. has the default route).
+ * But only if RIP is not running and we are not dealing with
+ * a bad gateway, since otherwise age() will be called.
+ */
+ if (rip_sock < 0 && bad_gate == 0)
+ age(0);
+}
+
+
+/* Zap all routes discovered via an interface that has gone bad
+ * This should only be called when !(ifp->int_state & IS_ALIAS)
+ */
+void
+if_bad_rdisc(struct interface *ifp)
+{
+ struct dr *drp;
+
+ for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
+ if (drp->dr_ifp != ifp)
+ continue;
+ drp->dr_recv_pref = 0;
+ drp->dr_ts = 0;
+ drp->dr_life = 0;
+ }
+
+ /* make a note to re-solicit, turn RIP on or off, etc. */
+ rdisc_timer.tv_sec = 0;
+}
+
+
+/* mark an interface ok for router discovering.
+ */
+void
+if_ok_rdisc(struct interface *ifp)
+{
+ set_rdisc_mg(ifp, 1);
+
+ ifp->int_rdisc_cnt = 0;
+ ifp->int_rdisc_timer.tv_sec = now.tv_sec + (supplier
+ ? MIN_WAITTIME
+ : MAX_SOLICITATION_DELAY);
+ if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
+ rdisc_timer = ifp->int_rdisc_timer;
+}
+
+
+/* get rid of a dead discovered router
+ */
+static void
+del_rdisc(struct dr *drp)
+{
+ struct interface *ifp;
+ naddr gate;
+ int i;
+
+
+ del_redirects(gate = drp->dr_gate, 0);
+ drp->dr_ts = 0;
+ drp->dr_life = 0;
+
+
+ /* Count the other discovered routes on the interface.
+ */
+ i = 0;
+ ifp = drp->dr_ifp;
+ for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
+ if (drp->dr_ts != 0
+ && drp->dr_ifp == ifp)
+ i++;
+ }
+
+ /* If that was the last good discovered router on the interface,
+ * then solicit a new one.
+ * This is contrary to RFC 1256, but defends against black holes.
+ */
+ if (i != 0) {
+ trace_act("discovered router %s via %s"
+ " is bad--have %d remaining",
+ naddr_ntoa(gate), ifp->int_name, i);
+ } else if (ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) {
+ trace_act("last discovered router %s via %s"
+ " is bad--re-solicit",
+ naddr_ntoa(gate), ifp->int_name);
+ ifp->int_rdisc_cnt = 0;
+ ifp->int_rdisc_timer.tv_sec = 0;
+ rdisc_sol();
+ } else {
+ trace_act("last discovered router %s via %s"
+ " is bad--wait to solicit",
+ naddr_ntoa(gate), ifp->int_name);
+ }
+}
+
+
+/* Find the best discovered route,
+ * and discard stale routers.
+ */
+static void
+rdisc_sort(void)
+{
+ struct dr *drp, *new_drp;
+ struct rt_entry *rt;
+ struct rt_spare new;
+ struct interface *ifp;
+ u_int new_st = 0;
+ n_long new_pref = 0;
+
+
+ /* Find the best discovered route.
+ */
+ new_drp = 0;
+ for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
+ if (drp->dr_ts == 0)
+ continue;
+ ifp = drp->dr_ifp;
+
+ /* Get rid of expired discovered routers.
+ */
+ if (drp->dr_ts + drp->dr_life <= now.tv_sec) {
+ del_rdisc(drp);
+ continue;
+ }
+
+ LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life+1);
+
+ /* Update preference with possibly changed interface
+ * metric.
+ */
+ drp->dr_pref = PREF(drp->dr_recv_pref, ifp);
+
+ /* Prefer the current route to prevent thrashing.
+ * Prefer shorter lifetimes to speed the detection of
+ * bad routers.
+ * Avoid sick interfaces.
+ */
+ if (new_drp == 0
+ || (!((new_st ^ drp->dr_ifp->int_state) & IS_SICK)
+ && (new_pref < drp->dr_pref
+ || (new_pref == drp->dr_pref
+ && (drp == cur_drp
+ || (new_drp != cur_drp
+ && new_drp->dr_life > drp->dr_life)))))
+ || ((new_st & IS_SICK)
+ && !(drp->dr_ifp->int_state & IS_SICK))) {
+ new_drp = drp;
+ new_st = drp->dr_ifp->int_state;
+ new_pref = drp->dr_pref;
+ }
+ }
+
+ /* switch to a better default route
+ */
+ if (new_drp != cur_drp) {
+ rt = rtget(RIP_DEFAULT, 0);
+
+ /* Stop using discovered routes if they are all bad
+ */
+ if (new_drp == 0) {
+ trace_act("turn off Router Discovery client");
+ rdisc_ok = 0;
+
+ if (rt != 0
+ && (rt->rt_state & RS_RDISC)) {
+ new = rt->rt_spares[0];
+ new.rts_metric = HOPCNT_INFINITY;
+ new.rts_time = now.tv_sec - GARBAGE_TIME;
+ rtchange(rt, rt->rt_state & ~RS_RDISC,
+ &new, 0);
+ rtswitch(rt, 0);
+ }
+
+ } else {
+ if (cur_drp == 0) {
+ trace_act("turn on Router Discovery client"
+ " using %s via %s",
+ naddr_ntoa(new_drp->dr_gate),
+ new_drp->dr_ifp->int_name);
+ rdisc_ok = 1;
+
+ } else {
+ trace_act("switch Router Discovery from"
+ " %s via %s to %s via %s",
+ naddr_ntoa(cur_drp->dr_gate),
+ cur_drp->dr_ifp->int_name,
+ naddr_ntoa(new_drp->dr_gate),
+ new_drp->dr_ifp->int_name);
+ }
+
+ memset(&new, 0, sizeof(new));
+ new.rts_ifp = new_drp->dr_ifp;
+ new.rts_gate = new_drp->dr_gate;
+ new.rts_router = new_drp->dr_gate;
+ new.rts_metric = HOPCNT_INFINITY-1;
+ new.rts_time = now.tv_sec;
+ if (rt != 0) {
+ rtchange(rt, rt->rt_state | RS_RDISC, &new, 0);
+ } else {
+ rtadd(RIP_DEFAULT, 0, RS_RDISC, &new);
+ }
+ }
+
+ cur_drp = new_drp;
+ }
+
+ /* turn RIP on or off */
+ if (!rdisc_ok || rip_interfaces > 1) {
+ rip_on(0);
+ } else {
+ rip_off();
+ }
+}
+
+
+/* handle a single address in an advertisement
+ */
+static void
+parse_ad(naddr from,
+ naddr gate,
+ n_long pref, /* signed and in network order */
+ u_short life, /* in host byte order */
+ struct interface *ifp)
+{
+ static struct msg_limit bad_gate;
+ struct dr *drp, *new_drp;
+
+
+ if (gate == RIP_DEFAULT
+ || !check_dst(gate)) {
+ msglim(&bad_gate, from,"router %s advertising bad gateway %s",
+ naddr_ntoa(from),
+ naddr_ntoa(gate));
+ return;
+ }
+
+ /* ignore pointers to ourself and routes via unreachable networks
+ */
+ if (ifwithaddr(gate, 1, 0) != 0) {
+ trace_pkt(" discard Router Discovery Ad pointing at us");
+ return;
+ }
+ if (!on_net(gate, ifp->int_net, ifp->int_mask)) {
+ trace_pkt(" discard Router Discovery Ad"
+ " toward unreachable net");
+ return;
+ }
+
+ /* Convert preference to an unsigned value
+ * and later bias it by the metric of the interface.
+ */
+ pref = UNSIGN_PREF(ntohl(pref));
+
+ if (pref == 0 || life < MinMaxAdvertiseInterval) {
+ pref = 0;
+ life = 0;
+ }
+
+ for (new_drp = 0, drp = drs; drp < &drs[MAX_ADS]; drp++) {
+ /* accept new info for a familiar entry
+ */
+ if (drp->dr_gate == gate) {
+ new_drp = drp;
+ break;
+ }
+
+ if (life == 0)
+ continue; /* do not worry about dead ads */
+
+ if (drp->dr_ts == 0) {
+ new_drp = drp; /* use unused entry */
+
+ } else if (new_drp == 0) {
+ /* look for an entry worse than the new one to
+ * reuse.
+ */
+ if ((!(ifp->int_state & IS_SICK)
+ && (drp->dr_ifp->int_state & IS_SICK))
+ || (pref > drp->dr_pref
+ && !((ifp->int_state ^ drp->dr_ifp->int_state)
+ & IS_SICK)))
+ new_drp = drp;
+
+ } else if (new_drp->dr_ts != 0) {
+ /* look for the least valuable entry to reuse
+ */
+ if ((!(new_drp->dr_ifp->int_state & IS_SICK)
+ && (drp->dr_ifp->int_state & IS_SICK))
+ || (new_drp->dr_pref > drp->dr_pref
+ && !((new_drp->dr_ifp->int_state
+ ^ drp->dr_ifp->int_state)
+ & IS_SICK)))
+ new_drp = drp;
+ }
+ }
+
+ /* forget it if all of the current entries are better */
+ if (new_drp == 0)
+ return;
+
+ new_drp->dr_ifp = ifp;
+ new_drp->dr_gate = gate;
+ new_drp->dr_ts = now.tv_sec;
+ new_drp->dr_life = life;
+ new_drp->dr_recv_pref = pref;
+ /* bias functional preference by metric of the interface */
+ new_drp->dr_pref = PREF(pref,ifp);
+
+ /* after hearing a good advertisement, stop asking
+ */
+ if (!(ifp->int_state & IS_SICK))
+ ifp->int_rdisc_cnt = MAX_SOLICITATIONS;
+}
+
+
+/* Compute the IP checksum
+ * This assumes the packet is less than 32K long.
+ */
+static u_short
+in_cksum(u_short *p,
+ u_int len)
+{
+ u_int sum = 0;
+ int nwords = len >> 1;
+
+ while (nwords-- != 0)
+ sum += *p++;
+
+ if (len & 1)
+ sum += *(u_char *)p;
+
+ /* end-around-carry */
+ sum = (sum >> 16) + (sum & 0xffff);
+ sum += (sum >> 16);
+ return (~sum);
+}
+
+
+/* Send a router discovery advertisement or solicitation ICMP packet.
+ */
+static void
+send_rdisc(union ad_u *p,
+ int p_size,
+ struct interface *ifp,
+ naddr dst, /* 0 or unicast destination */
+ int type) /* 0=unicast, 1=bcast, 2=mcast */
+{
+ struct sockaddr_in rsin;
+ int flags;
+ const char *msg;
+ naddr tgt_mcast;
+
+
+ memset(&rsin, 0, sizeof(rsin));
+ rsin.sin_addr.s_addr = dst;
+ rsin.sin_family = AF_INET;
+#ifdef _HAVE_SIN_LEN
+ rsin.sin_len = sizeof(rsin);
+#endif
+ flags = MSG_DONTROUTE;
+
+ switch (type) {
+ case 0: /* unicast */
+ default:
+ msg = "Send";
+ break;
+
+ case 1: /* broadcast */
+ if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ msg = "Send pt-to-pt";
+ rsin.sin_addr.s_addr = ifp->int_dstaddr;
+ } else {
+ msg = "Send broadcast";
+ rsin.sin_addr.s_addr = ifp->int_brdaddr;
+ }
+ break;
+
+ case 2: /* multicast */
+ msg = "Send multicast";
+ if (ifp->int_state & IS_DUP) {
+ trace_act("abort multicast output via %s"
+ " with duplicate address",
+ ifp->int_name);
+ return;
+ }
+ if (rdisc_sock_mcast != ifp) {
+ /* select the right interface. */
+#ifdef MCAST_IFINDEX
+ /* specify ifindex */
+ tgt_mcast = htonl(ifp->int_index);
+#else
+#ifdef MCAST_PPP_BUG
+ /* Do not specify the primary interface explicitly
+ * if we have the multicast point-to-point kernel
+ * bug, since the kernel will do the wrong thing
+ * if the local address of a point-to-point link
+ * is the same as the address of an ordinary
+ * interface.
+ */
+ if (ifp->int_addr == myaddr) {
+ tgt_mcast = 0;
+ } else
+#endif
+ tgt_mcast = ifp->int_addr;
+#endif
+ if (0 > setsockopt(rdisc_sock,
+ IPPROTO_IP, IP_MULTICAST_IF,
+ &tgt_mcast, sizeof(tgt_mcast))) {
+ LOGERR("setsockopt(rdisc_sock,"
+ "IP_MULTICAST_IF)");
+ rdisc_sock_mcast = 0;
+ return;
+ }
+ rdisc_sock_mcast = ifp;
+ }
+ flags = 0;
+ break;
+ }
+
+ if (rdisc_sock < 0)
+ get_rdisc_sock();
+
+ trace_rdisc(msg, ifp->int_addr, rsin.sin_addr.s_addr, ifp,
+ p, p_size);
+
+ if (0 > sendto(rdisc_sock, p, p_size, flags,
+ (struct sockaddr *)&rsin, sizeof(rsin))) {
+ if (ifp == 0 || !(ifp->int_state & IS_BROKE))
+ msglog("sendto(%s%s%s): %s",
+ ifp != 0 ? ifp->int_name : "",
+ ifp != 0 ? ", " : "",
+ inet_ntoa(rsin.sin_addr),
+ strerror(errno));
+ if (ifp != 0)
+ if_sick(ifp);
+ }
+}
+
+
+/* Send an advertisement
+ */
+static void
+send_adv(struct interface *ifp,
+ naddr dst, /* 0 or unicast destination */
+ int type) /* 0=unicast, 1=bcast, 2=mcast */
+{
+ union ad_u u;
+ n_long pref;
+
+
+ memset(&u, 0, sizeof(u.ad));
+
+ u.ad.icmp_type = ICMP_ROUTERADVERT;
+ u.ad.icmp_ad_num = 1;
+ u.ad.icmp_ad_asize = sizeof(u.ad.icmp_ad_info[0])/4;
+
+ u.ad.icmp_ad_life = stopint ? 0 : htons(ifp->int_rdisc_int*3);
+
+ /* Convert the configured preference to an unsigned value,
+ * bias it by the interface metric, and then send it as a
+ * signed, network byte order value.
+ */
+ pref = UNSIGN_PREF(ifp->int_rdisc_pref);
+ u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(SIGN_PREF(PREF(pref, ifp)));
+
+ u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr;
+
+ u.ad.icmp_cksum = in_cksum((u_short*)&u.ad, sizeof(u.ad));
+
+ send_rdisc(&u, sizeof(u.ad), ifp, dst, type);
+}
+
+
+/* Advertise for Router Discovery
+ */
+void
+rdisc_adv(void)
+{
+ struct interface *ifp;
+
+ if (!supplier)
+ return;
+
+ rdisc_timer.tv_sec = now.tv_sec + NEVER;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (0 != (ifp->int_state & (IS_NO_ADV_OUT | IS_BROKE)))
+ continue;
+
+ if (!timercmp(&ifp->int_rdisc_timer, &now, >)
+ || stopint) {
+ send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP),
+ (ifp->int_state&IS_BCAST_RDISC) ? 1 : 2);
+ ifp->int_rdisc_cnt++;
+
+ intvl_random(&ifp->int_rdisc_timer,
+ (ifp->int_rdisc_int*3)/4,
+ ifp->int_rdisc_int);
+ if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS
+ && (ifp->int_rdisc_timer.tv_sec
+ > MAX_INITIAL_ADVERT_INTERVAL)) {
+ ifp->int_rdisc_timer.tv_sec
+ = MAX_INITIAL_ADVERT_INTERVAL;
+ }
+ timevaladd(&ifp->int_rdisc_timer, &now);
+ }
+
+ if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
+ rdisc_timer = ifp->int_rdisc_timer;
+ }
+}
+
+
+/* Solicit for Router Discovery
+ */
+void
+rdisc_sol(void)
+{
+ struct interface *ifp;
+ union ad_u u;
+
+
+ if (supplier)
+ return;
+
+ rdisc_timer.tv_sec = now.tv_sec + NEVER;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (0 != (ifp->int_state & (IS_NO_SOL_OUT | IS_BROKE))
+ || ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
+ continue;
+
+ if (!timercmp(&ifp->int_rdisc_timer, &now, >)) {
+ memset(&u, 0, sizeof(u.so));
+ u.so.icmp_type = ICMP_ROUTERSOLICIT;
+ u.so.icmp_cksum = in_cksum((u_short*)&u.so,
+ sizeof(u.so));
+ send_rdisc(&u, sizeof(u.so), ifp,
+ htonl(INADDR_ALLROUTERS_GROUP),
+ ((ifp->int_state&IS_BCAST_RDISC) ? 1 : 2));
+
+ if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
+ continue;
+
+ ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL;
+ ifp->int_rdisc_timer.tv_usec = 0;
+ timevaladd(&ifp->int_rdisc_timer, &now);
+ }
+
+ if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
+ rdisc_timer = ifp->int_rdisc_timer;
+ }
+}
+
+
+/* check the IP header of a possible Router Discovery ICMP packet */
+static struct interface * /* 0 if bad */
+ck_icmp(const char *act,
+ naddr from,
+ struct interface *ifp,
+ naddr to,
+ union ad_u *p,
+ u_int len)
+{
+ const char *type;
+
+
+ if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
+ type = "advertisement";
+ } else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) {
+ type = "solicitation";
+ } else {
+ return 0;
+ }
+
+ if (p->icmp.icmp_code != 0) {
+ trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s",
+ type, p->icmp.icmp_code,
+ naddr_ntoa(from), naddr_ntoa(to));
+ return 0;
+ }
+
+ trace_rdisc(act, from, to, ifp, p, len);
+
+ if (ifp == 0)
+ trace_pkt("unknown interface for router-discovery %s"
+ " from %s to %s",
+ type, naddr_ntoa(from), naddr_ntoa(to));
+
+ return ifp;
+}
+
+
+/* read packets from the router discovery socket
+ */
+void
+read_d(void)
+{
+ static struct msg_limit bad_asize, bad_len;
+#ifdef USE_PASSIFNAME
+ static struct msg_limit bad_name;
+#endif
+ struct sockaddr_in from;
+ int n, fromlen, cc, hlen;
+ struct {
+#ifdef USE_PASSIFNAME
+ char ifname[IFNAMSIZ];
+#endif
+ union {
+ struct ip ip;
+ u_short s[512/2];
+ u_char b[512];
+ } pkt;
+ } buf;
+ union ad_u *p;
+ n_long *wp;
+ struct interface *ifp;
+
+
+ for (;;) {
+ fromlen = sizeof(from);
+ cc = recvfrom(rdisc_sock, &buf, sizeof(buf), 0,
+ (struct sockaddr*)&from,
+ &fromlen);
+ if (cc <= 0) {
+ if (cc < 0 && errno != EWOULDBLOCK)
+ LOGERR("recvfrom(rdisc_sock)");
+ break;
+ }
+ if (fromlen != sizeof(struct sockaddr_in))
+ logbad(1,"impossible recvfrom(rdisc_sock) fromlen=%d",
+ fromlen);
+#ifdef USE_PASSIFNAME
+ if ((cc -= sizeof(buf.ifname)) < 0)
+ logbad(0,"missing USE_PASSIFNAME; only %d bytes",
+ cc+sizeof(buf.ifname));
+#endif
+
+ hlen = buf.pkt.ip.ip_hl << 2;
+ if (cc < hlen + ICMP_MINLEN)
+ continue;
+ p = (union ad_u *)&buf.pkt.b[hlen];
+ cc -= hlen;
+
+#ifdef USE_PASSIFNAME
+ ifp = ifwithname(buf.ifname, 0);
+ if (ifp == 0)
+ msglim(&bad_name, from.sin_addr.s_addr,
+ "impossible rdisc if_ name %.*s",
+ IFNAMSIZ, buf.ifname);
+#else
+ /* If we could tell the interface on which a packet from
+ * address 0 arrived, we could deal with such solicitations.
+ */
+ ifp = ((from.sin_addr.s_addr == 0)
+ ? 0 : iflookup(from.sin_addr.s_addr));
+#endif
+ ifp = ck_icmp("Recv", from.sin_addr.s_addr, ifp,
+ buf.pkt.ip.ip_dst.s_addr, p, cc);
+ if (ifp == 0)
+ continue;
+ if (ifwithaddr(from.sin_addr.s_addr, 0, 0)) {
+ trace_pkt(" "
+ "discard our own Router Discovery message");
+ continue;
+ }
+
+ switch (p->icmp.icmp_type) {
+ case ICMP_ROUTERADVERT:
+ if (p->ad.icmp_ad_asize*4
+ < (int)sizeof(p->ad.icmp_ad_info[0])) {
+ msglim(&bad_asize, from.sin_addr.s_addr,
+ "intolerable rdisc address size=%d",
+ p->ad.icmp_ad_asize);
+ continue;
+ }
+ if (p->ad.icmp_ad_num == 0) {
+ trace_pkt(" empty?");
+ continue;
+ }
+ if (cc != (int)(sizeof(p->ad)
+ - sizeof(p->ad.icmp_ad_info)
+ + (p->ad.icmp_ad_num
+ * sizeof(p->ad.icmp_ad_info[0])))) {
+ msglim(&bad_len, from.sin_addr.s_addr,
+ "rdisc length %d does not match ad_num"
+ " %d", cc, p->ad.icmp_ad_num);
+ continue;
+ }
+ if (supplier)
+ continue;
+ if (ifp->int_state & IS_NO_ADV_IN)
+ continue;
+
+ wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
+ for (n = 0; n < p->ad.icmp_ad_num; n++) {
+ parse_ad(from.sin_addr.s_addr,
+ wp[0], wp[1],
+ ntohs(p->ad.icmp_ad_life),
+ ifp);
+ wp += p->ad.icmp_ad_asize;
+ }
+ break;
+
+
+ case ICMP_ROUTERSOLICIT:
+ if (!supplier)
+ continue;
+ if (ifp->int_state & IS_NO_ADV_OUT)
+ continue;
+ if (stopint)
+ continue;
+
+ /* XXX
+ * We should handle messages from address 0.
+ */
+
+ /* Respond with a point-to-point advertisement */
+ send_adv(ifp, from.sin_addr.s_addr, 0);
+ break;
+ }
+ }
+
+ rdisc_sort();
+}
diff --git a/sbin/routed/routed.8 b/sbin/routed/routed.8
new file mode 100644
index 0000000..25998c9
--- /dev/null
+++ b/sbin/routed/routed.8
@@ -0,0 +1,732 @@
+.\" $Revision: 2.26 $
+.\"
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)routed.8 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.Dd June 1, 1996
+.Dt ROUTED 8
+.Os
+.Sh NAME
+.Nm routed ,
+.Nm rdisc
+.Nd network RIP and router discovery routing daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl sqdghmpAtv
+.Op Fl T Ar tracefile
+.Oo
+.Fl F
+.Ar net Ns Op /mask Ns Op ,metric
+.Oc
+.Op Fl P Ar parms
+.Sh DESCRIPTION
+The
+.Nm
+utility is a daemon invoked at boot time to manage the network
+routing tables.
+It uses Routing Information Protocol, RIPv1 (RFC\ 1058),
+RIPv2 (RFC\ 1723),
+and Internet Router Discovery Protocol (RFC 1256)
+to maintain the kernel routing table.
+The RIPv1 protocol is based on the reference
+.Bx 4.3
+daemon.
+.Pp
+It listens on the
+.Xr udp 4
+socket for the
+.Xr route 8
+service (see
+.Xr services 5 )
+for Routing Information Protocol packets.
+It also sends and receives multicast Router Discovery ICMP messages.
+If the host is a router,
+.Nm
+periodically supplies copies
+of its routing tables to any directly connected hosts and networks.
+It also advertises or solicits default routes using Router Discovery
+ICMP messages.
+.Pp
+When started (or when a network interface is later turned on),
+.Nm
+uses an AF_ROUTE address family facility to find those
+directly connected interfaces configured into the
+system and marked "up".
+It adds necessary routes for the interfaces
+to the kernel routing table.
+Soon after being first started, and provided there is at least one
+interface on which RIP has not been disabled,
+.Nm
+deletes all pre-existing
+non-static routes in kernel table.
+Static routes in the kernel table are preserved and
+included in RIP responses if they have a valid RIP metric
+(see
+.Xr route 8 ) .
+.Pp
+If more than one interface is present (not counting the loopback interface),
+it is assumed that the host should forward packets among the
+connected networks.
+After transmitting a RIP
+.Em request
+and
+Router Discovery Advertisements or Solicitations on a new interface,
+the daemon enters a loop, listening for
+RIP request and response and Router Discovery packets from other hosts.
+.Pp
+When a
+.Em request
+packet is received,
+.Nm
+formulates a reply based on the information maintained in its
+internal tables.
+The
+.Em response
+packet generated contains a list of known routes, each marked
+with a "hop count" metric (a count of 16 or greater is
+considered "infinite").
+The advertised metric for a route reflects the metrics associated
+with interfaces
+(see
+.Xr ifconfig 8 )
+though which it is received and sent,
+so setting the metric on an interface
+is an effective way to steer traffic.
+See also
+.Cm adj_inmetric
+and
+.Cm adj_outmetric
+parameters below.
+.Pp
+Responses do not include routes with a first hop on the requesting
+network to implement in part
+.Em split-horizon .
+Requests from query programs
+such as
+.Xr rtquery 8
+are answered with the complete table.
+.Pp
+The routing table maintained by the daemon
+includes space for several gateways for each destination
+to speed recovery from a failing router.
+RIP
+.Em response
+packets received are used to update the routing tables provided they are
+from one of the several currently recognized gateways or
+advertise a better metric than at least one of the existing
+gateways.
+.Pp
+When an update is applied,
+.Nm
+records the change in its own tables and updates the kernel routing table
+if the best route to the destination changes.
+The change in the kernel routing table is reflected in the next batch of
+.Em response
+packets sent.
+If the next response is not scheduled for a while, a
+.Em flash update
+response containing only recently changed routes is sent.
+.Pp
+In addition to processing incoming packets,
+.Nm
+also periodically checks the routing table entries.
+If an entry has not been updated for 3 minutes, the entry's metric
+is set to infinity and marked for deletion.
+Deletions are delayed until the route has been advertised with
+an infinite metric to ensure the invalidation
+is propagated throughout the local internet.
+This is a form of
+.Em poison reverse .
+.Pp
+Routes in the kernel table that are added or changed as a result
+of ICMP Redirect messages are deleted after a while to minimize
+.Em black-holes .
+When a TCP connection suffers a timeout,
+the kernel tells
+.Nm ,
+which deletes all redirected routes
+through the gateway involved, advances the age of all RIP routes through
+the gateway to allow an alternate to be chosen, and advances of the
+age of any relevant Router Discovery Protocol default routes.
+.Pp
+Hosts acting as internetwork routers gratuitously supply their
+routing tables every 30 seconds to all directly connected hosts
+and networks.
+These RIP responses are sent to the broadcast address on nets that support
+broadcasting,
+to the destination address on point-to-point links, and to the router's
+own address on other networks.
+If RIPv2 is enabled, multicast packets are sent on interfaces that
+support multicasting.
+.Pp
+If no response is received on a remote interface, if there are errors
+while sending responses,
+or if there are more errors than input or output (see
+.Xr netstat 1 ) ,
+then the cable or some other part of the interface is assumed to be
+disconnected or broken, and routes are adjusted appropriately.
+.Pp
+The
+.Em Internet Router Discovery Protocol
+is handled similarly.
+When the daemon is supplying RIP routes, it also listens for
+Router Discovery Solicitations and sends Advertisements.
+When it is quiet and listening to other RIP routers, it
+sends Solicitations and listens for Advertisements.
+If it receives
+a good Advertisement and it is not multi-homed,
+it stops listening for broadcast or multicast RIP responses.
+It tracks several advertising routers to speed recovery when the
+currently chosen router dies.
+If all discovered routers disappear,
+the daemon resumes listening to RIP responses.
+It continues listening to RIP while using Router Discovery
+if multi-homed to ensure all interfaces are used.
+.Pp
+The Router Discovery standard requires that advertisements
+have a default "lifetime" of 30 minutes.
+That means should
+something happen, a client can be without a good route for
+30 minutes.
+It is a good idea to reduce the default to 45
+seconds using
+.Fl P Cm rdisc_interval=45
+on the command line or
+.Cm rdisc_interval=45
+in the
+.Pa /etc/gateways
+file.
+.Pp
+While using Router Discovery (which happens by default when
+the system has a single network interface and a Router Discover Advertisement
+is received), there is a single default route and a variable number of
+redirected host routes in the kernel table.
+On a host with more than one network interface,
+this default route will be via only one of the interfaces.
+Thus, multi-homed hosts running with
+.Fl q
+might need
+.Cm no_rdisc
+described below.
+.Pp
+See the
+.Cm pm_rdisc
+facility described below to support "legacy" systems
+that can handle neither RIPv2 nor Router Discovery.
+.Pp
+By default, neither Router Discovery advertisements nor solicitations
+are sent over point to point links (e.g.\& PPP).
+The netmask associated with point-to-point links (such as SLIP
+or PPP, with the IFF_POINTOPOINT flag) is used by
+.Nm
+to infer the netmask used by the remote system when RIPv1 is used.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl s
+force
+.Nm
+to supply routing information.
+This is the default if multiple network interfaces are present on which
+RIP or Router Discovery have not been disabled, and if the kernel switch
+ipforwarding=1.
+.It Fl q
+is the opposite of the
+.Fl s
+option.
+This is the default when only one interface is present.
+With this explicit option, the daemon is always in "quiet-mode" for RIP
+and does not supply routing information to other computers.
+.It Fl d
+do not run in the background.
+This option is meant for interactive use.
+.It Fl g
+used on internetwork routers to offer a route
+to the "default" destination.
+It is equivalent to
+.Fl F
+.Cm 0/0,1
+and is present mostly for historical reasons.
+A better choice is
+.Fl P Cm pm_rdisc
+on the command line or
+.Cm pm_rdisc
+in the
+.Pa /etc/gateways
+file,
+since a larger metric
+will be used, reducing the spread of the potentially dangerous
+default route.
+This is typically used on a gateway to the Internet,
+or on a gateway that uses another routing protocol whose routes
+are not reported to other local routers.
+Notice that because a metric of 1 is used, this feature is
+dangerous.
+It is more commonly accidentally used to create chaos with a
+routing loop than to solve problems.
+.It Fl h
+cause host or point-to-point routes to not be advertised,
+provided there is a network route going the same direction.
+That is a limited kind of aggregation.
+This option is useful on gateways to Ethernets that have other gateway
+machines connected with point-to-point links such as SLIP.
+.It Fl m
+cause the machine to advertise a host or point-to-point route to
+its primary interface.
+It is useful on multi-homed machines such as NFS servers.
+This option should not be used except when the cost of
+the host routes it generates is justified by the popularity of
+the server.
+It is effective only when the machine is supplying
+routing information, because there is more than one interface.
+The
+.Fl m
+option overrides the
+.Fl q
+option to the limited extent of advertising the host route.
+.It Fl A
+do not ignore RIPv2 authentication if we do not care about RIPv2
+authentication.
+This option is required for conformance with RFC 1723.
+However, it makes no sense and breaks using RIP as a discovery protocol
+to ignore all RIPv2 packets that carry authentication when this machine
+does not care about authentication.
+.It Fl t
+increase the debugging level, which causes more information to be logged
+on the tracefile specified with
+.Fl T
+or standard out.
+The debugging level can be increased or decreased
+with the
+.Em SIGUSR1
+or
+.Em SIGUSR2
+signals or with the
+.Xr rtquery 8
+command.
+.It Fl T Ar tracefile
+increases the debugging level to at least 1 and
+causes debugging information to be appended to the trace file.
+Note that because of security concerns, it is wisest to not run
+.Nm
+routinely with tracing directed to a file.
+.It Fl v
+display and logs the version of daemon.
+.It Fl F Ar net[/mask][,metric]
+minimize routes in transmissions via interfaces with addresses that match
+.Em net/mask ,
+and synthesizes a default route to this machine with the
+.Em metric .
+The intent is to reduce RIP traffic on slow, point-to-point links
+such as PPP links by replacing many large UDP packets of RIP information
+with a single, small packet containing a "fake" default route.
+If
+.Em metric
+is absent, a value of 14 is assumed to limit
+the spread of the "fake" default route.
+This is a dangerous feature that when used carelessly can cause routing
+loops.
+Notice also that more than one interface can match the specified network
+number and mask.
+See also
+.Fl g .
+.It Fl P Ar parms
+is equivalent to adding the parameter
+line
+.Em parms
+to the
+.Pa /etc/gateways
+file.
+.El
+.Pp
+Any other argument supplied is interpreted as the name
+of a file in which the actions of
+.Nm
+should be logged.
+It is better to use
+.Fl T
+instead of
+appending the name of the trace file to the command.
+.Pp
+The
+.Nm
+utility also supports the notion of
+"distant"
+.Em passive
+or
+.Em active
+gateways.
+When
+.Nm
+is started, it reads the file
+.Pa /etc/gateways
+to find such distant gateways which may not be located using
+only information from a routing socket, to discover if some
+of the local gateways are
+.Em passive ,
+and to obtain other parameters.
+Gateways specified in this manner should be marked passive
+if they are not expected to exchange routing information,
+while gateways marked active
+should be willing to exchange RIP packets.
+Routes through
+.Em passive
+gateways are installed in the
+kernel's routing tables once upon startup and are not included in
+transmitted RIP responses.
+.Pp
+Distant active gateways are treated like network interfaces.
+RIP responses are sent
+to the distant
+.Em active
+gateway.
+If no responses are received, the associated route is deleted from
+the kernel table and RIP responses advertised via other interfaces.
+If the distant gateway resumes sending RIP responses, the associated
+route is restored.
+.Pp
+Such gateways can be useful on media that do not support broadcasts
+or multicasts but otherwise act like classic shared media like
+Ethernets such as some ATM networks.
+One can list all RIP routers reachable on the HIPPI or ATM network in
+.Pa /etc/gateways
+with a series of
+"host" lines.
+Note that it is usually desirable to use RIPv2 in such situations
+to avoid generating lists of inferred host routes.
+.Pp
+Gateways marked
+.Em external
+are also passive, but are not placed in the kernel
+routing table nor are they included in routing updates.
+The function of external entries is to indicate
+that another routing process
+will install such a route if necessary,
+and that other routes to that destination should not be installed
+by
+.Nm .
+Such entries are only required when both routers may learn of routes
+to the same destination.
+.Pp
+The
+.Pa /etc/gateways
+file is comprised of a series of lines, each in
+one of the following two formats or consist of parameters described later.
+Blank lines and lines starting with '#' are comments.
+.Pp
+.Bd -ragged
+.Cm net
+.Ar Nname[/mask]
+.Cm gateway
+.Ar Gname
+.Cm metric
+.Ar value
+.Pf < Cm passive No \&|
+.Cm active No \&|
+.Cm extern Ns >
+.Ed
+.Bd -ragged
+.Cm host
+.Ar Hname
+.Cm gateway
+.Ar Gname
+.Cm metric
+.Ar value
+.Pf < Cm passive No \&|
+.Cm active No \&|
+.Cm extern Ns >
+.Ed
+.Pp
+.Ar Nname
+or
+.Ar Hname
+is the name of the destination network or host.
+It may be a symbolic network name or an Internet address
+specified in "dot" notation (see
+.Xr inet 3 ) .
+(If it is a name, then it must either be defined in
+.Pa /etc/networks
+or
+.Pa /etc/hosts ,
+or
+.Xr named 8 ,
+must have been started before
+.Nm . )
+.Pp
+.Ar Mask
+is an optional number between 1 and 32 indicating the netmask associated
+with
+.Ar Nname .
+.Pp
+.Ar Gname
+is the name or address of the gateway to which RIP responses should
+be forwarded.
+.Pp
+.Ar Value
+is the hop count to the destination host or network.
+.Pp
+.Cm Host Ar hname
+is equivalent to
+.Cm net Ar nname/32 .
+.Pp
+One of the keywords
+.Cm passive ,
+.Cm active
+or
+.Cm external
+must be present to indicate whether the gateway should be treated as
+.Cm passive
+or
+.Cm active
+(as described above),
+or whether the gateway is
+.Cm external
+to the scope of the RIP protocol.
+.Pp
+As can be seen when debugging is turned on with
+.Fl t ,
+such lines create pseudo-interfaces.
+To set parameters for remote or external interfaces,
+a line starting with
+.Cm if=alias(Hname) ,
+.Cm if=remote(Hname) ,
+etc.\& should be used.
+.Ss Parameters
+Lines that start with neither "net" nor "host" must consist of one
+or more of the following parameter settings, separated by commas or
+blanks:
+.Bl -tag -width indent
+.It Cm if Ns = Ns Ar ifname
+indicates that the other parameters on the line apply to the interface
+name
+.Ar ifname .
+.It Cm subnet Ns = Ns Ar nname Ns Oo / Ns Ar mask Oc Ns Op , Ns Ar metric
+advertises a route to network
+.Ar nname
+with mask
+.Ar mask
+and the supplied metric (default 1).
+This is useful for filling "holes" in CIDR allocations.
+This parameter must appear by itself on a line.
+The network number must specify a full, 32-bit value, as in 192.0.2.0
+instead of 192.0.2.
+.Pp
+Do not use this feature unless necessary.
+It is dangerous.
+.It Cm ripv1_mask Ns = Ns Ar nname Ns / Ns Ar mask1 , Ns Ar mask2
+specifies that netmask of the network of which
+.Ar nname Ns / Ns Ar mask1
+is
+a subnet should be
+.Ar mask2 .
+For example,
+.Dq Li ripv1_mask=192.0.2.16/28,27
+marks 192.0.2.16/28
+as a subnet of 192.0.2.0/27 instead of 192.0.2.0/24.
+It is better to turn on RIPv2 instead of using this facility, for example
+with
+.Cm ripv2_out .
+.It Cm passwd Ns = Ns Ar XXX[|KeyID[start|stop]]
+specifies a RIPv2 cleartext password that will be included on
+all RIPv2 responses sent, and checked on all RIPv2 responses received.
+Any blanks, tab characters, commas, or '#', '|', or NULL characters in the
+password must be escaped with a backslash (\\).
+The common escape sequences \\n, \\r, \\t, \\b, and \\xxx have their
+usual meanings.
+The
+.Cm KeyID
+must be unique but is ignored for cleartext passwords.
+If present,
+.Cm start
+and
+.Cm stop
+are timestamps in the form year/month/day@hour:minute.
+They specify when the password is valid.
+The valid password with the most future is used on output packets, unless
+all passwords have expired, in which case the password that expired most
+recently is used, or unless no passwords are valid yet, in which case
+no password is output.
+Incoming packets can carry any password that is valid, will
+be valid within the next 24 hours, or that was valid within the preceding
+24 hours.
+To protect the secrets, the passwd settings are valid only in the
+.Em /etc/gateways
+file and only when that file is readable only by UID 0.
+.It Cm md5_passwd Ns \&= Ns Ar XXX|KeyID[start|stop]
+specifies a RIPv2 MD5 password.
+Except that a
+.Cm KeyID
+is required, this keyword is similar to
+.Cm passwd .
+.It Cm no_ag
+turns off aggregation of subnets in RIPv1 and RIPv2 responses.
+.It Cm no_super_ag
+turns off aggregation of networks into supernets in RIPv2 responses.
+.It Cm passive
+marks the interface to not be advertised in updates sent via other
+interfaces, and turns off all RIP and router discovery through the interface.
+.It Cm no_rip
+disables all RIP processing on the specified interface.
+If no interfaces are allowed to process RIP packets,
+.Nm
+acts purely as a router discovery daemon.
+.Pp
+Note that turning off RIP without explicitly turning on router
+discovery advertisements with
+.Cm rdisc_adv
+or
+.Fl s
+causes
+.Nm
+to act as a client router discovery daemon, not advertising.
+.It Cm no_rip_mcast
+causes RIPv2 packets to be broadcast instead of multicast.
+.It Cm no_rip_out
+causes no RIP updates to be sent.
+.It Cm no_ripv1_in
+causes RIPv1 received responses to be ignored.
+.It Cm no_ripv2_in
+causes RIPv2 received responses to be ignored.
+.It Cm ripv2_out
+turns on RIPv2 output and causes RIPv2 advertisements to be
+multicast when possible.
+.It Cm ripv2
+is equivalent to
+.Cm no_ripv1_in
+and
+.Cm no_ripv1_out .
+This enables RIPv2.
+.It Cm no_rdisc
+disables the Internet Router Discovery Protocol.
+.It Cm no_solicit
+disables the transmission of Router Discovery Solicitations.
+.It Cm send_solicit
+specifies that Router Discovery solicitations should be sent,
+even on point-to-point links,
+which by default only listen to Router Discovery messages.
+.It Cm no_rdisc_adv
+disables the transmission of Router Discovery Advertisements.
+.It Cm rdisc_adv
+specifies that Router Discovery Advertisements should be sent,
+even on point-to-point links,
+which by default only listen to Router Discovery messages.
+.It Cm bcast_rdisc
+specifies that Router Discovery packets should be broadcast instead of
+multicast.
+.It Cm rdisc_pref Ns \&= Ns Ar N
+sets the preference in Router Discovery Advertisements to the optionally
+signed integer
+.Ar N .
+The default preference is 0.
+Default routes with smaller or more negative preferences are preferred by
+clients.
+.It Cm rdisc_interval Ns \&= Ns Ar N
+sets the nominal interval with which Router Discovery Advertisements
+are transmitted to N seconds and their lifetime to 3*N.
+.It Cm fake_default Ns \&= Ns Ar metric
+has an identical effect to
+.Fl F Ar net[/mask][=metric]
+with the network and mask coming from the specified interface.
+.It Cm pm_rdisc
+is similar to
+.Cm fake_default .
+When RIPv2 routes are multicast, so that RIPv1 listeners cannot
+receive them, this feature causes a RIPv1 default route to be
+broadcast to RIPv1 listeners.
+Unless modified with
+.Cm fake_default ,
+the default route is broadcast with a metric of 14.
+That serves as a "poor man's router discovery" protocol.
+.It Cm adj_inmetric Ns \&= Ns Ar delta
+adjusts the hop count or metric of received RIP routes by
+.Ar delta .
+The metric of every received RIP route is increased by the sum
+of two values associated with the interface.
+One is the adj_inmetric value and the other is the interface
+metric set with
+.Xr ifconfig 8 .
+.It Cm adj_outmetric Ns \&= Ns Ar delta
+adjusts the hop count or metric of advertised RIP routes by
+.Ar delta .
+The metric of every received RIP route is increased by the metric
+associated with the interface by which it was received, or by 1 if
+the interface does not have a non-zero metric.
+The metric of the received route is then increased by the
+adj_outmetric associated with the interface.
+Every advertised route is increased by a total of four
+values,
+the metric set for the interface by which it was received with
+.Xr ifconfig 8 ,
+the
+.Cm adj_inmetric Ar delta
+of the receiving interface,
+the metric set for the interface by which it is transmitted with
+.Xr ifconfig 8 ,
+and the
+.Cm adj_outmetric Ar delta
+of the transmitting interface.
+.It Cm trust_gateway Ns \&= Ns Ar rname[|net1/mask1|net2/mask2|...]
+causes RIP packets from router
+.Ar rname
+and other routers named in other
+.Cm trust_gateway
+keywords to be accepted, and packets from other routers to be ignored.
+If networks are specified, then routes to other networks will be ignored
+from that router.
+.It Cm redirect_ok
+allows the kernel to listen ICMP Redirect messages when the system is acting
+as a router and forwarding packets.
+Otherwise, ICMP Redirect messages are overridden and deleted when the
+system is acting as a router.
+.El
+.Sh FILES
+.Bl -tag -width /etc/gateways -compact
+.It Pa /etc/gateways
+for distant gateways
+.El
+.Sh SEE ALSO
+.Xr icmp 4 ,
+.Xr udp 4 ,
+.Xr rtquery 8
+.Rs
+.%T Internet Transport Protocols
+.%R XSIS 028112
+.%Q Xerox System Integration Standard
+.Re
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
+.\" LocalWords: loopback ICMP rtquery ifconfig multicasting Solicitations RIPv
+.\" LocalWords: netstat rdisc
+.Sh BUGS
+It does not always detect unidirectional failures in network interfaces,
+for example, when the output side fails.
diff --git a/sbin/routed/rtquery/Makefile b/sbin/routed/rtquery/Makefile
new file mode 100644
index 0000000..7076158
--- /dev/null
+++ b/sbin/routed/rtquery/Makefile
@@ -0,0 +1,11 @@
+# Make `routed` tools for BSD/OS
+# $Revision: 1.6 $
+# $FreeBSD$
+
+PROG= rtquery
+MAN= rtquery.8
+LDADD= -lmd
+DPADD= ${LIBMD}
+WARNS?= 0
+
+.include <bsd.prog.mk>
diff --git a/sbin/routed/rtquery/rtquery.8 b/sbin/routed/rtquery/rtquery.8
new file mode 100644
index 0000000..4f16e88
--- /dev/null
+++ b/sbin/routed/rtquery/rtquery.8
@@ -0,0 +1,131 @@
+.\" $Revision: 2.27 $
+.\" $FreeBSD$
+.\"
+.Dd June 1, 1996
+.Dt RTQUERY 8
+.Os
+.Sh NAME
+.Nm rtquery
+.Nd query routing daemons for their routing tables
+.Sh SYNOPSIS
+.Nm
+.Op Fl np1
+.Op Fl w Ar timeout
+.Op Fl r Ar addr
+.Op Fl a Ar secret
+.Ar host ...
+.Nm
+.Op Fl t Ar op
+.Ar host ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to query a RIP network routing daemon, such as
+.Xr routed 8 ,
+for its routing table by sending a
+.Em request
+or
+.Em poll
+command.
+The routing information in any routing
+.Em response
+packets returned is displayed numerically and symbolically.
+.Pp
+The
+.Nm
+utility by default uses the
+.Em request
+command.
+When the
+.Fl p
+option is specified,
+.Nm
+uses the
+.Em poll
+command, an
+undocumented extension to the RIP protocol supported by
+the commercial
+.Nm gated
+routing product.
+When querying
+.Nm gated ,
+the
+.Em poll
+command is preferred over the
+.Em request
+command because the response is not subject to Split Horizon and/or
+Poisoned Reverse, and because some versions of
+.Nm gated
+do not answer the
+.Em request
+command.
+The
+.Xr routed 8
+utility does not answer the
+.Em poll
+command, but recognizes
+.Em requests
+coming from
+.Nm
+and so answers completely.
+.Pp
+The
+.Nm
+utility is also used to turn tracing on or off in
+.Xr routed 8 .
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl n
+displays only the numeric network and host numbers instead of both
+numeric and symbolic.
+.It Fl p
+uses the
+.Em poll
+command to request full routing information from
+.Nm gated .
+This is an undocumented extension RIP protocol supported only by
+.Nm gated .
+.It Fl 1
+queries using RIP version 1 instead of RIP version 2.
+.It Fl w Ar timeout
+changes the delay for an answer from each host.
+By default, each host is given 15 seconds to respond.
+.It Fl r Ar addr
+asks about the route to destination
+.Em addr .
+.It Fl a Ar passwd=XXX
+.It Fl a Ar md5_passwd=XXX|KeyID
+causes the query to be sent with the indicated cleartext or MD5 password.
+.It Fl t Ar op
+changes tracing, where
+.Em op
+is one of the following.
+Requests from processes not running with UID 0 or on distant networks
+are generally ignored by the daemon except for a message in the system log.
+.Nm gated
+is likely to ignore these debugging requests.
+.El
+.Bl -tag -width Ds -offset indent-two
+.It Em on=tracefile
+turns tracing on into the specified file.
+That file must usually have been specified when the daemon was
+started or be the same as a fixed name, often
+.Pa /etc/routed.trace .
+.It Em more
+increases the debugging level.
+.It Em off
+turns off tracing.
+.It Em dump
+dumps the daemon's routing table to the current tracefile.
+.El
+.Sh SEE ALSO
+.Xr routed 8
+.Rs
+.%T Routing Information Protocol, RIPv1
+.%O RFC1058
+.Re
+.Rs
+.%T Routing Information Protocol, RIPv2
+.%O RFC1723
+.Re
diff --git a/sbin/routed/rtquery/rtquery.c b/sbin/routed/rtquery/rtquery.c
new file mode 100644
index 0000000..1b3a47b
--- /dev/null
+++ b/sbin/routed/rtquery/rtquery.c
@@ -0,0 +1,919 @@
+/*-
+ * Copyright (c) 1982, 1986, 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.
+ * 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#define RIPVERSION RIPv2
+#include <protocols/routed.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef sgi
+#include <strings.h>
+#include <bstring.h>
+#endif
+
+#define UNUSED __attribute__((unused))
+#ifndef __RCSID
+#define __RCSID(_s) static const char rcsid[] UNUSED = _s
+#endif
+#ifndef __COPYRIGHT
+#define __COPYRIGHT(_s) static const char copyright[] UNUSED = _s
+#endif
+__COPYRIGHT("@(#) Copyright (c) 1983, 1988, 1993\n"
+ "The Regents of the University of California."
+ " All rights reserved.\n");
+#ifdef __NetBSD__
+__RCSID("$NetBSD$");
+#elif defined(__FreeBSD__)
+__RCSID("$FreeBSD$");
+#else
+__RCSID("$Revision: 2.26 $");
+#ident "$Revision: 2.26 $"
+#endif
+
+#ifndef sgi
+#define _HAVE_SIN_LEN
+#endif
+
+#ifdef __NetBSD__
+#include <md5.h>
+#else
+#define MD5_DIGEST_LEN 16
+typedef struct {
+ u_int32_t state[4]; /* state (ABCD) */
+ u_int32_t count[2]; /* # of bits, modulo 2^64 (LSB 1st) */
+ unsigned char buffer[64]; /* input buffer */
+} MD5_CTX;
+extern void MD5Init(MD5_CTX*);
+extern void MD5Update(MD5_CTX*, u_char*, u_int);
+extern void MD5Final(u_char[MD5_DIGEST_LEN], MD5_CTX*);
+#endif
+
+
+#define WTIME 15 /* Time to wait for all responses */
+#define STIME (250*1000) /* usec to wait for another response */
+
+int soc;
+
+const char *pgmname;
+
+union {
+ struct rip rip;
+ char packet[MAXPACKETSIZE+MAXPATHLEN];
+} omsg_buf;
+#define OMSG omsg_buf.rip
+int omsg_len = sizeof(struct rip);
+
+union {
+ struct rip rip;
+ char packet[MAXPACKETSIZE+1024];
+ } imsg_buf;
+#define IMSG imsg_buf.rip
+
+int nflag; /* numbers, no names */
+int pflag; /* play the `gated` game */
+int ripv2 = 1; /* use RIP version 2 */
+int wtime = WTIME;
+int rflag; /* 1=ask about a particular route */
+int trace, not_trace; /* send trace command or not */
+int auth_type = RIP_AUTH_NONE;
+char passwd[RIP_AUTH_PW_LEN];
+u_long keyid;
+
+struct timeval sent; /* when query sent */
+
+static char localhost_str[] = "localhost";
+static char *default_argv[] = {localhost_str, 0};
+
+static void rip_input(struct sockaddr_in*, int);
+static int out(const char *);
+static void trace_loop(char *argv[]) __attribute((__noreturn__));
+static void query_loop(char *argv[], int) __attribute((__noreturn__));
+static int getnet(char *, struct netinfo *);
+static u_int std_mask(u_int);
+static int parse_quote(char **, const char *, char *, char *, int);
+static void usage(void);
+
+
+int
+main(int argc,
+ char *argv[])
+{
+ int ch, bsize;
+ char *p, *options, *value, delim;
+ const char *result;
+
+ OMSG.rip_nets[0].n_dst = RIP_DEFAULT;
+ OMSG.rip_nets[0].n_family = RIP_AF_UNSPEC;
+ OMSG.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY);
+
+ pgmname = argv[0];
+ while ((ch = getopt(argc, argv, "np1w:r:t:a:")) != -1)
+ switch (ch) {
+ case 'n':
+ not_trace = 1;
+ nflag = 1;
+ break;
+
+ case 'p':
+ not_trace = 1;
+ pflag = 1;
+ break;
+
+ case '1':
+ ripv2 = 0;
+ break;
+
+ case 'w':
+ not_trace = 1;
+ wtime = (int)strtoul(optarg, &p, 0);
+ if (*p != '\0'
+ || wtime <= 0)
+ usage();
+ break;
+
+ case 'r':
+ not_trace = 1;
+ if (rflag)
+ usage();
+ rflag = getnet(optarg, &OMSG.rip_nets[0]);
+ if (!rflag) {
+ struct hostent *hp = gethostbyname(optarg);
+ if (hp == 0) {
+ fprintf(stderr, "%s: %s:",
+ pgmname, optarg);
+ herror(0);
+ exit(1);
+ }
+ memcpy(&OMSG.rip_nets[0].n_dst, hp->h_addr,
+ sizeof(OMSG.rip_nets[0].n_dst));
+ OMSG.rip_nets[0].n_family = RIP_AF_INET;
+ OMSG.rip_nets[0].n_mask = -1;
+ rflag = 1;
+ }
+ break;
+
+ case 't':
+ trace = 1;
+ options = optarg;
+ while (*options != '\0') {
+ /* messy complications to make -W -Wall happy */
+ static char on_str[] = "on";
+ static char more_str[] = "more";
+ static char off_str[] = "off";
+ static char dump_str[] = "dump";
+ static char *traceopts[] = {
+# define TRACE_ON 0
+ on_str,
+# define TRACE_MORE 1
+ more_str,
+# define TRACE_OFF 2
+ off_str,
+# define TRACE_DUMP 3
+ dump_str,
+ 0
+ };
+ result = "";
+ switch (getsubopt(&options,traceopts,&value)) {
+ case TRACE_ON:
+ OMSG.rip_cmd = RIPCMD_TRACEON;
+ if (!value
+ || strlen(value) > MAXPATHLEN)
+ usage();
+ result = value;
+ break;
+ case TRACE_MORE:
+ if (value)
+ usage();
+ OMSG.rip_cmd = RIPCMD_TRACEON;
+ break;
+ case TRACE_OFF:
+ if (value)
+ usage();
+ OMSG.rip_cmd = RIPCMD_TRACEOFF;
+ break;
+ case TRACE_DUMP:
+ if (value)
+ usage();
+ OMSG.rip_cmd = RIPCMD_TRACEON;
+ result = "dump/../table";
+ break;
+ default:
+ usage();
+ }
+ strcpy((char*)OMSG.rip_tracefile, result);
+ omsg_len += strlen(result) - sizeof(OMSG.ripun);
+ }
+ break;
+
+ case 'a':
+ not_trace = 1;
+ p = strchr(optarg,'=');
+ if (!p)
+ usage();
+ *p++ = '\0';
+ if (!strcasecmp("passwd",optarg))
+ auth_type = RIP_AUTH_PW;
+ else if (!strcasecmp("md5_passwd",optarg))
+ auth_type = RIP_AUTH_MD5;
+ else
+ usage();
+ if (0 > parse_quote(&p,"|",&delim,
+ passwd, sizeof(passwd)))
+ usage();
+ if (auth_type == RIP_AUTH_MD5
+ && delim == '|') {
+ keyid = strtoul(p+1,&p,0);
+ if (keyid > 255 || *p != '\0')
+ usage();
+ } else if (delim != '\0') {
+ usage();
+ }
+ break;
+
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+ if (not_trace && trace)
+ usage();
+ if (argc == 0) {
+ argc = 1;
+ argv = default_argv;
+ }
+
+ soc = socket(AF_INET, SOCK_DGRAM, 0);
+ if (soc < 0) {
+ perror("socket");
+ exit(2);
+ }
+
+ /* be prepared to receive a lot of routes */
+ for (bsize = 127*1024; ; bsize -= 1024) {
+ if (setsockopt(soc, SOL_SOCKET, SO_RCVBUF,
+ &bsize, sizeof(bsize)) == 0)
+ break;
+ if (bsize <= 4*1024) {
+ perror("setsockopt SO_RCVBUF");
+ break;
+ }
+ }
+
+ if (trace)
+ trace_loop(argv);
+ else
+ query_loop(argv, argc);
+ /* NOTREACHED */
+ return 0;
+}
+
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: rtquery [-np1] [-r tgt_rt] [-w wtime]"
+ " [-a type=passwd] host1 [host2 ...]\n"
+ "\trtquery -t {on=filename|more|off|dump}"
+ " host1 [host2 ...]\n");
+ exit(1);
+}
+
+
+/* tell the target hosts about tracing
+ */
+static void
+trace_loop(char *argv[])
+{
+ struct sockaddr_in myaddr;
+ int res;
+
+ if (geteuid() != 0) {
+ (void)fprintf(stderr, "-t requires UID 0\n");
+ exit(1);
+ }
+
+ if (ripv2) {
+ OMSG.rip_vers = RIPv2;
+ } else {
+ OMSG.rip_vers = RIPv1;
+ }
+
+ memset(&myaddr, 0, sizeof(myaddr));
+ myaddr.sin_family = AF_INET;
+#ifdef _HAVE_SIN_LEN
+ myaddr.sin_len = sizeof(myaddr);
+#endif
+ myaddr.sin_port = htons(IPPORT_RESERVED-1);
+ while (bind(soc, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) {
+ if (errno != EADDRINUSE
+ || myaddr.sin_port == 0) {
+ perror("bind");
+ exit(2);
+ }
+ myaddr.sin_port = htons(ntohs(myaddr.sin_port)-1);
+ }
+
+ res = 1;
+ while (*argv != 0) {
+ if (out(*argv++) <= 0)
+ res = 0;
+ }
+ exit(res);
+}
+
+
+/* query all of the listed hosts
+ */
+static void
+query_loop(char *argv[], int argc)
+{
+# define NA0 (OMSG.rip_auths[0])
+# define NA2 (OMSG.rip_auths[2])
+ struct seen {
+ struct seen *next;
+ struct in_addr addr;
+ } *seen, *sp;
+ int answered = 0;
+ int cc;
+ fd_set bits;
+ struct timeval now, delay;
+ struct sockaddr_in from;
+ int fromlen;
+ MD5_CTX md5_ctx;
+
+
+ OMSG.rip_cmd = (pflag) ? RIPCMD_POLL : RIPCMD_REQUEST;
+ if (ripv2) {
+ OMSG.rip_vers = RIPv2;
+ if (auth_type == RIP_AUTH_PW) {
+ OMSG.rip_nets[1] = OMSG.rip_nets[0];
+ NA0.a_family = RIP_AF_AUTH;
+ NA0.a_type = RIP_AUTH_PW;
+ memcpy(NA0.au.au_pw, passwd, RIP_AUTH_PW_LEN);
+ omsg_len += sizeof(OMSG.rip_nets[0]);
+
+ } else if (auth_type == RIP_AUTH_MD5) {
+ OMSG.rip_nets[1] = OMSG.rip_nets[0];
+ NA0.a_family = RIP_AF_AUTH;
+ NA0.a_type = RIP_AUTH_MD5;
+ NA0.au.a_md5.md5_keyid = (int8_t)keyid;
+ NA0.au.a_md5.md5_auth_len = RIP_AUTH_MD5_KEY_LEN;
+ NA0.au.a_md5.md5_seqno = 0;
+ cc = (char *)&NA2-(char *)&OMSG;
+ NA0.au.a_md5.md5_pkt_len = htons(cc);
+ NA2.a_family = RIP_AF_AUTH;
+ NA2.a_type = htons(1);
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx,
+ (u_char *)&OMSG, cc);
+ MD5Update(&md5_ctx,
+ (u_char *)passwd, RIP_AUTH_MD5_HASH_LEN);
+ MD5Final(NA2.au.au_pw, &md5_ctx);
+ omsg_len += 2*sizeof(OMSG.rip_nets[0]);
+ }
+
+ } else {
+ OMSG.rip_vers = RIPv1;
+ OMSG.rip_nets[0].n_mask = 0;
+ }
+
+ /* ask the first (valid) host */
+ seen = 0;
+ while (0 > out(*argv++)) {
+ if (*argv == 0)
+ exit(1);
+ answered++;
+ }
+
+ FD_ZERO(&bits);
+ for (;;) {
+ FD_SET(soc, &bits);
+ delay.tv_sec = 0;
+ delay.tv_usec = STIME;
+ cc = select(soc+1, &bits, 0,0, &delay);
+ if (cc > 0) {
+ fromlen = sizeof(from);
+ cc = recvfrom(soc, imsg_buf.packet,
+ sizeof(imsg_buf.packet), 0,
+ (struct sockaddr *)&from, &fromlen);
+ if (cc < 0) {
+ perror("recvfrom");
+ exit(1);
+ }
+ /* count the distinct responding hosts.
+ * You cannot match responding hosts with
+ * addresses to which queries were transmitted,
+ * because a router might respond with a
+ * different source address.
+ */
+ for (sp = seen; sp != 0; sp = sp->next) {
+ if (sp->addr.s_addr == from.sin_addr.s_addr)
+ break;
+ }
+ if (sp == 0) {
+ sp = malloc(sizeof(*sp));
+ if (sp == 0) {
+ fprintf(stderr,
+ "rtquery: malloc failed\n");
+ exit(1);
+ }
+ sp->addr = from.sin_addr;
+ sp->next = seen;
+ seen = sp;
+ answered++;
+ }
+
+ rip_input(&from, cc);
+ continue;
+ }
+
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("select");
+ exit(1);
+ }
+
+ /* After a pause in responses, probe another host.
+ * This reduces the intermingling of answers.
+ */
+ while (*argv != 0 && 0 > out(*argv++))
+ answered++;
+
+ /* continue until no more packets arrive
+ * or we have heard from all hosts
+ */
+ if (answered >= argc)
+ break;
+
+ /* or until we have waited a long time
+ */
+ if (gettimeofday(&now, 0) < 0) {
+ perror("gettimeofday(now)");
+ exit(1);
+ }
+ if (sent.tv_sec + wtime <= now.tv_sec)
+ break;
+ }
+
+ /* fail if there was no answer */
+ exit (answered >= argc ? 0 : 1);
+}
+
+
+/* send to one host
+ */
+static int
+out(const char *host)
+{
+ struct sockaddr_in router;
+ struct hostent *hp;
+
+ if (gettimeofday(&sent, 0) < 0) {
+ perror("gettimeofday(sent)");
+ return -1;
+ }
+
+ memset(&router, 0, sizeof(router));
+ router.sin_family = AF_INET;
+#ifdef _HAVE_SIN_LEN
+ router.sin_len = sizeof(router);
+#endif
+ if (!inet_aton(host, &router.sin_addr)) {
+ hp = gethostbyname(host);
+ if (hp == 0) {
+ herror(host);
+ return -1;
+ }
+ memcpy(&router.sin_addr, hp->h_addr, sizeof(router.sin_addr));
+ }
+ router.sin_port = htons(RIP_PORT);
+
+ if (sendto(soc, &omsg_buf, omsg_len, 0,
+ (struct sockaddr *)&router, sizeof(router)) < 0) {
+ perror(host);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Convert string to printable characters
+ */
+static char *
+qstring(u_char *s, int len)
+{
+ static char buf[8*20+1];
+ char *p;
+ u_char *s2, c;
+
+
+ for (p = buf; len != 0 && p < &buf[sizeof(buf)-1]; len--) {
+ c = *s++;
+ if (c == '\0') {
+ for (s2 = s+1; s2 < &s[len]; s2++) {
+ if (*s2 != '\0')
+ break;
+ }
+ if (s2 >= &s[len])
+ goto exit;
+ }
+
+ if (c >= ' ' && c < 0x7f && c != '\\') {
+ *p++ = c;
+ continue;
+ }
+ *p++ = '\\';
+ switch (c) {
+ case '\\':
+ *p++ = '\\';
+ break;
+ case '\n':
+ *p++= 'n';
+ break;
+ case '\r':
+ *p++= 'r';
+ break;
+ case '\t':
+ *p++ = 't';
+ break;
+ case '\b':
+ *p++ = 'b';
+ break;
+ default:
+ p += sprintf(p,"%o",c);
+ break;
+ }
+ }
+exit:
+ *p = '\0';
+ return buf;
+}
+
+
+/*
+ * Handle an incoming RIP packet.
+ */
+static void
+rip_input(struct sockaddr_in *from,
+ int size)
+{
+ struct netinfo *n, *lim;
+ struct in_addr in;
+ const char *name;
+ char net_buf[80];
+ u_char hash[RIP_AUTH_MD5_KEY_LEN];
+ MD5_CTX md5_ctx;
+ u_char md5_authed = 0;
+ u_int mask, dmask;
+ char *sp;
+ int i;
+ struct hostent *hp;
+ struct netent *np;
+ struct netauth *na;
+
+
+ if (nflag) {
+ printf("%s:", inet_ntoa(from->sin_addr));
+ } else {
+ hp = gethostbyaddr((char*)&from->sin_addr,
+ sizeof(struct in_addr), AF_INET);
+ if (hp == 0) {
+ printf("%s:",
+ inet_ntoa(from->sin_addr));
+ } else {
+ printf("%s (%s):", hp->h_name,
+ inet_ntoa(from->sin_addr));
+ }
+ }
+ if (IMSG.rip_cmd != RIPCMD_RESPONSE) {
+ printf("\n unexpected response type %d\n", IMSG.rip_cmd);
+ return;
+ }
+ printf(" RIPv%d%s %d bytes\n", IMSG.rip_vers,
+ (IMSG.rip_vers != RIPv1 && IMSG.rip_vers != RIPv2) ? " ?" : "",
+ size);
+ if (size > MAXPACKETSIZE) {
+ if (size > (int)sizeof(imsg_buf) - (int)sizeof(*n)) {
+ printf(" at least %d bytes too long\n",
+ size-MAXPACKETSIZE);
+ size = (int)sizeof(imsg_buf) - (int)sizeof(*n);
+ } else {
+ printf(" %d bytes too long\n",
+ size-MAXPACKETSIZE);
+ }
+ } else if (size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) {
+ printf(" response of bad length=%d\n", size);
+ }
+
+ n = IMSG.rip_nets;
+ lim = (struct netinfo *)((char*)n + size) - 1;
+ for (; n <= lim; n++) {
+ name = "";
+ if (n->n_family == RIP_AF_INET) {
+ in.s_addr = n->n_dst;
+ (void)strcpy(net_buf, inet_ntoa(in));
+
+ mask = ntohl(n->n_mask);
+ dmask = mask & -mask;
+ if (mask != 0) {
+ sp = &net_buf[strlen(net_buf)];
+ if (IMSG.rip_vers == RIPv1) {
+ (void)sprintf(sp," mask=%#x ? ",mask);
+ mask = 0;
+ } else if (mask + dmask == 0) {
+ for (i = 0;
+ (i != 32
+ && ((1<<i)&mask) == 0);
+ i++)
+ continue;
+ (void)sprintf(sp, "/%d",32-i);
+ } else {
+ (void)sprintf(sp," (mask %#x)", mask);
+ }
+ }
+
+ if (!nflag) {
+ if (mask == 0) {
+ mask = std_mask(in.s_addr);
+ if ((ntohl(in.s_addr) & ~mask) != 0)
+ mask = 0;
+ }
+ /* Without a netmask, do not worry about
+ * whether the destination is a host or a
+ * network. Try both and use the first name
+ * we get.
+ *
+ * If we have a netmask we can make a
+ * good guess.
+ */
+ if ((in.s_addr & ~mask) == 0) {
+ np = getnetbyaddr((long)in.s_addr,
+ AF_INET);
+ if (np != 0)
+ name = np->n_name;
+ else if (in.s_addr == 0)
+ name = "default";
+ }
+ if (name[0] == '\0'
+ && ((in.s_addr & ~mask) != 0
+ || mask == 0xffffffff)) {
+ hp = gethostbyaddr((char*)&in,
+ sizeof(in),
+ AF_INET);
+ if (hp != 0)
+ name = hp->h_name;
+ }
+ }
+
+ } else if (n->n_family == RIP_AF_AUTH) {
+ na = (struct netauth*)n;
+ if (na->a_type == RIP_AUTH_PW
+ && n == IMSG.rip_nets) {
+ (void)printf(" Password Authentication:"
+ " \"%s\"\n",
+ qstring(na->au.au_pw,
+ RIP_AUTH_PW_LEN));
+ continue;
+ }
+
+ if (na->a_type == RIP_AUTH_MD5
+ && n == IMSG.rip_nets) {
+ (void)printf(" MD5 Auth"
+ " len=%d KeyID=%d"
+ " auth_len=%d"
+ " seqno=%#x"
+ " rsvd=%#x,%#x\n",
+ ntohs(na->au.a_md5.md5_pkt_len),
+ na->au.a_md5.md5_keyid,
+ na->au.a_md5.md5_auth_len,
+ (int)ntohl(na->au.a_md5.md5_seqno),
+ na->au.a_md5.rsvd[0],
+ na->au.a_md5.rsvd[1]);
+ md5_authed = 1;
+ continue;
+ }
+ (void)printf(" Authentication type %d: ",
+ ntohs(na->a_type));
+ for (i = 0; i < (int)sizeof(na->au.au_pw); i++)
+ (void)printf("%02x ", na->au.au_pw[i]);
+ putc('\n', stdout);
+ if (md5_authed && n+1 > lim
+ && na->a_type == ntohs(1)) {
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, (u_char *)&IMSG,
+ (char *)na-(char *)&IMSG
+ +RIP_AUTH_MD5_HASH_XTRA);
+ MD5Update(&md5_ctx, (u_char *)passwd,
+ RIP_AUTH_MD5_KEY_LEN);
+ MD5Final(hash, &md5_ctx);
+ (void)printf(" %s hash\n",
+ memcmp(hash, na->au.au_pw,
+ sizeof(hash))
+ ? "WRONG" : "correct");
+ }
+ continue;
+
+ } else {
+ (void)sprintf(net_buf, "(af %#x) %d.%d.%d.%d",
+ ntohs(n->n_family),
+ (u_char)(n->n_dst >> 24),
+ (u_char)(n->n_dst >> 16),
+ (u_char)(n->n_dst >> 8),
+ (u_char)n->n_dst);
+ }
+
+ (void)printf(" %-18s metric %2d %-10s",
+ net_buf, (int)ntohl(n->n_metric), name);
+
+ if (n->n_nhop != 0) {
+ in.s_addr = n->n_nhop;
+ if (nflag)
+ hp = 0;
+ else
+ hp = gethostbyaddr((char*)&in, sizeof(in),
+ AF_INET);
+ (void)printf(" nhop=%-15s%s",
+ (hp != 0) ? hp->h_name : inet_ntoa(in),
+ (IMSG.rip_vers == RIPv1) ? " ?" : "");
+ }
+ if (n->n_tag != 0)
+ (void)printf(" tag=%#x%s", n->n_tag,
+ (IMSG.rip_vers == RIPv1) ? " ?" : "");
+ putc('\n', stdout);
+ }
+}
+
+
+/* Return the classical netmask for an IP address.
+ */
+static u_int
+std_mask(u_int addr) /* in network order */
+{
+ addr = ntohl(addr); /* was a host, not a network */
+
+ if (addr == 0) /* default route has mask 0 */
+ return 0;
+ if (IN_CLASSA(addr))
+ return IN_CLASSA_NET;
+ if (IN_CLASSB(addr))
+ return IN_CLASSB_NET;
+ return IN_CLASSC_NET;
+}
+
+
+/* get a network number as a name or a number, with an optional "/xx"
+ * netmask.
+ */
+static int /* 0=bad */
+getnet(char *name,
+ struct netinfo *rt)
+{
+ int i;
+ struct netent *nentp;
+ u_int mask;
+ struct in_addr in;
+ char hname[MAXHOSTNAMELEN+1];
+ char *mname, *p;
+
+
+ /* Detect and separate "1.2.3.4/24"
+ */
+ if (0 != (mname = strrchr(name,'/'))) {
+ i = (int)(mname - name);
+ if (i > (int)sizeof(hname)-1) /* name too long */
+ return 0;
+ memmove(hname, name, i);
+ hname[i] = '\0';
+ mname++;
+ name = hname;
+ }
+
+ nentp = getnetbyname(name);
+ if (nentp != 0) {
+ in.s_addr = nentp->n_net;
+ } else if (inet_aton(name, &in) == 1) {
+ in.s_addr = ntohl(in.s_addr);
+ } else {
+ return 0;
+ }
+
+ if (mname == 0) {
+ mask = std_mask(in.s_addr);
+ if ((~mask & in.s_addr) != 0)
+ mask = 0xffffffff;
+ } else {
+ mask = (u_int)strtoul(mname, &p, 0);
+ if (*p != '\0' || mask > 32)
+ return 0;
+ mask = 0xffffffff << (32-mask);
+ }
+
+ rt->n_dst = htonl(in.s_addr);
+ rt->n_family = RIP_AF_INET;
+ rt->n_mask = htonl(mask);
+ return 1;
+}
+
+
+/* strtok(), but honoring backslash
+ */
+static int /* -1=bad */
+parse_quote(char **linep,
+ const char *delims,
+ char *delimp,
+ char *buf,
+ int lim)
+{
+ char c, *pc;
+ const char *p;
+
+
+ pc = *linep;
+ if (*pc == '\0')
+ return -1;
+
+ for (;;) {
+ if (lim == 0)
+ return -1;
+ c = *pc++;
+ if (c == '\0')
+ break;
+
+ if (c == '\\' && *pc != '\0') {
+ if ((c = *pc++) == 'n') {
+ c = '\n';
+ } else if (c == 'r') {
+ c = '\r';
+ } else if (c == 't') {
+ c = '\t';
+ } else if (c == 'b') {
+ c = '\b';
+ } else if (c >= '0' && c <= '7') {
+ c -= '0';
+ if (*pc >= '0' && *pc <= '7') {
+ c = (c<<3)+(*pc++ - '0');
+ if (*pc >= '0' && *pc <= '7')
+ c = (c<<3)+(*pc++ - '0');
+ }
+ }
+
+ } else {
+ for (p = delims; *p != '\0'; ++p) {
+ if (*p == c)
+ goto exit;
+ }
+ }
+
+ *buf++ = c;
+ --lim;
+ }
+exit:
+ if (delimp != 0)
+ *delimp = c;
+ *linep = pc-1;
+ if (lim != 0)
+ *buf = '\0';
+ return 0;
+}
diff --git a/sbin/routed/table.c b/sbin/routed/table.c
new file mode 100644
index 0000000..491c5cf
--- /dev/null
+++ b/sbin/routed/table.c
@@ -0,0 +1,2153 @@
+/*
+ * Copyright (c) 1983, 1988, 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.
+ * 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+
+#ifdef __NetBSD__
+__RCSID("$NetBSD$");
+#elif defined(__FreeBSD__)
+__RCSID("$FreeBSD$");
+#else
+__RCSID("$Revision: 2.27 $");
+#ident "$Revision: 2.27 $"
+#endif
+
+static struct rt_spare *rts_better(struct rt_entry *);
+static struct rt_spare rts_empty = {0,0,0,HOPCNT_INFINITY,0,0,0};
+static void set_need_flash(void);
+#ifdef _HAVE_SIN_LEN
+static void masktrim(struct sockaddr_in *ap);
+#else
+static void masktrim(struct sockaddr_in_new *ap);
+#endif
+
+
+struct radix_node_head *rhead; /* root of the radix tree */
+
+int need_flash = 1; /* flash update needed
+ * start =1 to suppress the 1st
+ */
+
+struct timeval age_timer; /* next check of old routes */
+struct timeval need_kern = { /* need to update kernel table */
+ EPOCH+MIN_WAITTIME-1, 0
+};
+
+int stopint;
+
+int total_routes;
+
+/* zap any old routes through this gateway */
+naddr age_bad_gate;
+
+
+/* It is desirable to "aggregate" routes, to combine differing routes of
+ * the same metric and next hop into a common route with a smaller netmask
+ * or to suppress redundant routes, routes that add no information to
+ * routes with smaller netmasks.
+ *
+ * A route is redundant if and only if any and all routes with smaller
+ * but matching netmasks and nets are the same. Since routes are
+ * kept sorted in the radix tree, redundant routes always come second.
+ *
+ * There are two kinds of aggregations. First, two routes of the same bit
+ * mask and differing only in the least significant bit of the network
+ * number can be combined into a single route with a coarser mask.
+ *
+ * Second, a route can be suppressed in favor of another route with a more
+ * coarse mask provided no incompatible routes with intermediate masks
+ * are present. The second kind of aggregation involves suppressing routes.
+ * A route must not be suppressed if an incompatible route exists with
+ * an intermediate mask, since the suppressed route would be covered
+ * by the intermediate.
+ *
+ * This code relies on the radix tree walk encountering routes
+ * sorted first by address, with the smallest address first.
+ */
+
+struct ag_info ag_slots[NUM_AG_SLOTS], *ag_avail, *ag_corsest, *ag_finest;
+
+/* #define DEBUG_AG */
+#ifdef DEBUG_AG
+#define CHECK_AG() {int acnt = 0; struct ag_info *cag; \
+ for (cag = ag_avail; cag != 0; cag = cag->ag_fine) \
+ acnt++; \
+ for (cag = ag_corsest; cag != 0; cag = cag->ag_fine) \
+ acnt++; \
+ if (acnt != NUM_AG_SLOTS) { \
+ (void)fflush(stderr); \
+ abort(); \
+ } \
+}
+#else
+#define CHECK_AG()
+#endif
+
+
+/* Output the contents of an aggregation table slot.
+ * This function must always be immediately followed with the deletion
+ * of the target slot.
+ */
+static void
+ag_out(struct ag_info *ag,
+ void (*out)(struct ag_info *))
+{
+ struct ag_info *ag_cors;
+ naddr bit;
+
+
+ /* Forget it if this route should not be output for split-horizon. */
+ if (ag->ag_state & AGS_SPLIT_HZ)
+ return;
+
+ /* If we output both the even and odd twins, then the immediate parent,
+ * if it is present, is redundant, unless the parent manages to
+ * aggregate into something coarser.
+ * On successive calls, this code detects the even and odd twins,
+ * and marks the parent.
+ *
+ * Note that the order in which the radix tree code emits routes
+ * ensures that the twins are seen before the parent is emitted.
+ */
+ ag_cors = ag->ag_cors;
+ if (ag_cors != 0
+ && ag_cors->ag_mask == ag->ag_mask<<1
+ && ag_cors->ag_dst_h == (ag->ag_dst_h & ag_cors->ag_mask)) {
+ ag_cors->ag_state |= ((ag_cors->ag_dst_h == ag->ag_dst_h)
+ ? AGS_REDUN0
+ : AGS_REDUN1);
+ }
+
+ /* Skip it if this route is itself redundant.
+ *
+ * It is ok to change the contents of the slot here, since it is
+ * always deleted next.
+ */
+ if (ag->ag_state & AGS_REDUN0) {
+ if (ag->ag_state & AGS_REDUN1)
+ return; /* quit if fully redundant */
+ /* make it finer if it is half-redundant */
+ bit = (-ag->ag_mask) >> 1;
+ ag->ag_dst_h |= bit;
+ ag->ag_mask |= bit;
+
+ } else if (ag->ag_state & AGS_REDUN1) {
+ /* make it finer if it is half-redundant */
+ bit = (-ag->ag_mask) >> 1;
+ ag->ag_mask |= bit;
+ }
+ out(ag);
+}
+
+
+static void
+ag_del(struct ag_info *ag)
+{
+ CHECK_AG();
+
+ if (ag->ag_cors == 0)
+ ag_corsest = ag->ag_fine;
+ else
+ ag->ag_cors->ag_fine = ag->ag_fine;
+
+ if (ag->ag_fine == 0)
+ ag_finest = ag->ag_cors;
+ else
+ ag->ag_fine->ag_cors = ag->ag_cors;
+
+ ag->ag_fine = ag_avail;
+ ag_avail = ag;
+
+ CHECK_AG();
+}
+
+
+/* Flush routes waiting for aggregation.
+ * This must not suppress a route unless it is known that among all
+ * routes with coarser masks that match it, the one with the longest
+ * mask is appropriate. This is ensured by scanning the routes
+ * in lexical order, and with the most restrictive mask first
+ * among routes to the same destination.
+ */
+void
+ag_flush(naddr lim_dst_h, /* flush routes to here */
+ naddr lim_mask, /* matching this mask */
+ void (*out)(struct ag_info *))
+{
+ struct ag_info *ag, *ag_cors;
+ naddr dst_h;
+
+
+ for (ag = ag_finest;
+ ag != 0 && ag->ag_mask >= lim_mask;
+ ag = ag_cors) {
+ ag_cors = ag->ag_cors;
+
+ /* work on only the specified routes */
+ dst_h = ag->ag_dst_h;
+ if ((dst_h & lim_mask) != lim_dst_h)
+ continue;
+
+ if (!(ag->ag_state & AGS_SUPPRESS))
+ ag_out(ag, out);
+
+ else for ( ; ; ag_cors = ag_cors->ag_cors) {
+ /* Look for a route that can suppress the
+ * current route */
+ if (ag_cors == 0) {
+ /* failed, so output it and look for
+ * another route to work on
+ */
+ ag_out(ag, out);
+ break;
+ }
+
+ if ((dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h) {
+ /* We found a route with a coarser mask that
+ * aggregates the current target.
+ *
+ * If it has a different next hop, it
+ * cannot replace the target, so output
+ * the target.
+ */
+ if (ag->ag_gate != ag_cors->ag_gate
+ && !(ag->ag_state & AGS_FINE_GATE)
+ && !(ag_cors->ag_state & AGS_CORS_GATE)) {
+ ag_out(ag, out);
+ break;
+ }
+
+ /* If the coarse route has a good enough
+ * metric, it suppresses the target.
+ * If the suppressed target was redundant,
+ * then mark the suppressor redundant.
+ */
+ if (ag_cors->ag_pref <= ag->ag_pref) {
+ if (AG_IS_REDUN(ag->ag_state)
+ && ag_cors->ag_mask==ag->ag_mask<<1) {
+ if (ag_cors->ag_dst_h == dst_h)
+ ag_cors->ag_state |= AGS_REDUN0;
+ else
+ ag_cors->ag_state |= AGS_REDUN1;
+ }
+ if (ag->ag_tag != ag_cors->ag_tag)
+ ag_cors->ag_tag = 0;
+ if (ag->ag_nhop != ag_cors->ag_nhop)
+ ag_cors->ag_nhop = 0;
+ break;
+ }
+ }
+ }
+
+ /* That route has either been output or suppressed */
+ ag_cors = ag->ag_cors;
+ ag_del(ag);
+ }
+
+ CHECK_AG();
+}
+
+
+/* Try to aggregate a route with previous routes.
+ */
+void
+ag_check(naddr dst,
+ naddr mask,
+ naddr gate,
+ naddr nhop,
+ char metric,
+ char pref,
+ u_int new_seqno,
+ u_short tag,
+ u_short state,
+ void (*out)(struct ag_info *)) /* output using this */
+{
+ struct ag_info *ag, *nag, *ag_cors;
+ naddr xaddr;
+ int x;
+
+ dst = ntohl(dst);
+
+ /* Punt non-contiguous subnet masks.
+ *
+ * (X & -X) contains a single bit if and only if X is a power of 2.
+ * (X + (X & -X)) == 0 if and only if X is a power of 2.
+ */
+ if ((mask & -mask) + mask != 0) {
+ struct ag_info nc_ag;
+
+ nc_ag.ag_dst_h = dst;
+ nc_ag.ag_mask = mask;
+ nc_ag.ag_gate = gate;
+ nc_ag.ag_nhop = nhop;
+ nc_ag.ag_metric = metric;
+ nc_ag.ag_pref = pref;
+ nc_ag.ag_tag = tag;
+ nc_ag.ag_state = state;
+ nc_ag.ag_seqno = new_seqno;
+ out(&nc_ag);
+ return;
+ }
+
+ /* Search for the right slot in the aggregation table.
+ */
+ ag_cors = 0;
+ ag = ag_corsest;
+ while (ag != 0) {
+ if (ag->ag_mask >= mask)
+ break;
+
+ /* Suppress old routes (i.e. combine with compatible routes
+ * with coarser masks) as we look for the right slot in the
+ * aggregation table for the new route.
+ * A route to an address less than the current destination
+ * will not be affected by the current route or any route
+ * seen hereafter. That means it is safe to suppress it.
+ * This check keeps poor routes (e.g. with large hop counts)
+ * from preventing suppression of finer routes.
+ */
+ if (ag_cors != 0
+ && ag->ag_dst_h < dst
+ && (ag->ag_state & AGS_SUPPRESS)
+ && ag_cors->ag_pref <= ag->ag_pref
+ && (ag->ag_dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h
+ && (ag_cors->ag_gate == ag->ag_gate
+ || (ag->ag_state & AGS_FINE_GATE)
+ || (ag_cors->ag_state & AGS_CORS_GATE))) {
+ /* If the suppressed target was redundant,
+ * then mark the suppressor redundant.
+ */
+ if (AG_IS_REDUN(ag->ag_state)
+ && ag_cors->ag_mask == ag->ag_mask<<1) {
+ if (ag_cors->ag_dst_h == dst)
+ ag_cors->ag_state |= AGS_REDUN0;
+ else
+ ag_cors->ag_state |= AGS_REDUN1;
+ }
+ if (ag->ag_tag != ag_cors->ag_tag)
+ ag_cors->ag_tag = 0;
+ if (ag->ag_nhop != ag_cors->ag_nhop)
+ ag_cors->ag_nhop = 0;
+ ag_del(ag);
+ CHECK_AG();
+ } else {
+ ag_cors = ag;
+ }
+ ag = ag_cors->ag_fine;
+ }
+
+ /* If we find the even/odd twin of the new route, and if the
+ * masks and so forth are equal, we can aggregate them.
+ * We can probably promote one of the pair.
+ *
+ * Since the routes are encountered in lexical order,
+ * the new route must be odd. However, the second or later
+ * times around this loop, it could be the even twin promoted
+ * from the even/odd pair of twins of the finer route.
+ */
+ while (ag != 0
+ && ag->ag_mask == mask
+ && ((ag->ag_dst_h ^ dst) & (mask<<1)) == 0) {
+
+ /* Here we know the target route and the route in the current
+ * slot have the same netmasks and differ by at most the
+ * last bit. They are either for the same destination, or
+ * for an even/odd pair of destinations.
+ */
+ if (ag->ag_dst_h == dst) {
+ /* We have two routes to the same destination.
+ * Routes are encountered in lexical order, so a
+ * route is never promoted until the parent route is
+ * already present. So we know that the new route is
+ * a promoted (or aggregated) pair and the route
+ * already in the slot is the explicit route.
+ *
+ * Prefer the best route if their metrics differ,
+ * or the aggregated one if not, following a sort
+ * of longest-match rule.
+ */
+ if (pref <= ag->ag_pref) {
+ ag->ag_gate = gate;
+ ag->ag_nhop = nhop;
+ ag->ag_tag = tag;
+ ag->ag_metric = metric;
+ ag->ag_pref = pref;
+ if (ag->ag_seqno < new_seqno)
+ ag->ag_seqno = new_seqno;
+ x = ag->ag_state;
+ ag->ag_state = state;
+ state = x;
+ }
+
+ /* Some bits are set if they are set on either route,
+ * except when the route is for an interface.
+ */
+ if (!(ag->ag_state & AGS_IF))
+ ag->ag_state |= (state & (AGS_AGGREGATE_EITHER
+ | AGS_REDUN0
+ | AGS_REDUN1));
+ return;
+ }
+
+ /* If one of the routes can be promoted and the other can
+ * be suppressed, it may be possible to combine them or
+ * worthwhile to promote one.
+ *
+ * Any route that can be promoted is always
+ * marked to be eligible to be suppressed.
+ */
+ if (!((state & AGS_AGGREGATE)
+ && (ag->ag_state & AGS_SUPPRESS))
+ && !((ag->ag_state & AGS_AGGREGATE)
+ && (state & AGS_SUPPRESS)))
+ break;
+
+ /* A pair of even/odd twin routes can be combined
+ * if either is redundant, or if they are via the
+ * same gateway and have the same metric.
+ */
+ if (AG_IS_REDUN(ag->ag_state)
+ || AG_IS_REDUN(state)
+ || (ag->ag_gate == gate
+ && ag->ag_pref == pref
+ && (state & ag->ag_state & AGS_AGGREGATE) != 0)) {
+
+ /* We have both the even and odd pairs.
+ * Since the routes are encountered in order,
+ * the route in the slot must be the even twin.
+ *
+ * Combine and promote (aggregate) the pair of routes.
+ */
+ if (new_seqno < ag->ag_seqno)
+ new_seqno = ag->ag_seqno;
+ if (!AG_IS_REDUN(state))
+ state &= ~AGS_REDUN1;
+ if (AG_IS_REDUN(ag->ag_state))
+ state |= AGS_REDUN0;
+ else
+ state &= ~AGS_REDUN0;
+ state |= (ag->ag_state & AGS_AGGREGATE_EITHER);
+ if (ag->ag_tag != tag)
+ tag = 0;
+ if (ag->ag_nhop != nhop)
+ nhop = 0;
+
+ /* Get rid of the even twin that was already
+ * in the slot.
+ */
+ ag_del(ag);
+
+ } else if (ag->ag_pref >= pref
+ && (ag->ag_state & AGS_AGGREGATE)) {
+ /* If we cannot combine the pair, maybe the route
+ * with the worse metric can be promoted.
+ *
+ * Promote the old, even twin, by giving its slot
+ * in the table to the new, odd twin.
+ */
+ ag->ag_dst_h = dst;
+
+ xaddr = ag->ag_gate;
+ ag->ag_gate = gate;
+ gate = xaddr;
+
+ xaddr = ag->ag_nhop;
+ ag->ag_nhop = nhop;
+ nhop = xaddr;
+
+ x = ag->ag_tag;
+ ag->ag_tag = tag;
+ tag = x;
+
+ /* The promoted route is even-redundant only if the
+ * even twin was fully redundant. It is not
+ * odd-redundant because the odd-twin will still be
+ * in the table.
+ */
+ x = ag->ag_state;
+ if (!AG_IS_REDUN(x))
+ x &= ~AGS_REDUN0;
+ x &= ~AGS_REDUN1;
+ ag->ag_state = state;
+ state = x;
+
+ x = ag->ag_metric;
+ ag->ag_metric = metric;
+ metric = x;
+
+ x = ag->ag_pref;
+ ag->ag_pref = pref;
+ pref = x;
+
+ /* take the newest sequence number */
+ if (new_seqno <= ag->ag_seqno)
+ new_seqno = ag->ag_seqno;
+ else
+ ag->ag_seqno = new_seqno;
+
+ } else {
+ if (!(state & AGS_AGGREGATE))
+ break; /* cannot promote either twin */
+
+ /* Promote the new, odd twin by shaving its
+ * mask and address.
+ * The promoted route is odd-redundant only if the
+ * odd twin was fully redundant. It is not
+ * even-redundant because the even twin is still in
+ * the table.
+ */
+ if (!AG_IS_REDUN(state))
+ state &= ~AGS_REDUN1;
+ state &= ~AGS_REDUN0;
+ if (new_seqno < ag->ag_seqno)
+ new_seqno = ag->ag_seqno;
+ else
+ ag->ag_seqno = new_seqno;
+ }
+
+ mask <<= 1;
+ dst &= mask;
+
+ if (ag_cors == 0) {
+ ag = ag_corsest;
+ break;
+ }
+ ag = ag_cors;
+ ag_cors = ag->ag_cors;
+ }
+
+ /* When we can no longer promote and combine routes,
+ * flush the old route in the target slot. Also flush
+ * any finer routes that we know will never be aggregated by
+ * the new route.
+ *
+ * In case we moved toward coarser masks,
+ * get back where we belong
+ */
+ if (ag != 0
+ && ag->ag_mask < mask) {
+ ag_cors = ag;
+ ag = ag->ag_fine;
+ }
+
+ /* Empty the target slot
+ */
+ if (ag != 0 && ag->ag_mask == mask) {
+ ag_flush(ag->ag_dst_h, ag->ag_mask, out);
+ ag = (ag_cors == 0) ? ag_corsest : ag_cors->ag_fine;
+ }
+
+#ifdef DEBUG_AG
+ (void)fflush(stderr);
+ if (ag == 0 && ag_cors != ag_finest)
+ abort();
+ if (ag_cors == 0 && ag != ag_corsest)
+ abort();
+ if (ag != 0 && ag->ag_cors != ag_cors)
+ abort();
+ if (ag_cors != 0 && ag_cors->ag_fine != ag)
+ abort();
+ CHECK_AG();
+#endif
+
+ /* Save the new route on the end of the table.
+ */
+ nag = ag_avail;
+ ag_avail = nag->ag_fine;
+
+ nag->ag_dst_h = dst;
+ nag->ag_mask = mask;
+ nag->ag_gate = gate;
+ nag->ag_nhop = nhop;
+ nag->ag_metric = metric;
+ nag->ag_pref = pref;
+ nag->ag_tag = tag;
+ nag->ag_state = state;
+ nag->ag_seqno = new_seqno;
+
+ nag->ag_fine = ag;
+ if (ag != 0)
+ ag->ag_cors = nag;
+ else
+ ag_finest = nag;
+ nag->ag_cors = ag_cors;
+ if (ag_cors == 0)
+ ag_corsest = nag;
+ else
+ ag_cors->ag_fine = nag;
+ CHECK_AG();
+}
+
+
+#define NAME0_LEN 14
+static const char *
+rtm_type_name(u_char type)
+{
+ static const char *rtm_types[] = {
+ "RTM_ADD",
+ "RTM_DELETE",
+ "RTM_CHANGE",
+ "RTM_GET",
+ "RTM_LOSING",
+ "RTM_REDIRECT",
+ "RTM_MISS",
+ "RTM_LOCK",
+ "RTM_OLDADD",
+ "RTM_OLDDEL",
+ "RTM_RESOLVE",
+ "RTM_NEWADDR",
+ "RTM_DELADDR",
+#ifdef RTM_OIFINFO
+ "RTM_OIFINFO",
+#endif
+ "RTM_IFINFO",
+ "RTM_NEWMADDR",
+ "RTM_DELMADDR"
+ };
+#define NEW_RTM_PAT "RTM type %#x"
+ static char name0[sizeof(NEW_RTM_PAT)+2];
+
+
+ if (type > sizeof(rtm_types)/sizeof(rtm_types[0])
+ || type == 0) {
+ snprintf(name0, sizeof(name0), NEW_RTM_PAT, type);
+ return name0;
+ } else {
+ return rtm_types[type-1];
+ }
+#undef NEW_RTM_PAT
+}
+
+
+/* Trim a mask in a sockaddr
+ * Produce a length of 0 for an address of 0.
+ * Otherwise produce the index of the first zero byte.
+ */
+void
+#ifdef _HAVE_SIN_LEN
+masktrim(struct sockaddr_in *ap)
+#else
+masktrim(struct sockaddr_in_new *ap)
+#endif
+{
+ char *cp;
+
+ if (ap->sin_addr.s_addr == 0) {
+ ap->sin_len = 0;
+ return;
+ }
+ cp = (char *)(&ap->sin_addr.s_addr+1);
+ while (*--cp == 0)
+ continue;
+ ap->sin_len = cp - (char*)ap + 1;
+}
+
+
+/* Tell the kernel to add, delete or change a route
+ */
+static void
+rtioctl(int action, /* RTM_DELETE, etc */
+ naddr dst,
+ naddr gate,
+ naddr mask,
+ int metric,
+ int flags)
+{
+ struct {
+ struct rt_msghdr w_rtm;
+ struct sockaddr_in w_dst;
+ struct sockaddr_in w_gate;
+#ifdef _HAVE_SA_LEN
+ struct sockaddr_in w_mask;
+#else
+ struct sockaddr_in_new w_mask;
+#endif
+ } w;
+ long cc;
+# define PAT " %-10s %s metric=%d flags=%#x"
+# define ARGS rtm_type_name(action), rtname(dst,mask,gate), metric, flags
+
+again:
+ memset(&w, 0, sizeof(w));
+ w.w_rtm.rtm_msglen = sizeof(w);
+ w.w_rtm.rtm_version = RTM_VERSION;
+ w.w_rtm.rtm_type = action;
+ w.w_rtm.rtm_flags = flags;
+ w.w_rtm.rtm_seq = ++rt_sock_seqno;
+ w.w_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
+ if (metric != 0 || action == RTM_CHANGE) {
+ w.w_rtm.rtm_rmx.rmx_hopcount = metric;
+ w.w_rtm.rtm_inits |= RTV_HOPCOUNT;
+ }
+ w.w_dst.sin_family = AF_INET;
+ w.w_dst.sin_addr.s_addr = dst;
+ w.w_gate.sin_family = AF_INET;
+ w.w_gate.sin_addr.s_addr = gate;
+#ifdef _HAVE_SA_LEN
+ w.w_dst.sin_len = sizeof(w.w_dst);
+ w.w_gate.sin_len = sizeof(w.w_gate);
+#endif
+ if (mask == HOST_MASK) {
+ w.w_rtm.rtm_flags |= RTF_HOST;
+ w.w_rtm.rtm_msglen -= sizeof(w.w_mask);
+ } else {
+ w.w_rtm.rtm_addrs |= RTA_NETMASK;
+ w.w_mask.sin_addr.s_addr = htonl(mask);
+#ifdef _HAVE_SA_LEN
+ masktrim(&w.w_mask);
+ if (w.w_mask.sin_len == 0)
+ w.w_mask.sin_len = sizeof(long);
+ w.w_rtm.rtm_msglen -= (sizeof(w.w_mask) - w.w_mask.sin_len);
+#endif
+ }
+
+#ifndef NO_INSTALL
+ cc = write(rt_sock, &w, w.w_rtm.rtm_msglen);
+ if (cc < 0) {
+ if (errno == ESRCH
+ && (action == RTM_CHANGE || action == RTM_DELETE)) {
+ trace_act("route disappeared before" PAT, ARGS);
+ if (action == RTM_CHANGE) {
+ action = RTM_ADD;
+ goto again;
+ }
+ return;
+ }
+ msglog("write(rt_sock)" PAT ": %s", ARGS, strerror(errno));
+ return;
+ } else if (cc != w.w_rtm.rtm_msglen) {
+ msglog("write(rt_sock) wrote %ld instead of %d for" PAT,
+ cc, w.w_rtm.rtm_msglen, ARGS);
+ return;
+ }
+#endif
+ if (TRACEKERNEL)
+ trace_misc("write kernel" PAT, ARGS);
+#undef PAT
+#undef ARGS
+}
+
+
+#define KHASH_SIZE 71 /* should be prime */
+#define KHASH(a,m) khash_bins[((a) ^ (m)) % KHASH_SIZE]
+static struct khash {
+ struct khash *k_next;
+ naddr k_dst;
+ naddr k_mask;
+ naddr k_gate;
+ short k_metric;
+ u_short k_state;
+#define KS_NEW 0x001
+#define KS_DELETE 0x002 /* need to delete the route */
+#define KS_ADD 0x004 /* add to the kernel */
+#define KS_CHANGE 0x008 /* tell kernel to change the route */
+#define KS_DEL_ADD 0x010 /* delete & add to change the kernel */
+#define KS_STATIC 0x020 /* Static flag in kernel */
+#define KS_GATEWAY 0x040 /* G flag in kernel */
+#define KS_DYNAMIC 0x080 /* result of redirect */
+#define KS_DELETED 0x100 /* already deleted from kernel */
+#define KS_CHECK 0x200
+ time_t k_keep;
+#define K_KEEP_LIM 30
+ time_t k_redirect_time; /* when redirected route 1st seen */
+} *khash_bins[KHASH_SIZE];
+
+
+static struct khash*
+kern_find(naddr dst, naddr mask, struct khash ***ppk)
+{
+ struct khash *k, **pk;
+
+ for (pk = &KHASH(dst,mask); (k = *pk) != 0; pk = &k->k_next) {
+ if (k->k_dst == dst && k->k_mask == mask)
+ break;
+ }
+ if (ppk != 0)
+ *ppk = pk;
+ return k;
+}
+
+
+static struct khash*
+kern_add(naddr dst, naddr mask)
+{
+ struct khash *k, **pk;
+
+ k = kern_find(dst, mask, &pk);
+ if (k != 0)
+ return k;
+
+ k = (struct khash *)rtmalloc(sizeof(*k), "kern_add");
+
+ memset(k, 0, sizeof(*k));
+ k->k_dst = dst;
+ k->k_mask = mask;
+ k->k_state = KS_NEW;
+ k->k_keep = now.tv_sec;
+ *pk = k;
+
+ return k;
+}
+
+
+/* If a kernel route has a non-zero metric, check that it is still in the
+ * daemon table, and not deleted by interfaces coming and going.
+ */
+static void
+kern_check_static(struct khash *k,
+ struct interface *ifp)
+{
+ struct rt_entry *rt;
+ struct rt_spare new;
+
+ if (k->k_metric == 0)
+ return;
+
+ memset(&new, 0, sizeof(new));
+ new.rts_ifp = ifp;
+ new.rts_gate = k->k_gate;
+ new.rts_router = (ifp != 0) ? ifp->int_addr : loopaddr;
+ new.rts_metric = k->k_metric;
+ new.rts_time = now.tv_sec;
+
+ rt = rtget(k->k_dst, k->k_mask);
+ if (rt != 0) {
+ if (!(rt->rt_state & RS_STATIC))
+ rtchange(rt, rt->rt_state | RS_STATIC, &new, 0);
+ } else {
+ rtadd(k->k_dst, k->k_mask, RS_STATIC, &new);
+ }
+}
+
+
+/* operate on a kernel entry
+ */
+static void
+kern_ioctl(struct khash *k,
+ int action, /* RTM_DELETE, etc */
+ int flags)
+
+{
+ switch (action) {
+ case RTM_DELETE:
+ k->k_state &= ~KS_DYNAMIC;
+ if (k->k_state & KS_DELETED)
+ return;
+ k->k_state |= KS_DELETED;
+ break;
+ case RTM_ADD:
+ k->k_state &= ~KS_DELETED;
+ break;
+ case RTM_CHANGE:
+ if (k->k_state & KS_DELETED) {
+ action = RTM_ADD;
+ k->k_state &= ~KS_DELETED;
+ }
+ break;
+ }
+
+ rtioctl(action, k->k_dst, k->k_gate, k->k_mask, k->k_metric, flags);
+}
+
+
+/* add a route the kernel told us
+ */
+static void
+rtm_add(struct rt_msghdr *rtm,
+ struct rt_addrinfo *info,
+ time_t keep)
+{
+ struct khash *k;
+ struct interface *ifp;
+ naddr mask;
+
+
+ if (rtm->rtm_flags & RTF_HOST) {
+ mask = HOST_MASK;
+ } else if (INFO_MASK(info) != 0) {
+ mask = ntohl(S_ADDR(INFO_MASK(info)));
+ } else {
+ msglog("ignore %s without mask", rtm_type_name(rtm->rtm_type));
+ return;
+ }
+
+ k = kern_add(S_ADDR(INFO_DST(info)), mask);
+ if (k->k_state & KS_NEW)
+ k->k_keep = now.tv_sec+keep;
+ if (INFO_GATE(info) == 0) {
+ trace_act("note %s without gateway",
+ rtm_type_name(rtm->rtm_type));
+ k->k_metric = HOPCNT_INFINITY;
+ } else if (INFO_GATE(info)->sa_family != AF_INET) {
+ trace_act("note %s with gateway AF=%d",
+ rtm_type_name(rtm->rtm_type),
+ INFO_GATE(info)->sa_family);
+ k->k_metric = HOPCNT_INFINITY;
+ } else {
+ k->k_gate = S_ADDR(INFO_GATE(info));
+ k->k_metric = rtm->rtm_rmx.rmx_hopcount;
+ if (k->k_metric < 0)
+ k->k_metric = 0;
+ else if (k->k_metric > HOPCNT_INFINITY-1)
+ k->k_metric = HOPCNT_INFINITY-1;
+ }
+ k->k_state &= ~(KS_DELETE | KS_ADD | KS_CHANGE | KS_DEL_ADD
+ | KS_DELETED | KS_GATEWAY | KS_STATIC
+ | KS_NEW | KS_CHECK);
+ if (rtm->rtm_flags & RTF_GATEWAY)
+ k->k_state |= KS_GATEWAY;
+ if (rtm->rtm_flags & RTF_STATIC)
+ k->k_state |= KS_STATIC;
+
+ if (0 != (rtm->rtm_flags & (RTF_DYNAMIC | RTF_MODIFIED))) {
+ if (INFO_AUTHOR(info) != 0
+ && INFO_AUTHOR(info)->sa_family == AF_INET)
+ ifp = iflookup(S_ADDR(INFO_AUTHOR(info)));
+ else
+ ifp = 0;
+ if (supplier
+ && (ifp == 0 || !(ifp->int_state & IS_REDIRECT_OK))) {
+ /* Routers are not supposed to listen to redirects,
+ * so delete it if it came via an unknown interface
+ * or the interface does not have special permission.
+ */
+ k->k_state &= ~KS_DYNAMIC;
+ k->k_state |= KS_DELETE;
+ LIM_SEC(need_kern, 0);
+ trace_act("mark for deletion redirected %s --> %s"
+ " via %s",
+ addrname(k->k_dst, k->k_mask, 0),
+ naddr_ntoa(k->k_gate),
+ ifp ? ifp->int_name : "unknown interface");
+ } else {
+ k->k_state |= KS_DYNAMIC;
+ k->k_redirect_time = now.tv_sec;
+ trace_act("accept redirected %s --> %s via %s",
+ addrname(k->k_dst, k->k_mask, 0),
+ naddr_ntoa(k->k_gate),
+ ifp ? ifp->int_name : "unknown interface");
+ }
+ return;
+ }
+
+ /* If it is not a static route, quit until the next comparison
+ * between the kernel and daemon tables, when it will be deleted.
+ */
+ if (!(k->k_state & KS_STATIC)) {
+ k->k_state |= KS_DELETE;
+ LIM_SEC(need_kern, k->k_keep);
+ return;
+ }
+
+ /* Put static routes with real metrics into the daemon table so
+ * they can be advertised.
+ *
+ * Find the interface toward the gateway.
+ */
+ ifp = iflookup(k->k_gate);
+ if (ifp == 0)
+ msglog("static route %s --> %s impossibly lacks ifp",
+ addrname(S_ADDR(INFO_DST(info)), mask, 0),
+ naddr_ntoa(k->k_gate));
+
+ kern_check_static(k, ifp);
+}
+
+
+/* deal with packet loss
+ */
+static void
+rtm_lose(struct rt_msghdr *rtm,
+ struct rt_addrinfo *info)
+{
+ if (INFO_GATE(info) == 0
+ || INFO_GATE(info)->sa_family != AF_INET) {
+ trace_act("ignore %s without gateway",
+ rtm_type_name(rtm->rtm_type));
+ return;
+ }
+
+ if (rdisc_ok)
+ rdisc_age(S_ADDR(INFO_GATE(info)));
+ age(S_ADDR(INFO_GATE(info)));
+}
+
+
+/* Make the gateway slot of an info structure point to something
+ * useful. If it is not already useful, but it specifies an interface,
+ * then fill in the sockaddr_in provided and point it there.
+ */
+static int
+get_info_gate(struct sockaddr **sap,
+ struct sockaddr_in *rsin)
+{
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)*sap;
+ struct interface *ifp;
+
+ if (sdl == 0)
+ return 0;
+ if ((sdl)->sdl_family == AF_INET)
+ return 1;
+ if ((sdl)->sdl_family != AF_LINK)
+ return 0;
+
+ ifp = ifwithindex(sdl->sdl_index, 1);
+ if (ifp == 0)
+ return 0;
+
+ rsin->sin_addr.s_addr = ifp->int_addr;
+#ifdef _HAVE_SA_LEN
+ rsin->sin_len = sizeof(*rsin);
+#endif
+ rsin->sin_family = AF_INET;
+ *sap = (struct sockaddr*)rsin;
+
+ return 1;
+}
+
+
+/* Clean the kernel table by copying it to the daemon image.
+ * Eventually the daemon will delete any extra routes.
+ */
+void
+flush_kern(void)
+{
+ static char *sysctl_buf;
+ static size_t sysctl_buf_size = 0;
+ size_t needed;
+ int mib[6];
+ char *next, *lim;
+ struct rt_msghdr *rtm;
+ struct sockaddr_in gate_sin;
+ struct rt_addrinfo info;
+ int i;
+ struct khash *k;
+
+
+ for (i = 0; i < KHASH_SIZE; i++) {
+ for (k = khash_bins[i]; k != 0; k = k->k_next) {
+ k->k_state |= KS_CHECK;
+ }
+ }
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0; /* protocol */
+ mib[3] = 0; /* wildcard address family */
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0; /* no flags */
+ for (;;) {
+ if ((needed = sysctl_buf_size) != 0) {
+ if (sysctl(mib, 6, sysctl_buf,&needed, 0, 0) >= 0)
+ break;
+ if (errno != ENOMEM && errno != EFAULT)
+ BADERR(1,"flush_kern: sysctl(RT_DUMP)");
+ free(sysctl_buf);
+ needed = 0;
+ }
+ if (sysctl(mib, 6, 0, &needed, 0, 0) < 0)
+ BADERR(1,"flush_kern: sysctl(RT_DUMP) estimate");
+ /* Kludge around the habit of some systems, such as
+ * BSD/OS 3.1, to not admit how many routes are in the
+ * kernel, or at least to be quite wrong.
+ */
+ needed += 50*(sizeof(*rtm)+5*sizeof(struct sockaddr));
+ sysctl_buf = rtmalloc(sysctl_buf_size = needed,
+ "flush_kern sysctl(RT_DUMP)");
+ }
+
+ lim = sysctl_buf + needed;
+ for (next = sysctl_buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ if (rtm->rtm_msglen == 0) {
+ msglog("zero length kernel route at "
+ " %#lx in buffer %#lx before %#lx",
+ (u_long)rtm, (u_long)sysctl_buf, (u_long)lim);
+ break;
+ }
+
+ rt_xaddrs(&info,
+ (struct sockaddr *)(rtm+1),
+ (struct sockaddr *)(next + rtm->rtm_msglen),
+ rtm->rtm_addrs);
+
+ if (INFO_DST(&info) == 0
+ || INFO_DST(&info)->sa_family != AF_INET)
+ continue;
+
+ /* ignore ARP table entries on systems with a merged route
+ * and ARP table.
+ */
+ if (rtm->rtm_flags & RTF_LLINFO)
+ continue;
+
+#if defined(RTF_WASCLONED) && defined(__FreeBSD__)
+ /* ignore cloned routes
+ */
+ if (rtm->rtm_flags & RTF_WASCLONED)
+ continue;
+#endif
+
+ /* ignore multicast addresses
+ */
+ if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info)))))
+ continue;
+
+ if (!get_info_gate(&INFO_GATE(&info), &gate_sin))
+ continue;
+
+ /* Note static routes and interface routes, and also
+ * preload the image of the kernel table so that
+ * we can later clean it, as well as avoid making
+ * unneeded changes. Keep the old kernel routes for a
+ * few seconds to allow a RIP or router-discovery
+ * response to be heard.
+ */
+ rtm_add(rtm,&info,MIN_WAITTIME);
+ }
+
+ for (i = 0; i < KHASH_SIZE; i++) {
+ for (k = khash_bins[i]; k != 0; k = k->k_next) {
+ if (k->k_state & KS_CHECK) {
+ msglog("%s --> %s disappeared from kernel",
+ addrname(k->k_dst, k->k_mask, 0),
+ naddr_ntoa(k->k_gate));
+ del_static(k->k_dst, k->k_mask, k->k_gate, 1);
+ }
+ }
+ }
+}
+
+
+/* Listen to announcements from the kernel
+ */
+void
+read_rt(void)
+{
+ long cc;
+ struct interface *ifp;
+ struct sockaddr_in gate_sin;
+ naddr mask, gate;
+ union {
+ struct {
+ struct rt_msghdr rtm;
+ struct sockaddr addrs[RTAX_MAX];
+ } r;
+ struct if_msghdr ifm;
+ } m;
+ char str[100], *strp;
+ struct rt_addrinfo info;
+
+
+ for (;;) {
+ cc = read(rt_sock, &m, sizeof(m));
+ if (cc <= 0) {
+ if (cc < 0 && errno != EWOULDBLOCK)
+ LOGERR("read(rt_sock)");
+ return;
+ }
+
+ if (m.r.rtm.rtm_version != RTM_VERSION) {
+ msglog("bogus routing message version %d",
+ m.r.rtm.rtm_version);
+ continue;
+ }
+
+ /* Ignore our own results.
+ */
+ if (m.r.rtm.rtm_type <= RTM_CHANGE
+ && m.r.rtm.rtm_pid == mypid) {
+ static int complained = 0;
+ if (!complained) {
+ msglog("receiving our own change messages");
+ complained = 1;
+ }
+ continue;
+ }
+
+ if (m.r.rtm.rtm_type == RTM_IFINFO
+ || m.r.rtm.rtm_type == RTM_NEWADDR
+ || m.r.rtm.rtm_type == RTM_DELADDR) {
+ ifp = ifwithindex(m.ifm.ifm_index,
+ m.r.rtm.rtm_type != RTM_DELADDR);
+ if (ifp == 0)
+ trace_act("note %s with flags %#x"
+ " for unknown interface index #%d",
+ rtm_type_name(m.r.rtm.rtm_type),
+ m.ifm.ifm_flags,
+ m.ifm.ifm_index);
+ else
+ trace_act("note %s with flags %#x for %s",
+ rtm_type_name(m.r.rtm.rtm_type),
+ m.ifm.ifm_flags,
+ ifp->int_name);
+
+ /* After being informed of a change to an interface,
+ * check them all now if the check would otherwise
+ * be a long time from now, if the interface is
+ * not known, or if the interface has been turned
+ * off or on.
+ */
+ if (ifinit_timer.tv_sec-now.tv_sec>=CHECK_BAD_INTERVAL
+ || ifp == 0
+ || ((ifp->int_if_flags ^ m.ifm.ifm_flags)
+ & IFF_UP) != 0)
+ ifinit_timer.tv_sec = now.tv_sec;
+ continue;
+ }
+#ifdef RTM_OIFINFO
+ if (m.r.rtm.rtm_type == RTM_OIFINFO)
+ continue; /* ignore compat message */
+#endif
+
+ strcpy(str, rtm_type_name(m.r.rtm.rtm_type));
+ strp = &str[strlen(str)];
+ if (m.r.rtm.rtm_type <= RTM_CHANGE)
+ strp += sprintf(strp," from pid %d",m.r.rtm.rtm_pid);
+
+ rt_xaddrs(&info, m.r.addrs, &m.r.addrs[RTAX_MAX],
+ m.r.rtm.rtm_addrs);
+
+ if (INFO_DST(&info) == 0) {
+ trace_act("ignore %s without dst", str);
+ continue;
+ }
+
+ if (INFO_DST(&info)->sa_family != AF_INET) {
+ trace_act("ignore %s for AF %d", str,
+ INFO_DST(&info)->sa_family);
+ continue;
+ }
+
+ mask = ((INFO_MASK(&info) != 0)
+ ? ntohl(S_ADDR(INFO_MASK(&info)))
+ : (m.r.rtm.rtm_flags & RTF_HOST)
+ ? HOST_MASK
+ : std_mask(S_ADDR(INFO_DST(&info))));
+
+ strp += sprintf(strp, ": %s",
+ addrname(S_ADDR(INFO_DST(&info)), mask, 0));
+
+ if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) {
+ trace_act("ignore multicast %s", str);
+ continue;
+ }
+
+ if (m.r.rtm.rtm_flags & RTF_LLINFO) {
+ trace_act("ignore ARP %s", str);
+ continue;
+ }
+
+#if defined(RTF_WASCLONED) && defined(__FreeBSD__)
+ if (m.r.rtm.rtm_flags & RTF_WASCLONED) {
+ trace_act("ignore cloned %s", str);
+ continue;
+ }
+#endif
+
+ if (get_info_gate(&INFO_GATE(&info), &gate_sin)) {
+ gate = S_ADDR(INFO_GATE(&info));
+ strp += sprintf(strp, " --> %s", naddr_ntoa(gate));
+ } else {
+ gate = 0;
+ }
+
+ if (INFO_AUTHOR(&info) != 0)
+ strp += sprintf(strp, " by authority of %s",
+ saddr_ntoa(INFO_AUTHOR(&info)));
+
+ switch (m.r.rtm.rtm_type) {
+ case RTM_ADD:
+ case RTM_CHANGE:
+ case RTM_REDIRECT:
+ if (m.r.rtm.rtm_errno != 0) {
+ trace_act("ignore %s with \"%s\" error",
+ str, strerror(m.r.rtm.rtm_errno));
+ } else {
+ trace_act("%s", str);
+ rtm_add(&m.r.rtm,&info,0);
+ }
+ break;
+
+ case RTM_DELETE:
+ if (m.r.rtm.rtm_errno != 0
+ && m.r.rtm.rtm_errno != ESRCH) {
+ trace_act("ignore %s with \"%s\" error",
+ str, strerror(m.r.rtm.rtm_errno));
+ } else {
+ trace_act("%s", str);
+ del_static(S_ADDR(INFO_DST(&info)), mask,
+ gate, 1);
+ }
+ break;
+
+ case RTM_LOSING:
+ trace_act("%s", str);
+ rtm_lose(&m.r.rtm,&info);
+ break;
+
+ default:
+ trace_act("ignore %s", str);
+ break;
+ }
+ }
+}
+
+
+/* after aggregating, note routes that belong in the kernel
+ */
+static void
+kern_out(struct ag_info *ag)
+{
+ struct khash *k;
+
+
+ /* Do not install bad routes if they are not already present.
+ * This includes routes that had RS_NET_SYN for interfaces that
+ * recently died.
+ */
+ if (ag->ag_metric == HOPCNT_INFINITY) {
+ k = kern_find(htonl(ag->ag_dst_h), ag->ag_mask, 0);
+ if (k == 0)
+ return;
+ } else {
+ k = kern_add(htonl(ag->ag_dst_h), ag->ag_mask);
+ }
+
+ if (k->k_state & KS_NEW) {
+ /* will need to add new entry to the kernel table */
+ k->k_state = KS_ADD;
+ if (ag->ag_state & AGS_GATEWAY)
+ k->k_state |= KS_GATEWAY;
+ k->k_gate = ag->ag_gate;
+ k->k_metric = ag->ag_metric;
+ return;
+ }
+
+ if (k->k_state & KS_STATIC)
+ return;
+
+ /* modify existing kernel entry if necessary */
+ if (k->k_gate != ag->ag_gate
+ || k->k_metric != ag->ag_metric) {
+ /* Must delete bad interface routes etc. to change them. */
+ if (k->k_metric == HOPCNT_INFINITY)
+ k->k_state |= KS_DEL_ADD;
+ k->k_gate = ag->ag_gate;
+ k->k_metric = ag->ag_metric;
+ k->k_state |= KS_CHANGE;
+ }
+
+ /* If the daemon thinks the route should exist, forget
+ * about any redirections.
+ * If the daemon thinks the route should exist, eventually
+ * override manual intervention by the operator.
+ */
+ if ((k->k_state & (KS_DYNAMIC | KS_DELETED)) != 0) {
+ k->k_state &= ~KS_DYNAMIC;
+ k->k_state |= (KS_ADD | KS_DEL_ADD);
+ }
+
+ if ((k->k_state & KS_GATEWAY)
+ && !(ag->ag_state & AGS_GATEWAY)) {
+ k->k_state &= ~KS_GATEWAY;
+ k->k_state |= (KS_ADD | KS_DEL_ADD);
+ } else if (!(k->k_state & KS_GATEWAY)
+ && (ag->ag_state & AGS_GATEWAY)) {
+ k->k_state |= KS_GATEWAY;
+ k->k_state |= (KS_ADD | KS_DEL_ADD);
+ }
+
+ /* Deleting-and-adding is necessary to change aspects of a route.
+ * Just delete instead of deleting and then adding a bad route.
+ * Otherwise, we want to keep the route in the kernel.
+ */
+ if (k->k_metric == HOPCNT_INFINITY
+ && (k->k_state & KS_DEL_ADD))
+ k->k_state |= KS_DELETE;
+ else
+ k->k_state &= ~KS_DELETE;
+#undef RT
+}
+
+
+/* ARGSUSED */
+static int
+walk_kern(struct radix_node *rn,
+ struct walkarg *argp UNUSED)
+{
+#define RT ((struct rt_entry *)rn)
+ char metric, pref;
+ u_int ags = 0;
+
+
+ /* Do not install synthetic routes */
+ if (RT->rt_state & RS_NET_SYN)
+ return 0;
+
+ if (!(RT->rt_state & RS_IF)) {
+ /* This is an ordinary route, not for an interface.
+ */
+
+ /* aggregate, ordinary good routes without regard to
+ * their metric
+ */
+ pref = 1;
+ ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_AGGREGATE);
+
+ /* Do not install host routes directly to hosts, to avoid
+ * interfering with ARP entries in the kernel table.
+ */
+ if (RT_ISHOST(RT)
+ && ntohl(RT->rt_dst) == RT->rt_gate)
+ return 0;
+
+ } else {
+ /* This is an interface route.
+ * Do not install routes for "external" remote interfaces.
+ */
+ if (RT->rt_ifp != 0 && (RT->rt_ifp->int_state & IS_EXTERNAL))
+ return 0;
+
+ /* Interfaces should override received routes.
+ */
+ pref = 0;
+ ags |= (AGS_IF | AGS_CORS_GATE);
+
+ /* If it is not an interface, or an alias for an interface,
+ * it must be a "gateway."
+ *
+ * If it is a "remote" interface, it is also a "gateway" to
+ * the kernel if is not an alias.
+ */
+ if (RT->rt_ifp == 0
+ || (RT->rt_ifp->int_state & IS_REMOTE))
+ ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_AGGREGATE);
+ }
+
+ /* If RIP is off and IRDP is on, let the route to the discovered
+ * route suppress any RIP routes. Eventually the RIP routes
+ * will time-out and be deleted. This reaches the steady-state
+ * quicker.
+ */
+ if ((RT->rt_state & RS_RDISC) && rip_sock < 0)
+ ags |= AGS_CORS_GATE;
+
+ metric = RT->rt_metric;
+ if (metric == HOPCNT_INFINITY) {
+ /* if the route is dead, so try hard to aggregate. */
+ pref = HOPCNT_INFINITY;
+ ags |= (AGS_FINE_GATE | AGS_SUPPRESS);
+ ags &= ~(AGS_IF | AGS_CORS_GATE);
+ }
+
+ ag_check(RT->rt_dst, RT->rt_mask, RT->rt_gate, 0,
+ metric,pref, 0, 0, ags, kern_out);
+ return 0;
+#undef RT
+}
+
+
+/* Update the kernel table to match the daemon table.
+ */
+static void
+fix_kern(void)
+{
+ int i;
+ struct khash *k, **pk;
+
+
+ need_kern = age_timer;
+
+ /* Walk daemon table, updating the copy of the kernel table.
+ */
+ (void)rn_walktree(rhead, walk_kern, 0);
+ ag_flush(0,0,kern_out);
+
+ for (i = 0; i < KHASH_SIZE; i++) {
+ for (pk = &khash_bins[i]; (k = *pk) != 0; ) {
+ /* Do not touch static routes */
+ if (k->k_state & KS_STATIC) {
+ kern_check_static(k,0);
+ pk = &k->k_next;
+ continue;
+ }
+
+ /* check hold on routes deleted by the operator */
+ if (k->k_keep > now.tv_sec) {
+ /* ensure we check when the hold is over */
+ LIM_SEC(need_kern, k->k_keep);
+ /* mark for the next cycle */
+ k->k_state |= KS_DELETE;
+ pk = &k->k_next;
+ continue;
+ }
+
+ if ((k->k_state & KS_DELETE)
+ && !(k->k_state & KS_DYNAMIC)) {
+ kern_ioctl(k, RTM_DELETE, 0);
+ *pk = k->k_next;
+ free(k);
+ continue;
+ }
+
+ if (k->k_state & KS_DEL_ADD)
+ kern_ioctl(k, RTM_DELETE, 0);
+
+ if (k->k_state & KS_ADD) {
+ kern_ioctl(k, RTM_ADD,
+ ((0 != (k->k_state & (KS_GATEWAY
+ | KS_DYNAMIC)))
+ ? RTF_GATEWAY : 0));
+ } else if (k->k_state & KS_CHANGE) {
+ kern_ioctl(k, RTM_CHANGE,
+ ((0 != (k->k_state & (KS_GATEWAY
+ | KS_DYNAMIC)))
+ ? RTF_GATEWAY : 0));
+ }
+ k->k_state &= ~(KS_ADD|KS_CHANGE|KS_DEL_ADD);
+
+ /* Mark this route to be deleted in the next cycle.
+ * This deletes routes that disappear from the
+ * daemon table, since the normal aging code
+ * will clear the bit for routes that have not
+ * disappeared from the daemon table.
+ */
+ k->k_state |= KS_DELETE;
+ pk = &k->k_next;
+ }
+ }
+}
+
+
+/* Delete a static route in the image of the kernel table.
+ */
+void
+del_static(naddr dst,
+ naddr mask,
+ naddr gate,
+ int gone)
+{
+ struct khash *k;
+ struct rt_entry *rt;
+
+ /* Just mark it in the table to be deleted next time the kernel
+ * table is updated.
+ * If it has already been deleted, mark it as such, and set its
+ * keep-timer so that it will not be deleted again for a while.
+ * This lets the operator delete a route added by the daemon
+ * and add a replacement.
+ */
+ k = kern_find(dst, mask, 0);
+ if (k != 0 && (gate == 0 || k->k_gate == gate)) {
+ k->k_state &= ~(KS_STATIC | KS_DYNAMIC | KS_CHECK);
+ k->k_state |= KS_DELETE;
+ if (gone) {
+ k->k_state |= KS_DELETED;
+ k->k_keep = now.tv_sec + K_KEEP_LIM;
+ }
+ }
+
+ rt = rtget(dst, mask);
+ if (rt != 0 && (rt->rt_state & RS_STATIC))
+ rtbad(rt);
+}
+
+
+/* Delete all routes generated from ICMP Redirects that use a given gateway,
+ * as well as old redirected routes.
+ */
+void
+del_redirects(naddr bad_gate,
+ time_t old)
+{
+ int i;
+ struct khash *k;
+
+
+ for (i = 0; i < KHASH_SIZE; i++) {
+ for (k = khash_bins[i]; k != 0; k = k->k_next) {
+ if (!(k->k_state & KS_DYNAMIC)
+ || (k->k_state & KS_STATIC))
+ continue;
+
+ if (k->k_gate != bad_gate
+ && k->k_redirect_time > old
+ && !supplier)
+ continue;
+
+ k->k_state |= KS_DELETE;
+ k->k_state &= ~KS_DYNAMIC;
+ need_kern.tv_sec = now.tv_sec;
+ trace_act("mark redirected %s --> %s for deletion",
+ addrname(k->k_dst, k->k_mask, 0),
+ naddr_ntoa(k->k_gate));
+ }
+ }
+}
+
+
+/* Start the daemon tables.
+ */
+extern int max_keylen;
+
+void
+rtinit(void)
+{
+ int i;
+ struct ag_info *ag;
+
+ /* Initialize the radix trees */
+ max_keylen = sizeof(struct sockaddr_in);
+ rn_init();
+ rn_inithead((void**)&rhead, 32);
+
+ /* mark all of the slots in the table free */
+ ag_avail = ag_slots;
+ for (ag = ag_slots, i = 1; i < NUM_AG_SLOTS; i++) {
+ ag->ag_fine = ag+1;
+ ag++;
+ }
+}
+
+
+#ifdef _HAVE_SIN_LEN
+static struct sockaddr_in dst_sock = {sizeof(dst_sock), AF_INET, 0, {0}, {0}};
+static struct sockaddr_in mask_sock = {sizeof(mask_sock), AF_INET, 0, {0}, {0}};
+#else
+static struct sockaddr_in_new dst_sock = {_SIN_ADDR_SIZE, AF_INET};
+static struct sockaddr_in_new mask_sock = {_SIN_ADDR_SIZE, AF_INET};
+#endif
+
+
+static void
+set_need_flash(void)
+{
+ if (!need_flash) {
+ need_flash = 1;
+ /* Do not send the flash update immediately. Wait a little
+ * while to hear from other routers.
+ */
+ no_flash.tv_sec = now.tv_sec + MIN_WAITTIME;
+ }
+}
+
+
+/* Get a particular routing table entry
+ */
+struct rt_entry *
+rtget(naddr dst, naddr mask)
+{
+ struct rt_entry *rt;
+
+ dst_sock.sin_addr.s_addr = dst;
+ mask_sock.sin_addr.s_addr = htonl(mask);
+ masktrim(&mask_sock);
+ rt = (struct rt_entry *)rhead->rnh_lookup(&dst_sock,&mask_sock,rhead);
+ if (!rt
+ || rt->rt_dst != dst
+ || rt->rt_mask != mask)
+ return 0;
+
+ return rt;
+}
+
+
+/* Find a route to dst as the kernel would.
+ */
+struct rt_entry *
+rtfind(naddr dst)
+{
+ dst_sock.sin_addr.s_addr = dst;
+ return (struct rt_entry *)rhead->rnh_matchaddr(&dst_sock, rhead);
+}
+
+
+/* add a route to the table
+ */
+void
+rtadd(naddr dst,
+ naddr mask,
+ u_int state, /* rt_state for the entry */
+ struct rt_spare *new)
+{
+ struct rt_entry *rt;
+ naddr smask;
+ int i;
+ struct rt_spare *rts;
+
+ rt = (struct rt_entry *)rtmalloc(sizeof (*rt), "rtadd");
+ memset(rt, 0, sizeof(*rt));
+ for (rts = rt->rt_spares, i = NUM_SPARES; i != 0; i--, rts++)
+ rts->rts_metric = HOPCNT_INFINITY;
+
+ rt->rt_nodes->rn_key = (caddr_t)&rt->rt_dst_sock;
+ rt->rt_dst = dst;
+ rt->rt_dst_sock.sin_family = AF_INET;
+#ifdef _HAVE_SIN_LEN
+ rt->rt_dst_sock.sin_len = dst_sock.sin_len;
+#endif
+ if (mask != HOST_MASK) {
+ smask = std_mask(dst);
+ if ((smask & ~mask) == 0 && mask > smask)
+ state |= RS_SUBNET;
+ }
+ mask_sock.sin_addr.s_addr = htonl(mask);
+ masktrim(&mask_sock);
+ rt->rt_mask = mask;
+ rt->rt_state = state;
+ rt->rt_spares[0] = *new;
+ rt->rt_time = now.tv_sec;
+ rt->rt_poison_metric = HOPCNT_INFINITY;
+ rt->rt_seqno = update_seqno;
+
+ if (++total_routes == MAX_ROUTES)
+ msglog("have maximum (%d) routes", total_routes);
+ if (TRACEACTIONS)
+ trace_add_del("Add", rt);
+
+ need_kern.tv_sec = now.tv_sec;
+ set_need_flash();
+
+ if (0 == rhead->rnh_addaddr(&rt->rt_dst_sock, &mask_sock,
+ rhead, rt->rt_nodes)) {
+ msglog("rnh_addaddr() failed for %s mask=%#lx",
+ naddr_ntoa(dst), (u_long)mask);
+ free(rt);
+ }
+}
+
+
+/* notice a changed route
+ */
+void
+rtchange(struct rt_entry *rt,
+ u_int state, /* new state bits */
+ struct rt_spare *new,
+ char *label)
+{
+ if (rt->rt_metric != new->rts_metric) {
+ /* Fix the kernel immediately if it seems the route
+ * has gone bad, since there may be a working route that
+ * aggregates this route.
+ */
+ if (new->rts_metric == HOPCNT_INFINITY) {
+ need_kern.tv_sec = now.tv_sec;
+ if (new->rts_time >= now.tv_sec - EXPIRE_TIME)
+ new->rts_time = now.tv_sec - EXPIRE_TIME;
+ }
+ rt->rt_seqno = update_seqno;
+ set_need_flash();
+ }
+
+ if (rt->rt_gate != new->rts_gate) {
+ need_kern.tv_sec = now.tv_sec;
+ rt->rt_seqno = update_seqno;
+ set_need_flash();
+ }
+
+ state |= (rt->rt_state & RS_SUBNET);
+
+ /* Keep various things from deciding ageless routes are stale.
+ */
+ if (!AGE_RT(state, new->rts_ifp))
+ new->rts_time = now.tv_sec;
+
+ if (TRACEACTIONS)
+ trace_change(rt, state, new,
+ label ? label : "Chg ");
+
+ rt->rt_state = state;
+ rt->rt_spares[0] = *new;
+}
+
+
+/* check for a better route among the spares
+ */
+static struct rt_spare *
+rts_better(struct rt_entry *rt)
+{
+ struct rt_spare *rts, *rts1;
+ int i;
+
+ /* find the best alternative among the spares */
+ rts = rt->rt_spares+1;
+ for (i = NUM_SPARES, rts1 = rts+1; i > 2; i--, rts1++) {
+ if (BETTER_LINK(rt,rts1,rts))
+ rts = rts1;
+ }
+
+ return rts;
+}
+
+
+/* switch to a backup route
+ */
+void
+rtswitch(struct rt_entry *rt,
+ struct rt_spare *rts)
+{
+ struct rt_spare swap;
+ char label[10];
+
+
+ /* Do not change permanent routes */
+ if (0 != (rt->rt_state & (RS_MHOME | RS_STATIC | RS_RDISC
+ | RS_NET_SYN | RS_IF)))
+ return;
+
+ /* find the best alternative among the spares */
+ if (rts == 0)
+ rts = rts_better(rt);
+
+ /* Do not bother if it is not worthwhile.
+ */
+ if (!BETTER_LINK(rt, rts, rt->rt_spares))
+ return;
+
+ swap = rt->rt_spares[0];
+ (void)sprintf(label, "Use #%d", (int)(rts - rt->rt_spares));
+ rtchange(rt, rt->rt_state & ~(RS_NET_SYN | RS_RDISC), rts, label);
+ if (swap.rts_metric == HOPCNT_INFINITY) {
+ *rts = rts_empty;
+ } else {
+ *rts = swap;
+ }
+}
+
+
+void
+rtdelete(struct rt_entry *rt)
+{
+ struct khash *k;
+
+
+ if (TRACEACTIONS)
+ trace_add_del("Del", rt);
+
+ k = kern_find(rt->rt_dst, rt->rt_mask, 0);
+ if (k != 0) {
+ k->k_state |= KS_DELETE;
+ need_kern.tv_sec = now.tv_sec;
+ }
+
+ dst_sock.sin_addr.s_addr = rt->rt_dst;
+ mask_sock.sin_addr.s_addr = htonl(rt->rt_mask);
+ masktrim(&mask_sock);
+ if (rt != (struct rt_entry *)rhead->rnh_deladdr(&dst_sock, &mask_sock,
+ rhead)) {
+ msglog("rnh_deladdr() failed");
+ } else {
+ free(rt);
+ total_routes--;
+ }
+}
+
+
+void
+rts_delete(struct rt_entry *rt,
+ struct rt_spare *rts)
+{
+ trace_upslot(rt, rts, &rts_empty);
+ *rts = rts_empty;
+}
+
+
+/* Get rid of a bad route, and try to switch to a replacement.
+ */
+void
+rtbad(struct rt_entry *rt)
+{
+ struct rt_spare new;
+
+ /* Poison the route */
+ new = rt->rt_spares[0];
+ new.rts_metric = HOPCNT_INFINITY;
+ rtchange(rt, rt->rt_state & ~(RS_IF | RS_LOCAL | RS_STATIC), &new, 0);
+ rtswitch(rt, 0);
+}
+
+
+/* Junk a RS_NET_SYN or RS_LOCAL route,
+ * unless it is needed by another interface.
+ */
+void
+rtbad_sub(struct rt_entry *rt)
+{
+ struct interface *ifp, *ifp1;
+ struct intnet *intnetp;
+ u_int state;
+
+
+ ifp1 = 0;
+ state = 0;
+
+ if (rt->rt_state & RS_LOCAL) {
+ /* Is this the route through loopback for the interface?
+ * If so, see if it is used by any other interfaces, such
+ * as a point-to-point interface with the same local address.
+ */
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
+ /* Retain it if another interface needs it.
+ */
+ if (ifp->int_addr == rt->rt_ifp->int_addr) {
+ state |= RS_LOCAL;
+ ifp1 = ifp;
+ break;
+ }
+ }
+
+ }
+
+ if (!(state & RS_LOCAL)) {
+ /* Retain RIPv1 logical network route if there is another
+ * interface that justifies it.
+ */
+ if (rt->rt_state & RS_NET_SYN) {
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
+ if ((ifp->int_state & IS_NEED_NET_SYN)
+ && rt->rt_mask == ifp->int_std_mask
+ && rt->rt_dst == ifp->int_std_addr) {
+ state |= RS_NET_SYN;
+ ifp1 = ifp;
+ break;
+ }
+ }
+ }
+
+ /* or if there is an authority route that needs it. */
+ for (intnetp = intnets;
+ intnetp != 0;
+ intnetp = intnetp->intnet_next) {
+ if (intnetp->intnet_addr == rt->rt_dst
+ && intnetp->intnet_mask == rt->rt_mask) {
+ state |= (RS_NET_SYN | RS_NET_INT);
+ break;
+ }
+ }
+ }
+
+ if (ifp1 != 0 || (state & RS_NET_SYN)) {
+ struct rt_spare new = rt->rt_spares[0];
+ new.rts_ifp = ifp1;
+ rtchange(rt, ((rt->rt_state & ~(RS_NET_SYN|RS_LOCAL)) | state),
+ &new, 0);
+ } else {
+ rtbad(rt);
+ }
+}
+
+
+/* Called while walking the table looking for sick interfaces
+ * or after a time change.
+ */
+/* ARGSUSED */
+int
+walk_bad(struct radix_node *rn,
+ struct walkarg *argp UNUSED)
+{
+#define RT ((struct rt_entry *)rn)
+ struct rt_spare *rts;
+ int i;
+
+
+ /* fix any spare routes through the interface
+ */
+ rts = RT->rt_spares;
+ for (i = NUM_SPARES; i != 1; i--) {
+ rts++;
+ if (rts->rts_metric < HOPCNT_INFINITY
+ && (rts->rts_ifp == 0
+ || (rts->rts_ifp->int_state & IS_BROKE)))
+ rts_delete(RT, rts);
+ }
+
+ /* Deal with the main route
+ */
+ /* finished if it has been handled before or if its interface is ok
+ */
+ if (RT->rt_ifp == 0 || !(RT->rt_ifp->int_state & IS_BROKE))
+ return 0;
+
+ /* Bad routes for other than interfaces are easy.
+ */
+ if (0 == (RT->rt_state & (RS_IF | RS_NET_SYN | RS_LOCAL))) {
+ rtbad(RT);
+ return 0;
+ }
+
+ rtbad_sub(RT);
+ return 0;
+#undef RT
+}
+
+
+/* Check the age of an individual route.
+ */
+/* ARGSUSED */
+static int
+walk_age(struct radix_node *rn,
+ struct walkarg *argp UNUSED)
+{
+#define RT ((struct rt_entry *)rn)
+ struct interface *ifp;
+ struct rt_spare *rts;
+ int i;
+
+
+ /* age all of the spare routes, including the primary route
+ * currently in use
+ */
+ rts = RT->rt_spares;
+ for (i = NUM_SPARES; i != 0; i--, rts++) {
+
+ ifp = rts->rts_ifp;
+ if (i == NUM_SPARES) {
+ if (!AGE_RT(RT->rt_state, ifp)) {
+ /* Keep various things from deciding ageless
+ * routes are stale
+ */
+ rts->rts_time = now.tv_sec;
+ continue;
+ }
+
+ /* forget RIP routes after RIP has been turned off.
+ */
+ if (rip_sock < 0) {
+ rtdelete(RT);
+ return 0;
+ }
+ }
+
+ /* age failing routes
+ */
+ if (age_bad_gate == rts->rts_gate
+ && rts->rts_time >= now_stale) {
+ rts->rts_time -= SUPPLY_INTERVAL;
+ }
+
+ /* trash the spare routes when they go bad */
+ if (rts->rts_metric < HOPCNT_INFINITY
+ && now_garbage > rts->rts_time
+ && i != NUM_SPARES)
+ rts_delete(RT, rts);
+ }
+
+
+ /* finished if the active route is still fresh */
+ if (now_stale <= RT->rt_time)
+ return 0;
+
+ /* try to switch to an alternative */
+ rtswitch(RT, 0);
+
+ /* Delete a dead route after it has been publically mourned. */
+ if (now_garbage > RT->rt_time) {
+ rtdelete(RT);
+ return 0;
+ }
+
+ /* Start poisoning a bad route before deleting it. */
+ if (now.tv_sec - RT->rt_time > EXPIRE_TIME) {
+ struct rt_spare new = RT->rt_spares[0];
+ new.rts_metric = HOPCNT_INFINITY;
+ rtchange(RT, RT->rt_state, &new, 0);
+ }
+ return 0;
+}
+
+
+/* Watch for dead routes and interfaces.
+ */
+void
+age(naddr bad_gate)
+{
+ struct interface *ifp;
+ int need_query = 0;
+
+ /* If not listening to RIP, there is no need to age the routes in
+ * the table.
+ */
+ age_timer.tv_sec = (now.tv_sec
+ + ((rip_sock < 0) ? NEVER : SUPPLY_INTERVAL));
+
+ /* Check for dead IS_REMOTE interfaces by timing their
+ * transmissions.
+ */
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (!(ifp->int_state & IS_REMOTE))
+ continue;
+
+ /* ignore unreachable remote interfaces */
+ if (!check_remote(ifp))
+ continue;
+
+ /* Restore remote interface that has become reachable
+ */
+ if (ifp->int_state & IS_BROKE)
+ if_ok(ifp, "remote ");
+
+ if (ifp->int_act_time != NEVER
+ && now.tv_sec - ifp->int_act_time > EXPIRE_TIME) {
+ msglog("remote interface %s to %s timed out after"
+ " %ld:%ld",
+ ifp->int_name,
+ naddr_ntoa(ifp->int_dstaddr),
+ (now.tv_sec - ifp->int_act_time)/60,
+ (now.tv_sec - ifp->int_act_time)%60);
+ if_sick(ifp);
+ }
+
+ /* If we have not heard from the other router
+ * recently, ask it.
+ */
+ if (now.tv_sec >= ifp->int_query_time) {
+ ifp->int_query_time = NEVER;
+ need_query = 1;
+ }
+ }
+
+ /* Age routes. */
+ age_bad_gate = bad_gate;
+ (void)rn_walktree(rhead, walk_age, 0);
+
+ /* delete old redirected routes to keep the kernel table small
+ * and prevent blackholes
+ */
+ del_redirects(bad_gate, now.tv_sec-STALE_TIME);
+
+ /* Update the kernel routing table. */
+ fix_kern();
+
+ /* poke reticent remote gateways */
+ if (need_query)
+ rip_query();
+}
diff --git a/sbin/routed/trace.c b/sbin/routed/trace.c
new file mode 100644
index 0000000..320056f
--- /dev/null
+++ b/sbin/routed/trace.c
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (c) 1983, 1988, 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.
+ * 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#define RIPCMDS
+#include "defs.h"
+#include "pathnames.h"
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <fcntl.h>
+
+#ifdef __NetBSD__
+__RCSID("$NetBSD$");
+#elif defined(__FreeBSD__)
+__RCSID("$FreeBSD$");
+#else
+__RCSID("$Revision: 2.27 $");
+#ident "$Revision: 2.27 $"
+#endif
+
+
+#ifdef sgi
+/* use *stat64 for files on large file systems */
+#define stat stat64
+#endif
+
+#define NRECORDS 50 /* size of circular trace buffer */
+
+int tracelevel, new_tracelevel;
+FILE *ftrace; /* output trace file */
+static const char *sigtrace_pat = "%s";
+static char savetracename[PATH_MAX];
+char inittracename[PATH_MAX];
+int file_trace; /* 1=tracing to file, not stdout */
+
+static void trace_dump(void);
+static void tmsg(const char *, ...) PATTRIB(1,2);
+
+
+/* convert string to printable characters
+ */
+static char *
+qstring(u_char *s, int len)
+{
+ static char buf[8*20+1];
+ char *p;
+ u_char *s2, c;
+
+
+ for (p = buf; len != 0 && p < &buf[sizeof(buf)-1]; len--) {
+ c = *s++;
+ if (c == '\0') {
+ for (s2 = s+1; s2 < &s[len]; s2++) {
+ if (*s2 != '\0')
+ break;
+ }
+ if (s2 >= &s[len])
+ goto exit;
+ }
+
+ if (c >= ' ' && c < 0x7f && c != '\\') {
+ *p++ = c;
+ continue;
+ }
+ *p++ = '\\';
+ switch (c) {
+ case '\\':
+ *p++ = '\\';
+ break;
+ case '\n':
+ *p++= 'n';
+ break;
+ case '\r':
+ *p++= 'r';
+ break;
+ case '\t':
+ *p++ = 't';
+ break;
+ case '\b':
+ *p++ = 'b';
+ break;
+ default:
+ p += sprintf(p,"%o",c);
+ break;
+ }
+ }
+exit:
+ *p = '\0';
+ return buf;
+}
+
+
+/* convert IP address to a string, but not into a single buffer
+ */
+char *
+naddr_ntoa(naddr a)
+{
+#define NUM_BUFS 4
+ static int bufno;
+ static struct {
+ char str[16]; /* xxx.xxx.xxx.xxx\0 */
+ } bufs[NUM_BUFS];
+ char *s;
+ struct in_addr addr;
+
+ addr.s_addr = a;
+ s = strcpy(bufs[bufno].str, inet_ntoa(addr));
+ bufno = (bufno+1) % NUM_BUFS;
+ return s;
+#undef NUM_BUFS
+}
+
+
+const char *
+saddr_ntoa(struct sockaddr *sa)
+{
+ return (sa == 0) ? "?" : naddr_ntoa(S_ADDR(sa));
+}
+
+
+static char *
+ts(time_t secs) {
+ static char s[20];
+
+ secs += epoch.tv_sec;
+#ifdef sgi
+ (void)cftime(s, "%T", &secs);
+#else
+ memcpy(s, ctime(&secs)+11, 8);
+ s[8] = '\0';
+#endif
+ return s;
+}
+
+
+/* On each event, display a time stamp.
+ * This assumes that 'now' is update once for each event, and
+ * that at least now.tv_usec changes.
+ */
+static struct timeval lastlog_time;
+
+void
+lastlog(void)
+{
+ if (lastlog_time.tv_sec != now.tv_sec
+ || lastlog_time.tv_usec != now.tv_usec) {
+ (void)fprintf(ftrace, "-- %s --\n", ts(now.tv_sec));
+ lastlog_time = now;
+ }
+}
+
+
+static void
+tmsg(const char *p, ...)
+{
+ va_list args;
+
+ if (ftrace != 0) {
+ lastlog();
+ va_start(args, p);
+ vfprintf(ftrace, p, args);
+ va_end(args);
+ (void)fputc('\n',ftrace);
+ fflush(ftrace);
+ }
+}
+
+
+void
+trace_close(int zap_stdio)
+{
+ int fd;
+
+
+ fflush(stdout);
+ fflush(stderr);
+
+ if (ftrace != 0 && zap_stdio) {
+ if (ftrace != stdout)
+ fclose(ftrace);
+ ftrace = 0;
+ fd = open(_PATH_DEVNULL, O_RDWR);
+ if (isatty(STDIN_FILENO))
+ (void)dup2(fd, STDIN_FILENO);
+ if (isatty(STDOUT_FILENO))
+ (void)dup2(fd, STDOUT_FILENO);
+ if (isatty(STDERR_FILENO))
+ (void)dup2(fd, STDERR_FILENO);
+ (void)close(fd);
+ }
+ lastlog_time.tv_sec = 0;
+}
+
+
+void
+trace_flush(void)
+{
+ if (ftrace != 0) {
+ fflush(ftrace);
+ if (ferror(ftrace))
+ trace_off("tracing off: %s", strerror(ferror(ftrace)));
+ }
+}
+
+
+void
+trace_off(const char *p, ...)
+{
+ va_list args;
+
+
+ if (ftrace != 0) {
+ lastlog();
+ va_start(args, p);
+ vfprintf(ftrace, p, args);
+ va_end(args);
+ (void)fputc('\n',ftrace);
+ }
+ trace_close(file_trace);
+
+ new_tracelevel = tracelevel = 0;
+}
+
+
+/* log a change in tracing
+ */
+void
+tracelevel_msg(const char *pat,
+ int dump) /* -1=no dump, 0=default, 1=force */
+{
+ static const char *off_msgs[MAX_TRACELEVEL] = {
+ "Tracing actions stopped",
+ "Tracing packets stopped",
+ "Tracing packet contents stopped",
+ "Tracing kernel changes stopped",
+ };
+ static const char *on_msgs[MAX_TRACELEVEL] = {
+ "Tracing actions started",
+ "Tracing packets started",
+ "Tracing packet contents started",
+ "Tracing kernel changes started",
+ };
+ u_int old_tracelevel = tracelevel;
+
+
+ if (new_tracelevel < 0)
+ new_tracelevel = 0;
+ else if (new_tracelevel > MAX_TRACELEVEL)
+ new_tracelevel = MAX_TRACELEVEL;
+
+ if (new_tracelevel < tracelevel) {
+ if (new_tracelevel <= 0) {
+ trace_off(pat, off_msgs[0]);
+ } else do {
+ tmsg(pat, off_msgs[tracelevel]);
+ }
+ while (--tracelevel != new_tracelevel);
+
+ } else if (new_tracelevel > tracelevel) {
+ do {
+ tmsg(pat, on_msgs[tracelevel++]);
+ } while (tracelevel != new_tracelevel);
+ }
+
+ if (dump > 0
+ || (dump == 0 && old_tracelevel == 0 && tracelevel != 0))
+ trace_dump();
+}
+
+
+void
+set_tracefile(const char *filename,
+ const char *pat,
+ int dump) /* -1=no dump, 0=default, 1=force */
+{
+ struct stat stbuf;
+ FILE *n_ftrace;
+ const char *fn;
+
+
+ /* Allow a null filename to increase the level if the trace file
+ * is already open or if coming from a trusted source, such as
+ * a signal or the command line.
+ */
+ if (filename == 0 || filename[0] == '\0') {
+ filename = 0;
+ if (ftrace == 0) {
+ if (inittracename[0] == '\0') {
+ msglog("missing trace file name");
+ return;
+ }
+ fn = inittracename;
+ } else {
+ fn = 0;
+ }
+
+ } else if (!strcmp(filename,"dump/../table")) {
+ trace_dump();
+ return;
+
+ } else {
+ /* Allow the file specified with "-T file" to be reopened,
+ * but require all other names specified over the net to
+ * match the official path. The path can specify a directory
+ * in which the file is to be created.
+ */
+ if (strcmp(filename, inittracename)
+#ifdef _PATH_TRACE
+ && (strncmp(filename, _PATH_TRACE, sizeof(_PATH_TRACE)-1)
+ || strstr(filename,"../")
+ || 0 > stat(_PATH_TRACE, &stbuf))
+#endif
+ ) {
+ msglog("wrong trace file \"%s\"", filename);
+ return;
+ }
+
+ /* If the new tracefile exists, it must be a regular file.
+ */
+ if (stat(filename, &stbuf) >= 0 && !S_ISREG(stbuf.st_mode)) {
+ msglog("wrong type (%#x) of trace file \"%s\"",
+ stbuf.st_mode, filename);
+ return;
+ }
+
+ fn = filename;
+ }
+
+ if (fn != 0) {
+ n_ftrace = fopen(fn, "a");
+ if (n_ftrace == 0) {
+ msglog("failed to open trace file \"%s\" %s",
+ fn, strerror(errno));
+ if (fn == inittracename)
+ inittracename[0] = '\0';
+ return;
+ }
+
+ tmsg("switch to trace file %s", fn);
+
+ trace_close(file_trace = 1);
+
+ if (fn != savetracename)
+ strncpy(savetracename, fn, sizeof(savetracename)-1);
+ ftrace = n_ftrace;
+
+ fflush(stdout);
+ fflush(stderr);
+ dup2(fileno(ftrace), STDOUT_FILENO);
+ dup2(fileno(ftrace), STDERR_FILENO);
+ }
+
+ if (new_tracelevel == 0 || filename == 0)
+ new_tracelevel++;
+ tracelevel_msg(pat, dump != 0 ? dump : (filename != 0));
+}
+
+
+/* ARGSUSED */
+void
+sigtrace_on(int s UNUSED)
+{
+ new_tracelevel++;
+ sigtrace_pat = "SIGUSR1: %s";
+}
+
+
+/* ARGSUSED */
+void
+sigtrace_off(int s UNUSED)
+{
+ new_tracelevel--;
+ sigtrace_pat = "SIGUSR2: %s";
+}
+
+
+/* Set tracing after a signal.
+ */
+void
+set_tracelevel(void)
+{
+ if (new_tracelevel == tracelevel)
+ return;
+
+ /* If tracing entirely off, and there was no tracefile specified
+ * on the command line, then leave it off.
+ */
+ if (new_tracelevel > tracelevel && ftrace == 0) {
+ if (savetracename[0] != '\0') {
+ set_tracefile(savetracename,sigtrace_pat,0);
+ } else if (inittracename[0] != '\0') {
+ set_tracefile(inittracename,sigtrace_pat,0);
+ } else {
+ new_tracelevel = 0;
+ return;
+ }
+ } else {
+ tracelevel_msg(sigtrace_pat, 0);
+ }
+}
+
+
+/* display an address
+ */
+char *
+addrname(naddr addr, /* in network byte order */
+ naddr mask,
+ int force) /* 0=show mask if nonstandard, */
+{ /* 1=always show mask, 2=never */
+#define NUM_BUFS 4
+ static int bufno;
+ static struct {
+ char str[15+20];
+ } bufs[NUM_BUFS];
+ char *s, *sp;
+ naddr dmask;
+ int i;
+
+ s = strcpy(bufs[bufno].str, naddr_ntoa(addr));
+ bufno = (bufno+1) % NUM_BUFS;
+
+ if (force == 1 || (force == 0 && mask != std_mask(addr))) {
+ sp = &s[strlen(s)];
+
+ dmask = mask & -mask;
+ if (mask + dmask == 0) {
+ for (i = 0; i != 32 && ((1<<i) & mask) == 0; i++)
+ continue;
+ (void)sprintf(sp, "/%d", 32-i);
+
+ } else {
+ (void)sprintf(sp, " (mask %#x)", (u_int)mask);
+ }
+ }
+
+ return s;
+#undef NUM_BUFS
+}
+
+
+/* display a bit-field
+ */
+struct bits {
+ u_int bits_mask;
+ u_int bits_clear;
+ const char *bits_name;
+};
+
+static struct bits if_bits[] = {
+ { IFF_LOOPBACK, 0, "LOOPBACK" },
+ { IFF_POINTOPOINT, 0, "PT-TO-PT" },
+ { 0, 0, 0}
+};
+
+static struct bits is_bits[] = {
+ { IS_ALIAS, 0, "ALIAS" },
+ { IS_SUBNET, 0, "" },
+ { IS_REMOTE, (IS_NO_RDISC
+ | IS_BCAST_RDISC), "REMOTE" },
+ { IS_PASSIVE, (IS_NO_RDISC
+ | IS_NO_RIP
+ | IS_NO_SUPER_AG
+ | IS_PM_RDISC
+ | IS_NO_AG), "PASSIVE" },
+ { IS_EXTERNAL, 0, "EXTERNAL" },
+ { IS_CHECKED, 0, "" },
+ { IS_ALL_HOSTS, 0, "" },
+ { IS_ALL_ROUTERS, 0, "" },
+ { IS_DISTRUST, 0, "DISTRUST" },
+ { IS_BROKE, IS_SICK, "BROKEN" },
+ { IS_SICK, 0, "SICK" },
+ { IS_DUP, 0, "DUPLICATE" },
+ { IS_REDIRECT_OK, 0, "REDIRECT_OK" },
+ { IS_NEED_NET_SYN, 0, "" },
+ { IS_NO_AG, IS_NO_SUPER_AG, "NO_AG" },
+ { IS_NO_SUPER_AG, 0, "NO_SUPER_AG" },
+ { (IS_NO_RIPV1_IN
+ | IS_NO_RIPV2_IN
+ | IS_NO_RIPV1_OUT
+ | IS_NO_RIPV2_OUT), 0, "NO_RIP" },
+ { (IS_NO_RIPV1_IN
+ | IS_NO_RIPV1_OUT), 0, "RIPV2" },
+ { IS_NO_RIPV1_IN, 0, "NO_RIPV1_IN" },
+ { IS_NO_RIPV2_IN, 0, "NO_RIPV2_IN" },
+ { IS_NO_RIPV1_OUT, 0, "NO_RIPV1_OUT" },
+ { IS_NO_RIPV2_OUT, 0, "NO_RIPV2_OUT" },
+ { (IS_NO_ADV_IN
+ | IS_NO_SOL_OUT
+ | IS_NO_ADV_OUT), IS_BCAST_RDISC, "NO_RDISC" },
+ { IS_NO_SOL_OUT, 0, "NO_SOLICIT" },
+ { IS_SOL_OUT, 0, "SEND_SOLICIT" },
+ { IS_NO_ADV_OUT, IS_BCAST_RDISC, "NO_RDISC_ADV" },
+ { IS_ADV_OUT, 0, "RDISC_ADV" },
+ { IS_BCAST_RDISC, 0, "BCAST_RDISC" },
+ { IS_PM_RDISC, 0, "" },
+ { 0, 0, "%#x"}
+};
+
+static struct bits rs_bits[] = {
+ { RS_IF, 0, "IF" },
+ { RS_NET_INT, RS_NET_SYN, "NET_INT" },
+ { RS_NET_SYN, 0, "NET_SYN" },
+ { RS_SUBNET, 0, "" },
+ { RS_LOCAL, 0, "LOCAL" },
+ { RS_MHOME, 0, "MHOME" },
+ { RS_STATIC, 0, "STATIC" },
+ { RS_RDISC, 0, "RDISC" },
+ { 0, 0, "%#x"}
+};
+
+
+static void
+trace_bits(const struct bits *tbl,
+ u_int field,
+ int force)
+{
+ u_int b;
+ char c;
+
+ if (force) {
+ (void)putc('<', ftrace);
+ c = 0;
+ } else {
+ c = '<';
+ }
+
+ while (field != 0
+ && (b = tbl->bits_mask) != 0) {
+ if ((b & field) == b) {
+ if (tbl->bits_name[0] != '\0') {
+ if (c)
+ (void)putc(c, ftrace);
+ (void)fprintf(ftrace, "%s", tbl->bits_name);
+ c = '|';
+ }
+ if (0 == (field &= ~(b | tbl->bits_clear)))
+ break;
+ }
+ tbl++;
+ }
+ if (field != 0 && tbl->bits_name != 0) {
+ if (c)
+ (void)putc(c, ftrace);
+ (void)fprintf(ftrace, tbl->bits_name, field);
+ c = '|';
+ }
+
+ if (c != '<' || force)
+ (void)fputs("> ", ftrace);
+}
+
+
+char *
+rtname(naddr dst,
+ naddr mask,
+ naddr gate)
+{
+ static char buf[3*4+3+1+2+3 /* "xxx.xxx.xxx.xxx/xx-->" */
+ +3*4+3+1]; /* "xxx.xxx.xxx.xxx" */
+ int i;
+
+ i = sprintf(buf, "%-16s-->", addrname(dst, mask, 0));
+ (void)sprintf(&buf[i], "%-*s", 15+20-MAX(20,i), naddr_ntoa(gate));
+ return buf;
+}
+
+
+static void
+print_rts(struct rt_spare *rts,
+ int force_metric, /* -1=suppress, 0=default */
+ int force_ifp, /* -1=suppress, 0=default */
+ int force_router, /* -1=suppress, 0=default, 1=display */
+ int force_tag, /* -1=suppress, 0=default, 1=display */
+ int force_time) /* 0=suppress, 1=display */
+{
+ int i;
+
+
+ if (force_metric >= 0)
+ (void)fprintf(ftrace, "metric=%-2d ", rts->rts_metric);
+ if (force_ifp >= 0)
+ (void)fprintf(ftrace, "%s ", (rts->rts_ifp == 0 ?
+ "if?" : rts->rts_ifp->int_name));
+ if (force_router > 0
+ || (force_router == 0 && rts->rts_router != rts->rts_gate))
+ (void)fprintf(ftrace, "router=%s ",
+ naddr_ntoa(rts->rts_router));
+ if (force_time > 0)
+ (void)fprintf(ftrace, "%s ", ts(rts->rts_time));
+ if (force_tag > 0
+ || (force_tag == 0 && rts->rts_tag != 0))
+ (void)fprintf(ftrace, "tag=%#x ", ntohs(rts->rts_tag));
+ if (rts->rts_de_ag != 0) {
+ for (i = 1; (u_int)(1 << i) <= rts->rts_de_ag; i++)
+ continue;
+ (void)fprintf(ftrace, "de_ag=%d ", i);
+ }
+
+}
+
+
+void
+trace_if(const char *act,
+ struct interface *ifp)
+{
+ if (!TRACEACTIONS || ftrace == 0)
+ return;
+
+ lastlog();
+ (void)fprintf(ftrace, "%-3s interface %-4s ", act, ifp->int_name);
+ (void)fprintf(ftrace, "%-15s-->%-15s ",
+ naddr_ntoa(ifp->int_addr),
+ addrname(((ifp->int_if_flags & IFF_POINTOPOINT)
+ ? ifp->int_dstaddr
+ : htonl(ifp->int_net)),
+ ifp->int_mask, 1));
+ if (ifp->int_metric != 0)
+ (void)fprintf(ftrace, "metric=%d ", ifp->int_metric);
+ if (ifp->int_adj_inmetric != 0)
+ (void)fprintf(ftrace, "adj_inmetric=%u ",
+ ifp->int_adj_inmetric);
+ if (ifp->int_adj_outmetric != 0)
+ (void)fprintf(ftrace, "adj_outmetric=%u ",
+ ifp->int_adj_outmetric);
+ if (!IS_RIP_OUT_OFF(ifp->int_state)
+ && ifp->int_d_metric != 0)
+ (void)fprintf(ftrace, "fake_default=%u ", ifp->int_d_metric);
+ trace_bits(if_bits, ifp->int_if_flags, 0);
+ trace_bits(is_bits, ifp->int_state, 0);
+ (void)fputc('\n',ftrace);
+}
+
+
+void
+trace_upslot(struct rt_entry *rt,
+ struct rt_spare *rts,
+ struct rt_spare *new)
+{
+ if (!TRACEACTIONS || ftrace == 0)
+ return;
+
+ if (rts->rts_gate == new->rts_gate
+ && rts->rts_router == new->rts_router
+ && rts->rts_metric == new->rts_metric
+ && rts->rts_tag == new->rts_tag
+ && rts->rts_de_ag == new->rts_de_ag)
+ return;
+
+ lastlog();
+ if (new->rts_gate == 0) {
+ (void)fprintf(ftrace, "Del #%d %-35s ",
+ (int)(rts - rt->rt_spares),
+ rtname(rt->rt_dst, rt->rt_mask, rts->rts_gate));
+ print_rts(rts, 0,0,0,0,
+ (rts != rt->rt_spares
+ || AGE_RT(rt->rt_state,new->rts_ifp)));
+
+ } else if (rts->rts_gate != RIP_DEFAULT) {
+ (void)fprintf(ftrace, "Chg #%d %-35s ",
+ (int)(rts - rt->rt_spares),
+ rtname(rt->rt_dst, rt->rt_mask, rts->rts_gate));
+ print_rts(rts, 0,0,
+ rts->rts_gate != new->rts_gate,
+ rts->rts_tag != new->rts_tag,
+ rts != rt->rt_spares || AGE_RT(rt->rt_state,
+ rt->rt_ifp));
+
+ (void)fprintf(ftrace, "\n %19s%-16s ", "",
+ (new->rts_gate != rts->rts_gate
+ ? naddr_ntoa(new->rts_gate) : ""));
+ print_rts(new,
+ -(new->rts_metric == rts->rts_metric),
+ -(new->rts_ifp == rts->rts_ifp),
+ 0,
+ rts->rts_tag != new->rts_tag,
+ (new->rts_time != rts->rts_time
+ && (rts != rt->rt_spares
+ || AGE_RT(rt->rt_state, new->rts_ifp))));
+
+ } else {
+ (void)fprintf(ftrace, "Add #%d %-35s ",
+ (int)(rts - rt->rt_spares),
+ rtname(rt->rt_dst, rt->rt_mask, new->rts_gate));
+ print_rts(new, 0,0,0,0,
+ (rts != rt->rt_spares
+ || AGE_RT(rt->rt_state,new->rts_ifp)));
+ }
+ (void)fputc('\n',ftrace);
+}
+
+
+/* miscellaneous message checked by the caller
+ */
+void
+trace_misc(const char *p, ...)
+{
+ va_list args;
+
+ if (ftrace == 0)
+ return;
+
+ lastlog();
+ va_start(args, p);
+ vfprintf(ftrace, p, args);
+ va_end(args);
+ (void)fputc('\n',ftrace);
+}
+
+
+/* display a message if tracing actions
+ */
+void
+trace_act(const char *p, ...)
+{
+ va_list args;
+
+ if (!TRACEACTIONS || ftrace == 0)
+ return;
+
+ lastlog();
+ va_start(args, p);
+ vfprintf(ftrace, p, args);
+ va_end(args);
+ (void)fputc('\n',ftrace);
+}
+
+
+/* display a message if tracing packets
+ */
+void
+trace_pkt(const char *p, ...)
+{
+ va_list args;
+
+ if (!TRACEPACKETS || ftrace == 0)
+ return;
+
+ lastlog();
+ va_start(args, p);
+ vfprintf(ftrace, p, args);
+ va_end(args);
+ (void)fputc('\n',ftrace);
+}
+
+
+void
+trace_change(struct rt_entry *rt,
+ u_int state,
+ struct rt_spare *new,
+ const char *label)
+{
+ if (ftrace == 0)
+ return;
+
+ if (rt->rt_metric == new->rts_metric
+ && rt->rt_gate == new->rts_gate
+ && rt->rt_router == new->rts_router
+ && rt->rt_state == state
+ && rt->rt_tag == new->rts_tag
+ && rt->rt_de_ag == new->rts_de_ag)
+ return;
+
+ lastlog();
+ (void)fprintf(ftrace, "%s %-35s ",
+ label,
+ rtname(rt->rt_dst, rt->rt_mask, rt->rt_gate));
+ print_rts(rt->rt_spares,
+ 0,0,0,0, AGE_RT(rt->rt_state, rt->rt_ifp));
+ trace_bits(rs_bits, rt->rt_state, rt->rt_state != state);
+
+ (void)fprintf(ftrace, "\n%*s %19s%-16s ",
+ (int)strlen(label), "", "",
+ (rt->rt_gate != new->rts_gate
+ ? naddr_ntoa(new->rts_gate) : ""));
+ print_rts(new,
+ -(new->rts_metric == rt->rt_metric),
+ -(new->rts_ifp == rt->rt_ifp),
+ 0,
+ rt->rt_tag != new->rts_tag,
+ (rt->rt_time != new->rts_time
+ && AGE_RT(rt->rt_state,new->rts_ifp)));
+ if (rt->rt_state != state)
+ trace_bits(rs_bits, state, 1);
+ (void)fputc('\n',ftrace);
+}
+
+
+void
+trace_add_del(const char * action, struct rt_entry *rt)
+{
+ if (ftrace == 0)
+ return;
+
+ lastlog();
+ (void)fprintf(ftrace, "%s %-35s ",
+ action,
+ rtname(rt->rt_dst, rt->rt_mask, rt->rt_gate));
+ print_rts(rt->rt_spares, 0,0,0,0,AGE_RT(rt->rt_state,rt->rt_ifp));
+ trace_bits(rs_bits, rt->rt_state, 0);
+ (void)fputc('\n',ftrace);
+}
+
+
+/* ARGSUSED */
+static int
+walk_trace(struct radix_node *rn,
+ struct walkarg *w UNUSED)
+{
+#define RT ((struct rt_entry *)rn)
+ struct rt_spare *rts;
+ int i;
+
+ (void)fprintf(ftrace, " %-35s ",
+ rtname(RT->rt_dst, RT->rt_mask, RT->rt_gate));
+ print_rts(&RT->rt_spares[0], 0,0,0,0, AGE_RT(RT->rt_state, RT->rt_ifp));
+ trace_bits(rs_bits, RT->rt_state, 0);
+ if (RT->rt_poison_time >= now_garbage
+ && RT->rt_poison_metric < RT->rt_metric)
+ (void)fprintf(ftrace, "pm=%d@%s",
+ RT->rt_poison_metric, ts(RT->rt_poison_time));
+
+ rts = &RT->rt_spares[1];
+ for (i = 1; i < NUM_SPARES; i++, rts++) {
+ if (rts->rts_gate != RIP_DEFAULT) {
+ (void)fprintf(ftrace,"\n #%d%15s%-16s ",
+ i, "", naddr_ntoa(rts->rts_gate));
+ print_rts(rts, 0,0,0,0,1);
+ }
+ }
+ (void)fputc('\n',ftrace);
+
+ return 0;
+}
+
+
+static void
+trace_dump(void)
+{
+ struct interface *ifp;
+
+ if (ftrace == 0)
+ return;
+ lastlog();
+
+ (void)fputs("current daemon state:\n", ftrace);
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next)
+ trace_if("", ifp);
+ (void)rn_walktree(rhead, walk_trace, 0);
+}
+
+
+void
+trace_rip(const char *dir1, const char *dir2,
+ struct sockaddr_in *who,
+ struct interface *ifp,
+ struct rip *msg,
+ int size) /* total size of message */
+{
+ struct netinfo *n, *lim;
+# define NA ((struct netauth*)n)
+ int i, seen_route;
+
+ if (!TRACEPACKETS || ftrace == 0)
+ return;
+
+ lastlog();
+ if (msg->rip_cmd >= RIPCMD_MAX
+ || msg->rip_vers == 0) {
+ (void)fprintf(ftrace, "%s bad RIPv%d cmd=%d %s"
+ " %s.%d size=%d\n",
+ dir1, msg->rip_vers, msg->rip_cmd, dir2,
+ naddr_ntoa(who->sin_addr.s_addr),
+ ntohs(who->sin_port),
+ size);
+ return;
+ }
+
+ (void)fprintf(ftrace, "%s RIPv%d %s %s %s.%d%s%s\n",
+ dir1, msg->rip_vers, ripcmds[msg->rip_cmd], dir2,
+ naddr_ntoa(who->sin_addr.s_addr), ntohs(who->sin_port),
+ ifp ? " via " : "", ifp ? ifp->int_name : "");
+ if (!TRACECONTENTS)
+ return;
+
+ seen_route = 0;
+ switch (msg->rip_cmd) {
+ case RIPCMD_REQUEST:
+ case RIPCMD_RESPONSE:
+ n = msg->rip_nets;
+ lim = (struct netinfo *)((char*)msg + size);
+ for (; n < lim; n++) {
+ if (!seen_route
+ && n->n_family == RIP_AF_UNSPEC
+ && ntohl(n->n_metric) == HOPCNT_INFINITY
+ && msg->rip_cmd == RIPCMD_REQUEST
+ && (n+1 == lim
+ || (n+2 == lim
+ && (n+1)->n_family == RIP_AF_AUTH))) {
+ (void)fputs("\tQUERY ", ftrace);
+ if (n->n_dst != 0)
+ (void)fprintf(ftrace, "%s ",
+ naddr_ntoa(n->n_dst));
+ if (n->n_mask != 0)
+ (void)fprintf(ftrace, "mask=%#x ",
+ (u_int)ntohl(n->n_mask));
+ if (n->n_nhop != 0)
+ (void)fprintf(ftrace, "nhop=%s ",
+ naddr_ntoa(n->n_nhop));
+ if (n->n_tag != 0)
+ (void)fprintf(ftrace, "tag=%#x ",
+ ntohs(n->n_tag));
+ (void)fputc('\n',ftrace);
+ continue;
+ }
+
+ if (n->n_family == RIP_AF_AUTH) {
+ if (NA->a_type == RIP_AUTH_PW
+ && n == msg->rip_nets) {
+ (void)fprintf(ftrace, "\tPassword"
+ " Authentication:"
+ " \"%s\"\n",
+ qstring(NA->au.au_pw,
+ RIP_AUTH_PW_LEN));
+ continue;
+ }
+
+ if (NA->a_type == RIP_AUTH_MD5
+ && n == msg->rip_nets) {
+ (void)fprintf(ftrace,
+ "\tMD5 Auth"
+ " pkt_len=%d KeyID=%u"
+ " auth_len=%d"
+ " seqno=%#x"
+ " rsvd=%#x,%#x\n",
+ ntohs(NA->au.a_md5.md5_pkt_len),
+ NA->au.a_md5.md5_keyid,
+ NA->au.a_md5.md5_auth_len,
+ (int)ntohl(NA->au.a_md5.md5_seqno),
+ (int)ntohs(NA->au.a_md5.rsvd[0]),
+ (int)ntohs(NA->au.a_md5.rsvd[1]));
+ continue;
+ }
+ (void)fprintf(ftrace,
+ "\tAuthentication type %d: ",
+ ntohs(NA->a_type));
+ for (i = 0;
+ i < (int)sizeof(NA->au.au_pw);
+ i++)
+ (void)fprintf(ftrace, "%02x ",
+ NA->au.au_pw[i]);
+ (void)fputc('\n',ftrace);
+ continue;
+ }
+
+ seen_route = 1;
+ if (n->n_family != RIP_AF_INET) {
+ (void)fprintf(ftrace,
+ "\t(af %d) %-18s mask=%#x ",
+ ntohs(n->n_family),
+ naddr_ntoa(n->n_dst),
+ (u_int)ntohl(n->n_mask));
+ } else if (msg->rip_vers == RIPv1) {
+ (void)fprintf(ftrace, "\t%-18s ",
+ addrname(n->n_dst,
+ ntohl(n->n_mask),
+ n->n_mask==0 ? 2 : 1));
+ } else {
+ (void)fprintf(ftrace, "\t%-18s ",
+ addrname(n->n_dst,
+ ntohl(n->n_mask),
+ n->n_mask==0 ? 2 : 0));
+ }
+ (void)fprintf(ftrace, "metric=%-2d ",
+ (u_int)ntohl(n->n_metric));
+ if (n->n_nhop != 0)
+ (void)fprintf(ftrace, " nhop=%s ",
+ naddr_ntoa(n->n_nhop));
+ if (n->n_tag != 0)
+ (void)fprintf(ftrace, "tag=%#x",
+ ntohs(n->n_tag));
+ (void)fputc('\n',ftrace);
+ }
+ if (size != (char *)n - (char *)msg)
+ (void)fprintf(ftrace, "truncated record, len %d\n",
+ size);
+ break;
+
+ case RIPCMD_TRACEON:
+ fprintf(ftrace, "\tfile=\"%.*s\"\n", size-4,
+ msg->rip_tracefile);
+ break;
+
+ case RIPCMD_TRACEOFF:
+ break;
+ }
+}
diff --git a/sbin/rtsol/Makefile b/sbin/rtsol/Makefile
new file mode 100644
index 0000000..d3d40e8
--- /dev/null
+++ b/sbin/rtsol/Makefile
@@ -0,0 +1,28 @@
+# Copyright (c) 1996 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modifications, are permitted provided that the above copyright notice
+# and this paragraph are duplicated in all such forms and that any
+# documentation, advertising materials, and other materials related to
+# such distribution and use acknowledge that the software was developed
+# by the WIDE Project, Japan. The name of the Project may not 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.
+# $FreeBSD$
+
+SRCDIR= ${.CURDIR}/../../usr.sbin/rtsold
+
+PROG= rtsol
+SRCS= rtsold.c rtsol.c if.c probe.c rtsock.c
+
+CFLAGS+=-DINET6 -DHAVE_ARC4RANDOM -DHAVE_POLL_H -DSMALL
+WARNS?= 0
+
+NO_MAN=
+
+.PATH: ${SRCDIR}
+
+.include <bsd.prog.mk>
diff --git a/sbin/savecore/Makefile b/sbin/savecore/Makefile
new file mode 100644
index 0000000..525f281
--- /dev/null
+++ b/sbin/savecore/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= savecore
+WARNS?= 6
+DPADD= ${LIBZ}
+LDADD= -lz
+MAN= savecore.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/savecore/savecore.8 b/sbin/savecore/savecore.8
new file mode 100644
index 0000000..447e008
--- /dev/null
+++ b/sbin/savecore/savecore.8
@@ -0,0 +1,148 @@
+.\" 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.
+.\" 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 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: @(#)savecore.8 8.1 (Berkeley) 6/5/93
+.\" $FreeBSD$
+.\"
+.Dd February 24, 2005
+.Dt SAVECORE 8
+.Os
+.Sh NAME
+.Nm savecore
+.Nd "save a core dump of the operating system"
+.Sh SYNOPSIS
+.Nm
+.Fl c
+.Nm
+.Fl C
+.Op Fl v
+.Op Ar directory device
+.Nm
+.Op Fl fkvz
+.Op Ar directory Op Ar device ...
+.Sh DESCRIPTION
+The
+.Nm
+utility
+copies a core dump into
+.Ar directory ,
+or the current working directory if no
+.Ar directory
+argument is given,
+and enters a reboot message and information about the core dump into
+the system log.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl C
+Check to see if a dump exists,
+and display a brief message to indicate the status.
+An exit status of 0 indicates that a dump is there,
+1 indicates that none exists.
+This option is compatible only with the
+.Op Fl v
+option.
+.It Fl c
+Clear the dump, so that future invocations of
+.Nm
+will ignore it.
+.It Fl f
+Force a dump to be taken even if either the dump was cleared or if the
+dump header information is inconsistent.
+.It Fl k
+Do not clear the dump after saving it.
+.It Fl v
+Print out some additional debugging information.
+Specify twice for more information.
+.It Fl z
+Compress the core dump and kernel (see
+.Xr gzip 1 ) .
+.El
+.Pp
+The
+.Nm
+utility
+looks for dumps on each device specified by the
+.Ar device
+argument(s), or on each device in
+.Pa /etc/fstab
+marked as
+.Dq dump
+or
+.Dq swap .
+The
+.Nm
+utility
+checks the core dump in various ways to make sure that it is complete.
+If it passes these checks, it saves the core image in
+.Ar directory Ns Pa /vmcore.#
+and information about the core in
+.Ar directory Ns Pa /info.#
+The ``#'' is the number from the first line of the file
+.Ar directory Ns Pa /bounds ,
+and it is incremented and stored back into the file each time
+.Nm
+successfully runs.
+.Pp
+The
+.Nm
+utility
+also checks the available disk space before attempting to make the copies.
+If there is insufficient disk space in the file system containing
+.Ar directory ,
+or if the file
+.Ar directory Ns Pa /minfree
+exists and the number of free kilobytes (for non-superusers) in the
+file system after the copies were made would be less than the number
+in the first line of this file, the copies are not attempted.
+.Pp
+If
+.Nm
+successfully copies the kernel and the core dump, the core dump is cleared
+so that future invocations of
+.Nm
+will ignore it.
+.Pp
+The
+.Nm
+utility
+is meant to be called near the end of the initialization file
+.Pa /etc/rc
+(see
+.Xr rc 8 ) .
+.Sh SEE ALSO
+.Xr gzip 1 ,
+.Xr getbootfile 3 ,
+.Xr dumpon 8 ,
+.Xr syslogd 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.1 .
+.Sh BUGS
+The minfree code does not consider the effect of compression or sparse files.
diff --git a/sbin/savecore/savecore.c b/sbin/savecore/savecore.c
new file mode 100644
index 0000000..0d88b20
--- /dev/null
+++ b/sbin/savecore/savecore.c
@@ -0,0 +1,614 @@
+/*-
+ * Copyright (c) 2002 Poul-Henning Kamp
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Poul-Henning Kamp
+ * and NAI Labs, the Security Research Division of Network Associates, Inc.
+ * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
+ * DARPA CHATS research program.
+ *
+ * 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 names of the authors 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 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.
+ *
+ * Copyright (c) 1986, 1992, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/disk.h>
+#include <sys/kerneldump.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <paths.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+/* The size of the buffer used for I/O. */
+#define BUFFERSIZE (1024*1024)
+
+#define STATUS_BAD 0
+#define STATUS_GOOD 1
+#define STATUS_UNKNOWN 2
+
+static int checkfor, compress, clear, force, keep, verbose; /* flags */
+static int nfound, nsaved, nerr; /* statistics */
+
+extern FILE *zopen(const char *, const char *);
+
+static void
+printheader(FILE *f, const struct kerneldumpheader *h, const char *device,
+ int bounds, const int status)
+{
+ uint64_t dumplen;
+ time_t t;
+ const char *stat_str;
+
+ fprintf(f, "Dump header from device %s\n", device);
+ fprintf(f, " Architecture: %s\n", h->architecture);
+ fprintf(f, " Architecture Version: %u\n",
+ dtoh32(h->architectureversion));
+ dumplen = dtoh64(h->dumplength);
+ fprintf(f, " Dump Length: %lldB (%lld MB)\n", (long long)dumplen,
+ (long long)(dumplen >> 20));
+ fprintf(f, " Blocksize: %d\n", dtoh32(h->blocksize));
+ t = dtoh64(h->dumptime);
+ fprintf(f, " Dumptime: %s", ctime(&t));
+ fprintf(f, " Hostname: %s\n", h->hostname);
+ fprintf(f, " Magic: %s\n", h->magic);
+ fprintf(f, " Version String: %s", h->versionstring);
+ fprintf(f, " Panic String: %s\n", h->panicstring);
+ fprintf(f, " Dump Parity: %u\n", h->parity);
+ fprintf(f, " Bounds: %d\n", bounds);
+
+ switch(status) {
+ case STATUS_BAD:
+ stat_str = "bad";
+ break;
+ case STATUS_GOOD:
+ stat_str = "good";
+ break;
+ default:
+ stat_str = "unknown";
+ }
+ fprintf(f, " Dump Status: %s\n", stat_str);
+ fflush(f);
+}
+
+static int
+getbounds(void) {
+ FILE *fp;
+ char buf[6];
+ int ret;
+
+ ret = 0;
+
+ if ((fp = fopen("bounds", "r")) == NULL) {
+ if (verbose)
+ printf("unable to open bounds file, using 0\n");
+ return (ret);
+ }
+
+ if (fgets(buf, sizeof buf, fp) == NULL) {
+ syslog(LOG_WARNING, "unable to read from bounds, using 0");
+ fclose(fp);
+ return (ret);
+ }
+
+ errno = 0;
+ ret = (int)strtol(buf, NULL, 10);
+ if (ret == 0 && (errno == EINVAL || errno == ERANGE))
+ syslog(LOG_WARNING, "invalid value found in bounds, using 0");
+ return (ret);
+}
+
+static void
+writebounds(int bounds) {
+ FILE *fp;
+
+ if ((fp = fopen("bounds", "w")) == NULL) {
+ syslog(LOG_WARNING, "unable to write to bounds file: %m");
+ return;
+ }
+
+ if (verbose)
+ printf("bounds number: %d\n", bounds);
+
+ fprintf(fp, "%d\n", bounds);
+ fclose(fp);
+}
+
+/*
+ * Check that sufficient space is available on the disk that holds the
+ * save directory.
+ */
+static int
+check_space(const char *savedir, off_t dumpsize)
+{
+ FILE *fp;
+ off_t minfree, spacefree, totfree, needed;
+ struct statfs fsbuf;
+ char buf[100], path[MAXPATHLEN];
+
+ if (statfs(savedir, &fsbuf) < 0) {
+ syslog(LOG_ERR, "%s: %m", savedir);
+ exit(1);
+ }
+ spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
+ totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
+
+ (void)snprintf(path, sizeof(path), "%s/minfree", savedir);
+ if ((fp = fopen(path, "r")) == NULL)
+ minfree = 0;
+ else {
+ if (fgets(buf, sizeof(buf), fp) == NULL)
+ minfree = 0;
+ else
+ minfree = atoi(buf);
+ (void)fclose(fp);
+ }
+
+ needed = dumpsize / 1024 + 2; /* 2 for info file */
+ if (((minfree > 0) ? spacefree : totfree) - needed < minfree) {
+ syslog(LOG_WARNING,
+ "no dump, not enough free space on device (%lld available, need %lld)",
+ (long long)(minfree > 0 ? spacefree : totfree),
+ (long long)needed);
+ return (0);
+ }
+ if (spacefree - needed < 0)
+ syslog(LOG_WARNING,
+ "dump performed, but free space threshold crossed");
+ return (1);
+}
+
+#define BLOCKSIZE (1<<12)
+#define BLOCKMASK (~(BLOCKSIZE-1))
+
+static void
+DoFile(const char *savedir, const char *device)
+{
+ static char *buf = NULL;
+ struct kerneldumpheader kdhf, kdhl;
+ off_t mediasize, dumpsize, firsthd, lasthd, dmpcnt;
+ FILE *info, *fp;
+ mode_t oumask;
+ int fd, fdinfo, error, wl;
+ int nr, nw, hs, he = 0;
+ int bounds, status;
+ u_int sectorsize;
+
+ bounds = getbounds();
+ dmpcnt = 0;
+ mediasize = 0;
+ status = STATUS_UNKNOWN;
+
+ if (buf == NULL) {
+ buf = malloc(BUFFERSIZE);
+ if (buf == NULL) {
+ syslog(LOG_ERR, "%m");
+ return;
+ }
+ }
+
+ if (verbose)
+ printf("checking for kernel dump on device %s\n", device);
+
+ fd = open(device, O_RDWR);
+ if (fd < 0) {
+ syslog(LOG_ERR, "%s: %m", device);
+ return;
+ }
+
+ error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
+ if (!error)
+ error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
+ if (error) {
+ syslog(LOG_ERR,
+ "couldn't find media and/or sector size of %s: %m", device);
+ goto closefd;
+ }
+
+ if (verbose) {
+ printf("mediasize = %lld\n", (long long)mediasize);
+ printf("sectorsize = %u\n", sectorsize);
+ }
+
+ lasthd = mediasize - sectorsize;
+ lseek(fd, lasthd, SEEK_SET);
+ error = read(fd, &kdhl, sizeof kdhl);
+ if (error != sizeof kdhl) {
+ syslog(LOG_ERR,
+ "error reading last dump header at offset %lld in %s: %m",
+ (long long)lasthd, device);
+ goto closefd;
+ }
+ if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) {
+ if (verbose)
+ printf("magic mismatch on last dump header on %s\n",
+ device);
+
+ status = STATUS_BAD;
+ if (force == 0)
+ goto closefd;
+
+ if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED,
+ sizeof kdhl.magic) == 0) {
+ if (verbose)
+ printf("forcing magic on %s\n", device);
+ memcpy(kdhl.magic, KERNELDUMPMAGIC,
+ sizeof kdhl.magic);
+ } else {
+ syslog(LOG_ERR, "unable to force dump - bad magic");
+ goto closefd;
+ }
+ }
+ if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
+ syslog(LOG_ERR,
+ "unknown version (%d) in last dump header on %s",
+ dtoh32(kdhl.version), device);
+
+ status = STATUS_BAD;
+ if (force == 0)
+ goto closefd;
+ }
+
+ nfound++;
+ if (clear)
+ goto nuke;
+
+ if (kerneldump_parity(&kdhl)) {
+ syslog(LOG_ERR,
+ "parity error on last dump header on %s", device);
+ nerr++;
+ status = STATUS_BAD;
+ if (force == 0)
+ goto closefd;
+ }
+ dumpsize = dtoh64(kdhl.dumplength);
+ firsthd = lasthd - dumpsize - sizeof kdhf;
+ lseek(fd, firsthd, SEEK_SET);
+ error = read(fd, &kdhf, sizeof kdhf);
+ if (error != sizeof kdhf) {
+ syslog(LOG_ERR,
+ "error reading first dump header at offset %lld in %s: %m",
+ (long long)firsthd, device);
+ nerr++;
+ goto closefd;
+ }
+
+ if (verbose >= 2) {
+ printf("First dump headers:\n");
+ printheader(stdout, &kdhf, device, bounds, -1);
+
+ printf("\nLast dump headers:\n");
+ printheader(stdout, &kdhl, device, bounds, -1);
+ printf("\n");
+ }
+
+ if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
+ syslog(LOG_ERR,
+ "first and last dump headers disagree on %s", device);
+ nerr++;
+ status = STATUS_BAD;
+ if (force == 0)
+ goto closefd;
+ } else {
+ status = STATUS_GOOD;
+ }
+
+ if (checkfor) {
+ printf("A dump exists on %s\n", device);
+ close(fd);
+ exit(0);
+ }
+
+ if (kdhl.panicstring[0])
+ syslog(LOG_ALERT, "reboot after panic: %s", kdhl.panicstring);
+ else
+ syslog(LOG_ALERT, "reboot");
+
+ if (verbose)
+ printf("Checking for available free space\n");
+ if (!check_space(savedir, dumpsize)) {
+ nerr++;
+ goto closefd;
+ }
+
+ writebounds(bounds + 1);
+
+ sprintf(buf, "info.%d", bounds);
+
+ /*
+ * Create or overwrite any existing dump header files.
+ */
+ fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ if (fdinfo < 0) {
+ syslog(LOG_ERR, "%s: %m", buf);
+ nerr++;
+ goto closefd;
+ }
+ oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
+ if (compress) {
+ sprintf(buf, "vmcore.%d.gz", bounds);
+ fp = zopen(buf, "w");
+ } else {
+ sprintf(buf, "vmcore.%d", bounds);
+ fp = fopen(buf, "w");
+ }
+ if (fp == NULL) {
+ syslog(LOG_ERR, "%s: %m", buf);
+ close(fdinfo);
+ nerr++;
+ goto closefd;
+ }
+ (void)umask(oumask);
+
+ info = fdopen(fdinfo, "w");
+
+ if (verbose)
+ printheader(stdout, &kdhl, device, bounds, status);
+
+ printheader(info, &kdhl, device, bounds, status);
+ fclose(info);
+
+ syslog(LOG_NOTICE, "writing %score to %s",
+ compress ? "compressed " : "", buf);
+
+ while (dumpsize > 0) {
+ wl = BUFFERSIZE;
+ if (wl > dumpsize)
+ wl = dumpsize;
+ nr = read(fd, buf, wl);
+ if (nr != wl) {
+ if (nr == 0)
+ syslog(LOG_WARNING,
+ "WARNING: EOF on dump device");
+ else
+ syslog(LOG_ERR, "read error on %s: %m", device);
+ nerr++;
+ goto closeall;
+ }
+ if (compress) {
+ nw = fwrite(buf, 1, wl, fp);
+ } else {
+ for (nw = 0; nw < nr; nw = he) {
+ /* find a contiguous block of zeroes */
+ for (hs = nw; hs < nr; hs += BLOCKSIZE) {
+ for (he = hs; he < nr && buf[he] == 0;
+ ++he)
+ /* nothing */ ;
+ /* is the hole long enough to matter? */
+ if (he >= hs + BLOCKSIZE)
+ break;
+ }
+
+ /* back down to a block boundary */
+ he &= BLOCKMASK;
+
+ /*
+ * 1) Don't go beyond the end of the buffer.
+ * 2) If the end of the buffer is less than
+ * BLOCKSIZE bytes away, we're at the end
+ * of the file, so just grab what's left.
+ */
+ if (hs + BLOCKSIZE > nr)
+ hs = he = nr;
+
+ /*
+ * At this point, we have a partial ordering:
+ * nw <= hs <= he <= nr
+ * If hs > nw, buf[nw..hs] contains non-zero data.
+ * If he > hs, buf[hs..he] is all zeroes.
+ */
+ if (hs > nw)
+ if (fwrite(buf + nw, hs - nw, 1, fp)
+ != 1)
+ break;
+ if (he > hs)
+ if (fseeko(fp, he - hs, SEEK_CUR) == -1)
+ break;
+ }
+ }
+ if (nw != wl) {
+ syslog(LOG_ERR,
+ "write error on vmcore.%d file: %m", bounds);
+ syslog(LOG_WARNING,
+ "WARNING: vmcore may be incomplete");
+ nerr++;
+ goto closeall;
+ }
+ if (verbose) {
+ dmpcnt += wl;
+ printf("%llu\r", (unsigned long long)dmpcnt);
+ fflush(stdout);
+ }
+ dumpsize -= wl;
+ }
+ if (verbose)
+ printf("\n");
+
+ if (fclose(fp) < 0) {
+ syslog(LOG_ERR, "error on vmcore.%d: %m", bounds);
+ nerr++;
+ goto closeall;
+ }
+ nsaved++;
+
+ if (verbose)
+ printf("dump saved\n");
+
+nuke:
+ if (clear || !keep) {
+ if (verbose)
+ printf("clearing dump header\n");
+ memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof kdhl.magic);
+ lseek(fd, lasthd, SEEK_SET);
+ error = write(fd, &kdhl, sizeof kdhl);
+ if (error != sizeof kdhl)
+ syslog(LOG_ERR,
+ "error while clearing the dump header: %m");
+ }
+ close(fd);
+ return;
+
+closeall:
+ fclose(fp);
+
+closefd:
+ close(fd);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: savecore -c",
+ " savecore -C [-v] [directory device]",
+ " savecore [-fkvz] [directory [device ...]]");
+ exit (1);
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *savedir = ".";
+ struct fstab *fsp;
+ int i, ch, error;
+
+ checkfor = compress = clear = force = keep = verbose = 0;
+ nfound = nsaved = nerr = 0;
+
+ openlog("savecore", LOG_PERROR, LOG_DAEMON);
+
+ while ((ch = getopt(argc, argv, "Ccfkvz")) != -1)
+ switch(ch) {
+ case 'C':
+ checkfor = 1;
+ break;
+ case 'c':
+ clear = 1;
+ break;
+ case 'k':
+ keep = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'f':
+ force = 1;
+ break;
+ case 'z':
+ compress = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ if (checkfor && (clear || force || keep))
+ usage();
+ argc -= optind;
+ argv += optind;
+ if (argc >= 1) {
+ error = chdir(argv[0]);
+ if (error) {
+ syslog(LOG_ERR, "chdir(%s): %m", argv[0]);
+ exit(1);
+ }
+ savedir = argv[0];
+ argc--;
+ argv++;
+ }
+ if (argc == 0) {
+ for (;;) {
+ fsp = getfsent();
+ if (fsp == NULL)
+ break;
+ if (strcmp(fsp->fs_vfstype, "swap") &&
+ strcmp(fsp->fs_vfstype, "dump"))
+ continue;
+ DoFile(savedir, fsp->fs_spec);
+ }
+ } else {
+ for (i = 0; i < argc; i++)
+ DoFile(savedir, argv[i]);
+ }
+
+ /* Emit minimal output. */
+ if (nfound == 0) {
+ if (checkfor) {
+ printf("No dump exists\n");
+ exit(1);
+ }
+ syslog(LOG_WARNING, "no dumps found");
+ }
+ else if (nsaved == 0) {
+ if (nerr != 0)
+ syslog(LOG_WARNING, "unsaved dumps found but not saved");
+ else
+ syslog(LOG_WARNING, "no unsaved dumps found");
+ }
+
+ return (0);
+}
diff --git a/sbin/sconfig/Makefile b/sbin/sconfig/Makefile
new file mode 100644
index 0000000..5923692
--- /dev/null
+++ b/sbin/sconfig/Makefile
@@ -0,0 +1,8 @@
+# Cronyx Id: sbin.sconfig.Makefile,v 1.1.4.1 2003/02/17 12:51:24 rik Exp $
+# $FreeBSD$
+
+PROG= sconfig
+MAN= sconfig.8
+MANSUBDIR= /i386
+
+.include <bsd.prog.mk>
diff --git a/sbin/sconfig/sconfig.8 b/sbin/sconfig/sconfig.8
new file mode 100644
index 0000000..3fe7abc
--- /dev/null
+++ b/sbin/sconfig/sconfig.8
@@ -0,0 +1,607 @@
+.\" Copyright (c) 2002-2004 Roman Kurakin <rik@cronyx.ru>
+.\" Copyright (c) 2002-2004 Cronyx Engineering
+.\" All rights reserved.
+.\"
+.\" This software is distributed with NO WARRANTIES, not even the implied
+.\" warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" Authors grant any other persons or organisations a permission to use,
+.\" modify and redistribute this software in source and binary forms,
+.\" as long as this message is kept with the software, all derivative
+.\" works or modified versions.
+.\"
+.\" $FreeBSD$
+.Dd May 19, 2004
+.Dt SCONFIG 8 i386
+.Os
+.Sh NAME
+.Nm sconfig
+.Nd "channel configuration utility for Cronyx adapters"
+.Sh SYNOPSIS
+.Nm
+.Op Fl aimsxeftuc
+.Op Ar device
+.Op Ar data_rate_options
+.Op Ar protocol_options ...
+.Op Ar interface_options ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used for configuring the channel options of the Cronyx
+adapters.
+In asynchronous mode, all parameters should be set using the standard
+.Xr stty 1
+utility.
+With
+.Nm ,
+it is only possible to set some of them (see below).
+.Pp
+Some of the options can be set only on free channels,
+that is when the corresponding network interface is in the
+.Cm down
+state in the synchronous mode,
+and the terminal device
+.Pa /dev/tty*
+is closed in asynchronous mode.
+.Pp
+Other channel options can be changed
+.Dq "on the fly" .
+Generally, the channel options are set up during the operating system startup,
+for example, from the
+.Pa /etc/rc
+script.
+.Pp
+Note that not all options make sense in every particular case,
+and an attempt to set some of them may hung up the channel
+or the whole adapter.
+.\"--------------------------------------------------------------
+.Ss "Information Options"
+Only one of these options can be specified.
+If information option is specified,
+.Nm
+will show the corresponding information and will ignore all other options,
+except
+.Ar device .
+See also the description of the
+.Ar device
+argument.
+.Bl -tag -width indent
+.It <none>
+This will show settings of the channel.
+.It Fl a
+Print all settings of the channel.
+.It Fl i
+Print interface settings, equal to the output of the
+.Xr ifconfig 8
+utility.
+.It Fl m
+Print modem signal status.
+The description of all signals can be found in any modem documentation.
+Only LE signal should be described.
+If this signal is ON then the channel is busy.
+If it is OFF then the channel is free.
+.It Fl s
+Print brief channel statistics.
+This is the generic statistics,
+see also the
+.Fl x , e , f , t ,
+and
+.Fl u
+options.
+For a description of the output, see below.
+.Pp
+This statistics is very useful if something goes wrong.
+For example, if all interrupt counters are zero then the device
+was configured to use an interrupt that was not registered in the
+BIOS for use with the ISA bus.
+.It Fl x
+Print full channel statistics.
+This options prints additional counters,
+but with less precision than with the
+.Fl s
+option.
+.It Fl e
+Print brief E1/G703 statistics.
+If this option is selected, the
+statistics accumulated over the last 15 minutes is printed.
+For a description of the output, see below.
+.It Fl f
+Print full E1/G703 statistics.
+This option shows all E1/G703 statistics that the
+.Fl e
+option shows,
+plus total statistics for the whole period of time and statistics for
+last 24 hours (if available).
+For a description of the output, see below.
+.It Fl t
+Print brief E3/T3/STS-1 statistics.
+If this option is selected, the
+statistics accumulated over the last 15 minutes is printed.
+For a description of the output, see below.
+.It Fl u
+Print full E3/T3/STS-1 statistics.
+This option shows all E3/T3/STS-1 statistics that the
+.Fl t
+option shows,
+plus total statistics for the whole period of time and statistics for
+last 24 hours (if available).
+For a description of the output, see below.
+.It Fl c
+Cleans all kind of statistics.
+.El
+.\"--------------------------------------------------------------
+.Ss "Device Selection"
+The device is selected using the name of the network interface,
+as shown by
+.Xr ifconfig 8 .
+The channel number depends on the order the drivers were loaded into the system.
+Sometimes people confuse channel number and adapter number because of the
+same spelling.
+The adapter number appears in the kernel context, while the channel number
+is in the configuration context.
+.Bl -tag -width indent
+.It <none>
+If the device name is omitted,
+.Nm
+will print information about all channels of all Cronyx adapters
+available in the system.
+If some settings need to be made, the device name must be specified.
+.It Li cx Ns Ar ##
+This is the channel name for the Sigma family of Cronyx adapters.
+(ISA bus.)
+.It Li ct Ns Ar ##
+This is the channel name for the Tau family of Cronyx adapters.
+(ISA bus.)
+.It Li cp Ns Ar ##
+This is the channel name for the Tau-PCI family of Cronyx adapters.
+(PCI bus.)
+.It Li ce Ns Ar ##
+This is the channel name for the Tau32-PCI family of Cronyx adapters.
+(PCI bus.)
+.El
+.\"--------------------------------------------------------------
+.Ss "Data Rate Options"
+.Bl -tag -width indent
+.It Ar value
+A non-zero value will set the data rate to a given value
+in asynchronous mode,
+and will set the data rate and internal clock source of synchronization
+in synchronous mode.
+A zero value is equivalent to specifying the
+.Cm extclock
+option.
+The transmitted data (TxD) are synchronized using the internal on-board timing
+generator, the internally generated timing signal is driven on the TXCOUT pin,
+and the signal on the TXCIN pin is ignored.
+This mode is used for direct
+terminal-to-terminal communication, e.g., when connecting two computers together
+in synchronous mode with a relatively short cable.
+This method should also be
+used for testing channels with an external loopback connector.
+.It Cm extclock
+Set the external timing clock source for synchronous channels.
+External clock mode is the most commonly used method for connecting
+external modem hardware.
+In this mode,
+the external timing signal is received on the TXCIN pin of the connector,
+and it is used as a synchronization clock for transmitting data (TxD).
+.Pp
+Note: in
+.Cm extclock
+mode, the device cannot determine the value of the external timing clock
+since it does not have the built-in clock gauge.
+.El
+.\"--------------------------------------------------------------
+.Ss "Protocol Options"
+Note: these option can only be used on a free channel, and they require
+specifying the device name.
+.Bl -tag -width indent
+.It Cm async
+(Only for Sigma family.)
+Select the asynchronous protocol (or mode).
+In this mode, Cronyx adapters behave as normal serial devices,
+and standard serial communications utilities can be used to
+work with them.
+All asynchronous settings should be set using the standard
+serial communications configuration utilities, e.g.,
+.Xr stty 1 .
+With
+.Nm ,
+it is only possible to set some of them.
+.It Cm cisco
+Select the Cisco HDLC synchronous protocol.
+.It Cm fr
+Select the Frame Relay synchronous protocol
+.Tn ( ANSI
+T1.617 Annex D).
+.It Cm ppp
+Select the synchronous PPP protocol.
+PPP parameters can be configured using the
+.Xr spppcontrol 8
+utility.
+.It Sm Cm keepalive No = Bro Cm on , off Brc Sm
+Turn on/off transmission of keepalive messages.
+This option is used only for synchronous PPP.
+If this option is
+.Cm on ,
+PPP will periodically send ECHO-REQUEST messages.
+If it will not receive any ECHO-REPLY messages for
+some (definite) period of time it will break the connection.
+It is used for tracking the line state.
+.It Cm idle
+This mode is reported when using Netgraph.
+An actual protocol depends on the type of a connected Netgraph node,
+and it cannot be changed with
+.Nm .
+.El
+.\"--------------------------------------------------------------
+.Ss "Interface Options"
+Not all of these options can be set on a busy channel, and not all of them
+are applicable to all kinds of adapters/channels.
+For all dual-state options,
+.Cm off
+is the default value.
+None of these options can be used in the asynchronous mode,
+except for the
+.Cm debug
+option.
+.Bl -tag -width indent
+.It Sm Cm port No = Bro Cm rs232 , v35 , rs449 Brc Sm
+Set the port type for old Sigma models.
+.It Sm Cm cfg No = Bro Cm A , B , C Brc Sm
+Set the configuration for the adapter.
+This option can be used only with Tau/E1
+and Tau/G703 adapters, and only if all channels are free.
+.Bl -tag -width ".Cm cfg Ns = Ns Cm A"
+.It Cm cfg Ns = Ns Cm A
+Two independent E1/G703 channels.
+This is the default setting.
+.It Cm cfg Ns = Ns Cm B
+(Only for ISA models.)
+For Tau/G703 this means one G703 channel and one digital channel.
+For Tau/E1, the first physical channel is divided into two subchannels.
+One of them goes to the first logical channel, another one goes to the
+second physical channel.
+Second (logical) channel is the digital channel.
+.It Cm cfg Ns = Ns Cm C
+(Only for E1 models.)
+In this mode, first
+physical channel consists of three data flows.
+Two of them go to the two (logical) channels.
+The last one goes to the second physical channel.
+On newer models (Tau32-PCI, Tau-PCI/2E1 and Tau-PCI/4E1),
+this programs the hardware to use a single source of synchronization
+and pass all unused (in both channels) timeslots from
+one channel to another.
+.El
+.Pp
+For a detailed description of available configuration modes,
+see the adapter documentation.
+This option cannot be set on a busy channel.
+.It Sm Cm loop No = Bro Cm on , off Brc Sm
+Turn on/off internal loopback.
+This mode is useful for debugging.
+When this mode is
+.Cm on ,
+some data should be sent.
+If no interrupts are generated, chances are that
+the corresponding IRQ configuration entry in the BIOS
+was not switched from
+.Dq Li "PCI/ISA PNP"
+to
+.Dq Li "Legacy ISA" .
+.It Sm Cm rloop No = Bro Cm on , off Brc Sm
+(Only for Tau32-PCI and Tau-PCI/E3.)
+Turn on/off remote loopback feature.
+This mode is also useful for debugging.
+.It Sm Cm dpll No = Bro Cm on , off Brc Sm
+Turn on/off digital phase locked loop mode (DPLL).
+When enabled, the receiver
+timing clock signal is derived from the received data.
+Must be used with the NRZI
+encoding to avoid the synchronization loss.
+.It Sm Cm nrzi No = Bro Cm on , off Brc Sm
+Turn on/off NRZI encoding.
+If
+.Cm off ,
+NRZ encoding is used.
+.Bl -tag -width "NRZI"
+.It NRZ
+The zero bit is transmitted by the zero signal level,
+the one bit is transmitted by the positive signal level.
+.It NRZI
+The zero bit is transmitted by the change of the signal
+level, the one bit is by the constant signal level.
+Commonly used with the
+.Cm dpll Ns = Ns Cm on
+option.
+.El
+.It Sm Cm invclk No = Bro Cm on , off Brc Sm
+(Tau and Tau-PCI only.)
+Invert both the transmit and receive clock signals.
+.It Sm Cm invrclk No = Bro Cm on , off Brc Sm
+(Tau-PCI only.)
+Invert the receive clock signals.
+.It Sm Cm invtclk No = Bro Cm on , off Brc Sm
+(Tau-PCI only.)
+Invert the transmit clock signals.
+.It Sm Cm higain No = Bro Cm on , off Brc Sm
+(E1 only.)
+In off state the sensitivity is -12 dB.
+Turn on/off increasing the E1 receiver's non-linear sensitivity to -30dB.
+This allows increasing of the line distance.
+.It Sm Cm cablen No = Bro Cm on , off Brc Sm
+(Tau-PCI/T3 and Tau-PCI/STS-1 only.)
+Turn on/off adjusting of the transmit signal for a long cable T3/STS-1.
+.It Sm Cm monitor No = Bro Cm on , off Brc Sm
+(Tau32-PCI, Tau-PCI/2E1 and Tau-PCI/4E1 only.)
+Turn on/off increasing of the E1 receiver's linear sensitivity to -30dB.
+This can be used for the interception purposes.
+.It Sm Cm phony No = Bro Cm on , off Brc Sm
+(Tau32-PCI and Tau-PCI E1 family only.)
+Turn on/off the so-called
+.Dq phony
+mode.
+This mode allows
+receiving raw CEPT frames from the E1 line.
+Raw frames can be accessed, for example, with the raw protocol.
+Packets would come at a rate of 500 frames per second
+with length
+.No 16* Ns Ar N
+(for Tau-PCI/E1 model), where
+.Ar N
+is the number of timeslots.
+For
+Tau-PCI/2E1 and Tau-PCI/4E1,
+.Ar N
+should be equal to 32 regardless of the number of
+used timeslots.
+.It Sm Cm unfram No = Bro Cm on , off Brc Sm
+(Tau32-PCI, Tau-PCI/2E1 and Tau-PCI/4E1 only.)
+Turn on/off unframed mode.
+.Bl -tag -width ".Cm unfram Ns = Ns Cm off"
+.It Cm unfram Ns = Ns Cm on
+Switch channel to the unframed G.703 mode.
+.It Cm unfram Ns = Ns Cm off
+Switch channel to the framed E1 (G.704) mode.
+.El
+.It Sm Cm scrambler No = Bro Cm on , off Brc Sm
+(Tau32-PCI, Tau-PCI/G.703, Tau-PCI/2E1, and
+Tau-PCI/4E1 in unframed mode only.)
+Turn on/off scrambling of the G.703 data.
+.It Sm Cm use16 No = Bro Cm on , off Brc Sm
+(Tau32-PCI and Tau-PCI E1 family only.)
+Turn on/off the usage of the 16th timeslot for data transmission.
+Normally, the 16th timeslot is used for signalling information
+(multiframing CAS).
+.It Sm Cm crc4 No = Bro Cm on , off Brc Sm
+(E1 only.)
+Turn on/off CRC4 superframe mode.
+.It Sm Cm syn No = Bro Cm int , rcv , rcv0 , rcv1 , rcv2 , rcv3 Brc Sm
+.Bl -tag -width ".Cm rcv3"
+.It Cm int
+Use an internal clock generator for G703 transmitter
+(clock master).
+.It Cm rcv
+Use the G703 receiver data clock as the transmit clock
+(clock slave).
+.It Cm rcv0 , rcv1 , rcv2 , rcv3
+Use the G703 receiver clock of the other channel
+(E1 models only).
+.El
+.It Cm dir Ns = Ns Ar number
+(Tau32-PCI, Tau-PCI/2E1 and Tau-PCI/4E1 only.)
+Bind a logical channel to a physical channel.
+Using this parameter it is possible, for example, to split
+physical E1 channel into several logical channels.
+.It Cm ts Ns = Ns Ar interval
+(E1 only.)
+Set up the list of timeslots for use by the channel.
+The timeslots are numbered from 1 to 31,
+and are separated by a comma or a minus sign,
+giving an interval.
+Example:
+.Dq Li ts=1-3,5,17 .
+.It Cm pass Ns = Ns Ar interval
+(Tau/E1 only.)
+Set up the list of timeslots, translated to the E1 subchannel in
+.Cm cfg Ns = Ns Cm B
+and
+.Cm cfg Ns = Ns Cm C
+configurations.
+.It Sm Cm debug No = Bro Cm 0 , 1 , 2 Brc Sm
+Turn on/off debug messages.
+.Bl -tag -width 2n
+.It Cm 0
+Turn debug messages off.
+.It Cm 1
+Turn debug messages on, equivalent to the
+.Cm debug
+option of the
+.Xr ifconfig 8
+utility.
+.It Cm 2
+High intensive debug messages, for developers only.
+.El
+.El
+.\"--------------------------------------------------------------
+.Sh EXAMPLES
+Set up channel 1 for use with the HDSL modem or any other
+synchronous leased-line modem, and PPP/HDLC protocol (for Sigma):
+.Bd -literal -offset indent
+sconfig cx1 ppp extclock
+ifconfig cx1 158.250.244.2 158.250.244.1 up
+.Ed
+.Pp
+Set up channel 0 of Tau/E1 for use with the Cisco protocol
+over the E1 link, with a single virtual connection.
+The DLCI number is detected automatically.
+Use timeslots 1-10:
+.Bd -literal -offset indent
+sconfig ct0 cisco ts=1-10
+ifconfig ct0 158.250.244.2 158.250.244.1 up
+.Ed
+.Pp
+Set up channel 0 for the synchronous null-modem link to the nearby computer,
+internal clock source, 256000 bits/sec, protocol Cisco/HDLC (for Tau):
+.Bd -literal -offset indent
+sconfig ct0 cisco 256000
+ifconfig ct0 200.1.1.1 200.1.1.2 up
+.Ed
+.Pp
+Set up channel 1 for the leased line link using the data-only
+null-modem cable (or modems like Zelax+ M115).
+Synchronous DPLL mode, 128000
+bits/sec, protocol PPP/HDLC, NRZI encoding (for Sigma):
+.Bd -literal -offset indent
+sconfig cx1 ppp 128000 nrzi=on dpll=on
+ifconfig cx1 158.250.244.2 158.250.244.1 up
+.Ed
+.\"--------------------------------------------------------------
+.Sh DIAGNOSTICS
+This section contains a description of abbreviations used by
+.Nm
+while displaying various statistics.
+For a description of options related to
+statistics, please see above.
+.\"--------------------------------------------------------------
+.Ss Statistics
+When running, the driver gathers statistics about the channels, which
+can be accessed using the
+.Nm
+utility,
+or through the
+.Xr ioctl 2
+call
+.Dv SERIAL_GETSTAT .
+.Pp
+.Bl -tag -width indent -compact
+.It Va Rintr
+Total number of receive interrupts.
+.It Va Tintr
+Total number of transmit interrupts.
+.It Va Mintr
+Total number of modem interrupts.
+.It Va Ibytes
+Total bytes received.
+.It Va Ipkts
+Total packets received (for HDLC mode).
+.It Va Ierrs
+Number of receive errors.
+.It Va Obytes
+Total bytes transmitted.
+.It Va Opkts
+Total packets transmitted (for HDLC mode).
+.It Va Oerrs
+Number of transmit errors.
+.El
+.\"--------------------------------------------------------------
+.Ss E1/G.703 Statistics
+For E1 and G.703 channels, the SNMP-compatible statistics data are gathered
+(see RFC 1406).
+It can be accessed using the
+.Nm
+utility,
+or through the
+.Xr ioctl 2
+call
+.Dv SERIAL_GETESTAT .
+.Bl -tag -width ".Va RCRC Pq Va rcrce"
+.It Va Unav Pq Va uas
+Unavailable seconds: receiving all ones, loss of carrier, or loss of
+signal.
+.It Va Degr Pq Va dm
+Degraded minutes: having error rate more than 10E-6, not counting unavailable
+and severely errored seconds.
+.It Va Bpv Pq Va bpv
+HDB3 bipolar violation errors.
+.It Va Fsyn Pq Va fse
+Frame synchronization errors (E1 only).
+.It Va CRC Pq Va crce
+CRC4 errors (E1).
+.It Va RCRC Pq Va rcrce
+Remote CRC4 errors: E-bit counter (E1).
+.It Va Err Pq Va es
+Errored seconds: any framing errors, or out of frame sync, or any slip events.
+.It Va Lerr Pq Va les
+Line errored seconds: any BPV.
+.It Va Sev Pq Va ses
+Severely errored seconds: 832 or more framing errors, or 2048 or more bipolar
+violations.
+.It Va Bur Pq Va bes
+Bursty errored seconds: more than 1 framing error, but not severely errored.
+.It Va Oof Pq Va oofs
+Severely errored framing seconds: out of frame sync.
+.It Va Slp Pq Va css
+Controlled slip seconds: any slip buffer overflow or underflow.
+.El
+.\"--------------------------------------------------------------
+.Ss E1/G.703 Status
+The
+.Nm
+utility also prints the E1/G.703 channel status.
+The status can have the
+following values (non-exclusive):
+.Pp
+.Bl -tag -width ".Li FARLOMF" -compact
+.It Li Ok
+The channel is in a valid state, synchronized.
+.It Li LOS
+Loss of sync.
+.It Li AIS
+Receiving unframed all ones (E1 only).
+.It Li LOF
+Loss of framing (E1 only).
+.It Li LOMF
+Loss of multiframing (E1 only).
+.It Li FARLOF
+Receiving remote alarm (E1 only).
+.It Li AIS16
+Receiving all ones in the timeslot 16 (E1 only).
+.It Li FARLOMF
+Receiving distant multiframe alarm (E1 only).
+.It Li TSTREQ
+Receiving test request code (G.703 only).
+.It Li TSTERR
+Test error (G.703 only).
+.El
+.\"--------------------------------------------------------------
+.Sh SEE ALSO
+.Xr stty 1 ,
+.Xr ioctl 2 ,
+.Xr sppp 4 ,
+.Xr ifconfig 8 ,
+.Xr route 8 ,
+.Xr spppcontrol 8
+.\"--------------------------------------------------------------
+.Sh HISTORY
+This utility is a replacement for the
+.Nm cxconfig
+and
+.Nm ctconfig
+utilities that were used in the past with
+.Fx
+drivers.
+Those two utilities and
+.Nm
+are not compatible,
+and therefore all scripts using them have to be rewritten.
+Moreover,
+.Tn Linux
+and
+.Fx
+versions of the
+.Nm
+utility are not fully compatible.
+.\"--------------------------------------------------------------
+.Sh AUTHORS
+.An Cronyx Engineering Aq info@cronyx.ru
+.Pp
+.Pa http://www.cronyx.ru
+.\"--------------------------------------------------------------
+.Sh BUGS
+All software produced by Cronyx Engineering is thoroughly tested.
+But as created by a man, it can contain some bugs.
+If you have caught one, try to localize it and send an email with the
+description of the bug, and all operations that you have done.
+We will try to reproduce the error and fix it.
diff --git a/sbin/sconfig/sconfig.c b/sbin/sconfig/sconfig.c
new file mode 100644
index 0000000..783eac0
--- /dev/null
+++ b/sbin/sconfig/sconfig.c
@@ -0,0 +1,1190 @@
+/*
+ * Channel configuration utility for Cronyx serial adapters.
+ *
+ * Copyright (C) 1997-2002 Cronyx Engineering.
+ * Author: Serge Vakulenko, <vak@cronyx.ru>
+ *
+ * Copyright (C) 1999-2005 Cronyx Engineering.
+ * Author: Roman Kurakin, <rik@cronyx.ru>
+ *
+ * This software is distributed with NO WARRANTIES, not even the implied
+ * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Authors grant any other persons or organisations permission to use
+ * or modify this software as long as this message is kept with the software,
+ * all derivative works or modified versions.
+ *
+ * Cronyx Id: sconfig.c,v 1.4.2.2 2005/11/09 13:01:35 rik Exp $
+ * $FreeBSD$
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <machine/cserial.h>
+
+#define MAXCHAN 128
+
+int vflag, eflag, sflag, mflag, cflag, fflag, iflag, aflag, xflag;
+int tflag, uflag;
+char mask[64];
+int adapter_type; /* 0-sigma, 1-tau, 2-taupci, 3-tau32 */
+char chan_name[16];
+
+extern char *optarg;
+extern int optind;
+
+static void
+usage (void)
+{
+ printf(
+"Serial Adapter Configuration Utility\n"
+"Copyright (C) 1998-2005 Cronyx Engineering.\n"
+"See also man sconfig (8)\n"
+"Usage:\n"
+"\tsconfig [-aimsxeftuc] [device [parameters ...]]\n"
+"\n"
+"Options:\n"
+"\t<no options>\t\t -- print channel options\n"
+"\t-a\t\t\t -- print all settings of the channel\n"
+"\t-i\t\t\t -- print network interface status\n"
+"\t-m\t\t\t -- print modem signal status\n"
+"\t-s\t\t\t -- print channel statistics\n"
+"\t-x\t\t\t -- print extended channel statistics\n"
+"\t-e\t\t\t -- print short E1/G703 statistics\n"
+"\t-f\t\t\t -- print full E1/G703 statistics\n"
+"\t-t\t\t\t -- print short E3/T3/STS-1 statistics\n"
+"\t-u\t\t\t -- print full E3/T3/STS-1 statistics\n"
+"\t-c\t\t\t -- clear statistics\n"
+"\nParameters:\n"
+"\t<number>\t\t -- baud rate, internal clock\n"
+"\textclock\t\t -- external clock (default)\n"
+"\nProtocol options:\n"
+"\tasync\t\t\t -- asynchronous protocol\n"
+#ifdef __linux__
+"\tsync\t\t\t -- synchronous protocol\n"
+#endif
+"\tcisco\t\t\t -- Cisco/HDLC protocol\n"
+"\tfr\t\t\t -- Frame Relay protocol\n"
+#ifdef __linux__
+"\t dlci<number>\t -- Add new DLCI\n"
+#endif
+"\tppp\t\t\t -- PPP protocol\n"
+#ifdef __linux__
+"\trbrg\t\t\t -- Remote bridge\n"
+"\traw\t\t\t -- raw HDLC protocol\n"
+"\tpacket\t\t\t -- packetized HDLC protocol\n"
+"\tidle\t\t\t -- no protocol\n"
+#else
+"\t keepalive={on,of}\t -- Enable/disable keepalive\n"
+#endif
+"\nInterface options:\n"
+"\tport={rs232,v35,rs449}\t -- port type (for old models of Sigma)\n"
+"\tcfg={A,B,C}\t\t -- adapter configuration\n"
+"\tloop={on,off}\t\t -- internal loopback\n"
+"\trloop={on,off}\t\t -- remote loopback\n"
+"\tdpll={on,off}\t\t -- DPLL mode\n"
+"\tnrzi={on,off}\t\t -- NRZI encoding\n"
+"\tinvclk={on,off}\t\t -- invert receive and transmit clock\n"
+"\tinvrclk={on,off}\t -- invert receive clock\n"
+"\tinvtclk={on,off}\t -- invert transmit clock\n"
+"\thigain={on,off}\t\t -- E1 high non linear input sensitivity \n\t\t\t\t (long line)\n"
+"\tmonitor={on,off}\t -- E1 high linear input sensitivity \n\t\t\t\t (interception mode)\n"
+"\tphony={on,off}\t\t -- E1 telepnony mode\n"
+"\tunfram={on,off}\t\t -- E1 unframed mode\n"
+"\tscrambler={on,off}\t -- G.703 scrambling mode\n"
+"\tuse16={on,off}\t\t -- E1 timeslot 16 usage\n"
+"\tcrc4={on,off}\t\t -- E1 CRC4 mode\n"
+#ifdef __linux__
+"\tami={on,off}\t\t -- E1 AMI or HDB3 line code\n"
+"\tmtu={size}\t\t -- set MTU in bytes\n"
+#endif
+"\tsyn={int,rcv,rcvX}\t -- G.703 transmit clock\n"
+"\tts=...\t\t\t -- E1 timeslots\n"
+"\tpass=...\t\t -- E1 subchannel timeslots\n"
+"\tdir=<num>\t\t -- connect channel to link<num>\n"
+/*"\trqken={size}\t\t -- set receive queue length in packets\n"*/
+/*"\tcablen={on,off}\t\t -- T3/STS-1 high transmitter output for long cable\n"*/
+"\tdebug={0,1,2}\t\t -- enable/disable debug messages\n"
+ );
+ exit (0);
+}
+
+static unsigned long
+scan_timeslots (char *s)
+{
+ char *e;
+ long v;
+ int i;
+ unsigned long ts, lastv;
+
+ ts = lastv = 0;
+ for (;;) {
+ v = strtol (s, &e, 10);
+ if (e == s)
+ break;
+ if (*e == '-') {
+ lastv = v;
+ s = e+1;
+ continue;
+ }
+ if (*e == ',')
+ ++e;
+
+ if (lastv)
+ for (i=lastv; i<v; ++i)
+ ts |= 1L << i;
+ ts |= 1L << v;
+
+ lastv = 0;
+ s = e;
+ }
+ return ts;
+}
+
+static int
+ppp_ok (void)
+{
+#ifdef __linux__
+ int s, p;
+ struct ifreq ifr;
+ char pttyname[32];
+ char *p1, *p2;
+ int i, j;
+ int ppp_disc = N_PPP;
+
+ /*
+ * Open a socket for doing the ioctl operations.
+ */
+ s = socket (AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ fprintf (stderr, "Error opening socket.\n");
+ return 0;
+ }
+ strncpy (ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
+ if (ioctl (s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0) {
+ /* Ok. */
+ close (s);
+ return 1;
+ }
+ close (s);
+
+ /* open pseudo-tty and try to set PPP discipline */
+ sprintf (pttyname, "/dev/ptyXX");
+ p1 = &pttyname[8];
+ p2 = &pttyname[9];
+ for (i=0; i<16; i++) {
+ struct stat stb;
+
+ *p1 = "pqrstuvwxyzabcde"[i];
+ *p2 = '0';
+ if (stat (pttyname, &stb) < 0)
+ continue;
+ for (j=0; j<16; j++) {
+ *p2 = "0123456789abcdef"[j];
+ p = open (pttyname, 2);
+ if (p > 0) {
+ if (ioctl (p, TIOCSETD, &ppp_disc) < 0) {
+ fprintf (stderr, "No PPP discipline in kernel.\n");
+ close (p);
+ return 0;
+ }
+ close (p);
+ return 1;
+ }
+ }
+ }
+ fprintf (stderr, "Cannot get pseudo-tty.\n");
+ return 0;
+#else
+ return 1;
+#endif
+}
+
+static char *
+format_timeslots (unsigned long s)
+{
+ static char buf [100];
+ char *p = buf;
+ int i;
+
+ for (i=1; i<32; ++i)
+ if ((s >> i) & 1) {
+ int prev = (i > 1) & (s >> (i-1));
+ int next = (i < 31) & (s >> (i+1));
+
+ if (prev) {
+ if (next)
+ continue;
+ *p++ = '-';
+ } else if (p > buf)
+ *p++ = ',';
+
+ if (i >= 10)
+ *p++ = '0' + i / 10;
+ *p++ = '0' + i % 10;
+ }
+ *p = 0;
+ return buf;
+}
+
+static void
+print_modems (int fd, int need_header)
+{
+ int status;
+
+ if (ioctl (fd, TIOCMGET, &status) < 0) {
+ perror ("getting modem status");
+ return;
+ }
+ if (need_header)
+ printf ("Channel\tLE\tDTR\tDSR\tRTS\tCTS\tCD\n");
+ printf ("%s\t%s\t%s\t%s\t%s\t%s\t%s\n", chan_name,
+ status & TIOCM_LE ? "On" : "-",
+ status & TIOCM_DTR ? "On" : "-",
+ status & TIOCM_DSR ? "On" : "-",
+ status & TIOCM_RTS ? "On" : "-",
+ status & TIOCM_CTS ? "On" : "-",
+ status & TIOCM_CD ? "On" : "-");
+}
+
+static void
+print_ifconfig (int fd)
+{
+ char buf [64];
+#ifdef __linux__
+ char protocol [8];
+
+ if (ioctl (fd, SERIAL_GETPROTO, &protocol) >= 0 &&
+ strcmp (protocol, "fr") == 0)
+ sprintf (buf, "ifconfig %sd16 2>/dev/null", chan_name);
+ else
+#endif
+ sprintf (buf, "ifconfig %s 2>/dev/null", chan_name);
+ system (buf);
+}
+
+static char *
+format_long (unsigned long val)
+{
+ static char s[32];
+ int l;
+ l = sprintf (s, "%lu", val);
+ if (l>7 && !sflag) {
+ s[3] = s[2];
+ s[2] = s[1];
+ s[1] = '.';
+ s[4] = 'e';
+ sprintf (s + 5, "%02d", l-1);
+ }
+ return s;
+}
+
+static void
+print_stats (int fd, int need_header)
+{
+ struct serial_statistics st;
+ unsigned long sarr [9];
+ int i;
+
+ if (ioctl (fd, SERIAL_GETSTAT, &st) < 0) {
+ perror ("getting statistics");
+ return;
+ }
+ if (need_header) {
+ if (sflag) {
+ printf (" ------------Receive----------- "
+ "------------Transmit----------\n");
+ printf ("Channel Interrupts Packets Errors "
+ "Interrupts Packets Errors\n");
+ }
+ else {
+ printf (" --------Receive--------------- "
+ "--------Transmit-------------- Modem\n");
+ printf ("Channel Intrs Bytes Packets Errors "
+ "Intrs Bytes Packets Errors Intrs\n");
+ }
+ }
+
+ sarr [0] = st.rintr;
+ sarr [1] = st.ibytes;
+ sarr [2] = st.ipkts;
+ sarr [3] = st.ierrs;
+ sarr [4] = st.tintr;
+ sarr [5] = st.obytes;
+ sarr [6] = st.opkts;
+ sarr [7] = st.oerrs;
+ sarr [8] = st.mintr;
+ printf ("%s", chan_name);
+ if (sflag) {
+ printf ("\t%-12lu%-12lu%-12lu%-12lu%-12lu%-12lu", sarr[0],
+ sarr[2], sarr[3], sarr[4], sarr[6], sarr[7]);
+ } else {
+ for (i = 0; i < 9; i++)
+ printf ("\t%s", format_long (sarr [i]));
+ printf ("\n");
+ }
+}
+
+static void
+clear_stats (int fd)
+{
+ if (ioctl (fd, SERIAL_CLRSTAT, 0) < 0) {
+ perror ("clearing statistics");
+ exit (-1);
+ }
+}
+
+static char *
+format_e1_status (unsigned long status)
+{
+ static char buf [80];
+
+ if (status == 0)
+ return "n/a";
+ if (status & E1_NOALARM)
+ return "Ok";
+ buf[0] = 0;
+ if (status & E1_LOS) strcat (buf, ",LOS");
+ if (status & E1_AIS) strcat (buf, ",AIS");
+ if (status & E1_LOF) strcat (buf, ",LOF");
+ if (status & E1_LOMF) strcat (buf, ",LOMF");
+ if (status & E1_CRC4E) strcat (buf, ",CRC4E");
+ if (status & E1_FARLOF) strcat (buf, ",FARLOF");
+ if (status & E1_AIS16) strcat (buf, ",AIS16");
+ if (status & E1_FARLOMF) strcat (buf, ",FARLOMF");
+/* if (status & E1_TSTREQ) strcat (buf, ",TSTREQ");*/
+/* if (status & E1_TSTERR) strcat (buf, ",TSTERR");*/
+ if (buf[0] == ',')
+ return buf+1;
+ return "Unknown";
+}
+
+static void
+print_frac (int leftalign, unsigned long numerator, unsigned long divider)
+{
+ int n;
+
+ if (numerator < 1 || divider < 1) {
+ printf (leftalign ? "/- " : " -");
+ return;
+ }
+ n = (int) (0.5 + 1000.0 * numerator / divider);
+ if (n < 1000) {
+ printf (leftalign ? "/.%-3d" : " .%03d", n);
+ return;
+ }
+ putchar (leftalign ? '/' : ' ');
+
+ if (n >= 1000000) n = (n+500) / 1000 * 1000;
+ else if (n >= 100000) n = (n+50) / 100 * 100;
+ else if (n >= 10000) n = (n+5) / 10 * 10;
+
+ switch (n) {
+ case 1000: printf (".999"); return;
+ case 10000: n = 9990; break;
+ case 100000: n = 99900; break;
+ case 1000000: n = 999000; break;
+ }
+ if (n < 10000) printf ("%d.%d", n/1000, n/10%100);
+ else if (n < 100000) printf ("%d.%d", n/1000, n/100%10);
+ else if (n < 1000000) printf ("%d.", n/1000);
+ else printf ("%d", n/1000);
+}
+
+static void
+print_e1_stats (int fd, int need_header)
+{
+ struct e1_statistics st;
+ int i, maxi;
+
+ if (need_header)
+ printf ("Chan\t Unav/Degr Bpv/Fsyn CRC/RCRC Err/Lerr Sev/Bur Oof/Slp Status\n");
+
+ if (ioctl (fd, SERIAL_GETESTAT, &st) < 0)
+ return;
+ printf ("%s\t", chan_name);
+
+ /* Unavailable seconds, degraded minutes */
+ print_frac (0, st.currnt.uas, st.cursec);
+ print_frac (1, 60 * st.currnt.dm, st.cursec);
+
+ /* Bipolar violations, frame sync errors */
+ print_frac (0, st.currnt.bpv, st.cursec);
+ print_frac (1, st.currnt.fse, st.cursec);
+
+ /* CRC errors, remote CRC errors (E-bit) */
+ print_frac (0, st.currnt.crce, st.cursec);
+ print_frac (1, st.currnt.rcrce, st.cursec);
+
+ /* Errored seconds, line errored seconds */
+ print_frac (0, st.currnt.es, st.cursec);
+ print_frac (1, st.currnt.les, st.cursec);
+
+ /* Severely errored seconds, bursty errored seconds */
+ print_frac (0, st.currnt.ses, st.cursec);
+ print_frac (1, st.currnt.bes, st.cursec);
+
+ /* Out of frame seconds, controlled slip seconds */
+ print_frac (0, st.currnt.oofs, st.cursec);
+ print_frac (1, st.currnt.css, st.cursec);
+
+ printf (" %s\n", format_e1_status (st.status));
+
+ if (fflag) {
+ /* Print total statistics. */
+ printf ("\t");
+ print_frac (0, st.total.uas, st.totsec);
+ print_frac (1, 60 * st.total.dm, st.totsec);
+
+ print_frac (0, st.total.bpv, st.totsec);
+ print_frac (1, st.total.fse, st.totsec);
+
+ print_frac (0, st.total.crce, st.totsec);
+ print_frac (1, st.total.rcrce, st.totsec);
+
+ print_frac (0, st.total.es, st.totsec);
+ print_frac (1, st.total.les, st.totsec);
+
+ print_frac (0, st.total.ses, st.totsec);
+ print_frac (1, st.total.bes, st.totsec);
+
+ print_frac (0, st.total.oofs, st.totsec);
+ print_frac (1, st.total.css, st.totsec);
+
+ printf (" -- Total\n");
+
+ /* Print 24-hour history. */
+ maxi = (st.totsec - st.cursec) / 900;
+ if (maxi > 48)
+ maxi = 48;
+ for (i=0; i<maxi; ++i) {
+ printf (" ");
+ print_frac (0, st.interval[i].uas, 15*60);
+ print_frac (1, 60 * st.interval[i].dm, 15*60);
+
+ print_frac (0, st.interval[i].bpv, 15*60);
+ print_frac (1, st.interval[i].fse, 15*60);
+
+ print_frac (0, st.interval[i].crce, 15*60);
+ print_frac (1, st.interval[i].rcrce, 15*60);
+
+ print_frac (0, st.interval[i].es, 15*60);
+ print_frac (1, st.interval[i].les, 15*60);
+
+ print_frac (0, st.interval[i].ses, 15*60);
+ print_frac (1, st.interval[i].bes, 15*60);
+
+ print_frac (0, st.interval[i].oofs, 15*60);
+ print_frac (1, st.interval[i].css, 15*60);
+
+ if (i < 3)
+ printf (" -- %dm\n", (i+1)*15);
+ else
+ printf (" -- %dh %dm\n", (i+1)/4, (i+1)%4*15);
+ }
+ }
+}
+
+static char *
+format_e3_status (unsigned long status)
+{
+ static char buf [80];
+
+ buf[0] = 0;
+ if (status & E3_LOS) strcat (buf, ",LOS");
+ if (status & E3_TXE) strcat (buf, ",XMIT");
+ if (buf[0] == ',')
+ return buf+1;
+ return "Ok";
+}
+
+static char *
+format_e3_cv (unsigned long cv, unsigned long baud, unsigned long time)
+{
+ static char buf[80];
+
+ if (!cv || !baud || !time)
+ sprintf (buf, " - ");
+ else
+ sprintf (buf, "%10lu (%.1e)", cv, (double)cv/baud/time);
+ return buf;
+}
+
+static void
+print_e3_stats (int fd, int need_header)
+{
+ struct e3_statistics st;
+ int i, maxi;
+ long baud;
+
+ if (need_header)
+ printf ("Chan\t--Code Violations---\t\t\t\t\t ----Status----\n");
+
+ if (ioctl (fd, SERIAL_GETE3STAT, &st) < 0 ||
+ ioctl (fd, SERIAL_GETBAUD, &baud) < 0)
+ return;
+
+ if (!st.cursec)
+ st.cursec = 1;
+
+ printf ("%s\t%s\t\t\t\t\t", chan_name,
+ format_e3_cv (st.ccv, baud, st.cursec));
+
+ printf (" %s\n", format_e3_status (st.status));
+
+
+ if (uflag) {
+ /* Print total statistics. */
+ printf ("\t%s\t\t\t\t\t",
+ format_e3_cv (st.tcv, baud, st.totsec));
+ printf (" -- Total\n");
+
+ /* Print 24-hour history. */
+ maxi = (st.totsec - st.cursec) / 900;
+ if (maxi > 48)
+ maxi = 48;
+ for (i=0; i<maxi; ++i) {
+ printf ("\t%s\t\t\t\t\t",
+ format_e3_cv (st.icv[i], baud, 15*60));
+ if (i < 3)
+ printf (" -- %2dm\n", (i+1)*15);
+ else
+ printf (" -- %2dh %2dm\n", (i+1)/4, (i+1)%4*15);
+ }
+ }
+}
+
+static void
+print_chan (int fd)
+{
+ char protocol [8];
+ char cfg;
+ int loop, dpll, nrzi, invclk, clk, higain, phony, use16, crc4;
+ int level, keepalive, debug, port, invrclk, invtclk, unfram, monitor;
+ int cable, dir, scrambler, ami, mtu;
+ int cablen, rloop, rqlen;
+ long baud, timeslots, subchan;
+ int protocol_valid, baud_valid, loop_valid, use16_valid, crc4_valid;
+ int dpll_valid, nrzi_valid, invclk_valid, clk_valid, phony_valid;
+ int timeslots_valid, subchan_valid, higain_valid, level_valid;
+ int keepalive_valid, debug_valid, cfg_valid, port_valid;
+ int invrclk_valid, invtclk_valid, unfram_valid, monitor_valid;
+ int cable_valid, dir_valid, scrambler_valid, ami_valid, mtu_valid;
+ int cablen_valid, rloop_valid, rqlen_valid;
+
+ protocol_valid = ioctl (fd, SERIAL_GETPROTO, &protocol) >= 0;
+ cfg_valid = ioctl (fd, SERIAL_GETCFG, &cfg) >= 0;
+ baud_valid = ioctl (fd, SERIAL_GETBAUD, &baud) >= 0;
+ loop_valid = ioctl (fd, SERIAL_GETLOOP, &loop) >= 0;
+ dpll_valid = ioctl (fd, SERIAL_GETDPLL, &dpll) >= 0;
+ nrzi_valid = ioctl (fd, SERIAL_GETNRZI, &nrzi) >= 0;
+ invclk_valid = ioctl (fd, SERIAL_GETINVCLK, &invclk) >= 0;
+ invrclk_valid = ioctl (fd, SERIAL_GETINVRCLK, &invrclk) >= 0;
+ invtclk_valid = ioctl (fd, SERIAL_GETINVTCLK, &invtclk) >= 0;
+ clk_valid = ioctl (fd, SERIAL_GETCLK, &clk) >= 0;
+ timeslots_valid = ioctl (fd, SERIAL_GETTIMESLOTS, &timeslots) >= 0;
+ subchan_valid = ioctl (fd, SERIAL_GETSUBCHAN, &subchan) >= 0;
+ higain_valid = ioctl (fd, SERIAL_GETHIGAIN, &higain) >= 0;
+ phony_valid = ioctl (fd, SERIAL_GETPHONY, &phony) >= 0;
+ unfram_valid = ioctl (fd, SERIAL_GETUNFRAM, &unfram) >= 0;
+ monitor_valid = ioctl (fd, SERIAL_GETMONITOR, &monitor) >= 0;
+ use16_valid = ioctl (fd, SERIAL_GETUSE16, &use16) >= 0;
+ crc4_valid = ioctl (fd, SERIAL_GETCRC4, &crc4) >= 0;
+ ami_valid = ioctl (fd, SERIAL_GETLCODE, &ami) >= 0;
+ level_valid = ioctl (fd, SERIAL_GETLEVEL, &level) >= 0;
+ keepalive_valid = ioctl (fd, SERIAL_GETKEEPALIVE, &keepalive) >= 0;
+ debug_valid = ioctl (fd, SERIAL_GETDEBUG, &debug) >= 0;
+ port_valid = ioctl (fd, SERIAL_GETPORT, &port) >= 0;
+ cable_valid = ioctl (fd, SERIAL_GETCABLE, &cable) >= 0;
+ dir_valid = ioctl (fd, SERIAL_GETDIR, &dir) >= 0;
+ scrambler_valid = ioctl (fd, SERIAL_GETSCRAMBLER, &scrambler) >= 0;
+ cablen_valid = ioctl (fd, SERIAL_GETCABLEN, &cablen) >= 0;
+ rloop_valid = ioctl (fd, SERIAL_GETRLOOP, &rloop) >= 0;
+ mtu_valid = ioctl (fd, SERIAL_GETMTU, &mtu) >= 0;
+ rqlen_valid = ioctl (fd, SERIAL_GETRQLEN, &rqlen) >= 0;
+
+ printf ("%s", chan_name);
+ if (port_valid)
+ switch (port) {
+ case 0: printf (" (rs232)"); break;
+ case 1: printf (" (v35)"); break;
+ case 2: printf (" (rs530)"); break;
+ }
+ else if (cable_valid)
+ switch (cable) {
+ case 0: printf (" (rs232)"); break;
+ case 1: printf (" (v35)"); break;
+ case 2: printf (" (rs530)"); break;
+ case 3: printf (" (x21)"); break;
+ case 4: printf (" (rs485)"); break;
+ case 9: printf (" (no cable)"); break;
+ }
+ if (debug_valid && debug)
+ printf (" debug=%d", debug);
+ if (protocol_valid && *protocol)
+ printf (" %.8s", protocol);
+ else
+ printf (" idle");
+ if (cablen_valid)
+ printf (" cablen=%s", cablen ? "on" : "off");
+ if (keepalive_valid)
+ printf (" keepalive=%s", keepalive ? "on" : "off");
+
+ if (cfg_valid)
+ switch (cfg) {
+ case 'a' : printf (" cfg=A"); break;
+ case 'b' : printf (" cfg=B"); break;
+ case 'c' : printf (" cfg=C"); break;
+ case 'd' : printf (" cfg=D"); break;
+ default : printf (" cfg=unknown");
+ }
+ if (dir_valid)
+ printf (" dir=%d", dir);
+
+ if (baud_valid) {
+ if (baud)
+ printf (" %ld", baud);
+ else
+ printf (" extclock");
+ }
+ if (mtu_valid)
+ printf (" mtu=%d", mtu);
+
+ if (aflag && rqlen_valid)
+ printf (" rqlen=%d", rqlen);
+
+ if (clk_valid)
+ switch (clk) {
+ case E1CLK_INTERNAL: printf (" syn=int"); break;
+ case E1CLK_RECEIVE: printf (" syn=rcv"); break;
+ case E1CLK_RECEIVE_CHAN0: printf (" syn=rcv0"); break;
+ case E1CLK_RECEIVE_CHAN1: printf (" syn=rcv1"); break;
+ case E1CLK_RECEIVE_CHAN2: printf (" syn=rcv2"); break;
+ case E1CLK_RECEIVE_CHAN3: printf (" syn=rcv3"); break;
+ default: printf (" syn=%d", clk); break;
+ }
+
+ if (dpll_valid)
+ printf (" dpll=%s", dpll ? "on" : "off");
+ if (nrzi_valid)
+ printf (" nrzi=%s", nrzi ? "on" : "off");
+ if (invclk_valid)
+ printf (" invclk=%s", invclk ? "on" : "off");
+ if (invrclk_valid)
+ printf (" invrclk=%s", invrclk ? "on" : "off");
+ if (invtclk_valid)
+ printf (" invtclk=%s", invtclk ? "on" : "off");
+ if (unfram_valid)
+ printf (" unfram=%s", unfram ? "on" : "off");
+ if (use16_valid)
+ printf (" use16=%s", use16 ? "on" : "off");
+ if (aflag) {
+ if (crc4_valid)
+ printf (" crc4=%s", crc4 ? "on" : "off");
+ if (higain_valid)
+ printf (" higain=%s", higain ? "on" : "off");
+ if (monitor_valid)
+ printf (" monitor=%s", monitor ? "on" : "off");
+ if (phony_valid)
+ printf (" phony=%s", phony ? "on" : "off");
+ if (scrambler_valid)
+ printf (" scrambler=%s", scrambler ? "on" : "off");
+ if (loop_valid)
+ printf (" loop=%s", loop ? "on" : "off");
+ if (rloop_valid)
+ printf (" rloop=%s", rloop ? "on" : "off");
+ if (ami_valid)
+ printf (" ami=%s", ami ? "on" : "off");
+ }
+ if (timeslots_valid)
+ printf (" ts=%s", format_timeslots (timeslots));
+ if (subchan_valid)
+ printf (" pass=%s", format_timeslots (subchan));
+ if (level_valid)
+ printf (" (level=-%.1fdB)", level / 10.0);
+ printf ("\n");
+}
+
+static void
+setup_chan (int fd, int argc, char **argv)
+{
+ int i, mode, loop, nrzi, dpll, invclk, phony, use16, crc4, unfram, ami;
+ int higain, clk, keepalive, debug, port, dlci, invrclk, invtclk;
+ int monitor, dir, scrambler, rloop, cablen;
+ long baud, timeslots, mtu, rqlen;
+
+ for (i=0; i<argc; ++i) {
+ if (argv[i][0] >= '0' && argv[i][0] <= '9') {
+ baud = strtol (argv[i], 0, 10);
+ ioctl (fd, SERIAL_SETBAUD, &baud);
+ } else if (strcasecmp ("extclock", argv[i]) == 0) {
+ baud = 0;
+ ioctl (fd, SERIAL_SETBAUD, &baud);
+ } else if (strncasecmp ("cfg=", argv[i], 4) == 0) {
+ if (strncasecmp ("a", argv[i]+4, 1) == 0)
+ ioctl (fd, SERIAL_SETCFG, "a");
+ else if (strncasecmp ("b", argv[i]+4, 1) == 0)
+ ioctl (fd, SERIAL_SETCFG, "b");
+ else if (strncasecmp ("c", argv[i]+4, 1) == 0)
+ ioctl (fd, SERIAL_SETCFG, "c");
+ else if (strncasecmp ("d", argv[i]+4, 1) == 0)
+ ioctl (fd, SERIAL_SETCFG, "d");
+ else {
+ fprintf (stderr, "invalid cfg\n");
+ exit (-1);
+ }
+ } else if (strcasecmp ("idle", argv[i]) == 0)
+ ioctl (fd, SERIAL_SETPROTO, "\0\0\0\0\0\0\0");
+ else if (strcasecmp ("async", argv[i]) == 0) {
+ mode = SERIAL_ASYNC;
+ if (ioctl (fd, SERIAL_SETMODE, &mode) >= 0)
+ ioctl (fd, SERIAL_SETPROTO, "async\0\0");
+ } else if (strcasecmp ("sync", argv[i]) == 0) {
+ mode = SERIAL_HDLC;
+ if (ioctl (fd, SERIAL_SETMODE, &mode) >= 0)
+ ioctl (fd, SERIAL_SETPROTO, "sync\0\0\0");
+ } else if (strcasecmp ("cisco", argv[i]) == 0) {
+ mode = SERIAL_HDLC;
+ ioctl (fd, SERIAL_SETMODE, &mode);
+ ioctl (fd, SERIAL_SETPROTO, "cisco\0\0");
+ } else if (strcasecmp ("rbrg", argv[i]) == 0) {
+ mode = SERIAL_HDLC;
+ ioctl (fd, SERIAL_SETMODE, &mode);
+ ioctl (fd, SERIAL_SETPROTO, "rbrg\0\0\0");
+ } else if (strcasecmp ("raw", argv[i]) == 0) {
+ mode = SERIAL_HDLC;
+ ioctl (fd, SERIAL_SETMODE, &mode);
+ ioctl (fd, SERIAL_SETPROTO, "raw\0\0\0\0");
+ } else if (strcasecmp ("packet", argv[i]) == 0) {
+ mode = SERIAL_HDLC;
+ ioctl (fd, SERIAL_SETMODE, &mode);
+ ioctl (fd, SERIAL_SETPROTO, "packet\0");
+ } else if (strcasecmp ("ppp", argv[i]) == 0) {
+ /* check that ppp line discipline is present */
+ if (ppp_ok ()) {
+ mode = SERIAL_HDLC;
+ ioctl (fd, SERIAL_SETMODE, &mode);
+ ioctl (fd, SERIAL_SETPROTO, "ppp\0\0\0\0");
+ }
+ } else if (strncasecmp ("keepalive=", argv[i], 10) == 0) {
+ keepalive = (strcasecmp ("on", argv[i] + 10) == 0);
+ ioctl (fd, SERIAL_SETKEEPALIVE, &keepalive);
+ } else if (strcasecmp ("fr", argv[i]) == 0) {
+ mode = SERIAL_HDLC;
+ ioctl (fd, SERIAL_SETMODE, &mode);
+ ioctl (fd, SERIAL_SETPROTO, "fr\0\0\0\0\0");
+ } else if (strcasecmp ("zaptel", argv[i]) == 0) {
+ mode = SERIAL_HDLC;
+ ioctl (fd, SERIAL_SETMODE, &mode);
+ ioctl (fd, SERIAL_SETPROTO, "zaptel\0");
+ } else if (strncasecmp ("debug=", argv[i], 6) == 0) {
+ debug = strtol (argv[i]+6, 0, 10);
+ ioctl (fd, SERIAL_SETDEBUG, &debug);
+ } else if (strncasecmp ("loop=", argv[i], 5) == 0) {
+ loop = (strcasecmp ("on", argv[i] + 5) == 0);
+ ioctl (fd, SERIAL_SETLOOP, &loop);
+ } else if (strncasecmp ("rloop=", argv[i], 6) == 0) {
+ rloop = (strcasecmp ("on", argv[i] + 6) == 0);
+ ioctl (fd, SERIAL_SETRLOOP, &rloop);
+ } else if (strncasecmp ("dpll=", argv[i], 5) == 0) {
+ dpll = (strcasecmp ("on", argv[i] + 5) == 0);
+ ioctl (fd, SERIAL_SETDPLL, &dpll);
+ } else if (strncasecmp ("nrzi=", argv[i], 5) == 0) {
+ nrzi = (strcasecmp ("on", argv[i] + 5) == 0);
+ ioctl (fd, SERIAL_SETNRZI, &nrzi);
+ } else if (strncasecmp ("invclk=", argv[i], 7) == 0) {
+ invclk = (strcasecmp ("on", argv[i] + 7) == 0);
+ ioctl (fd, SERIAL_SETINVCLK, &invclk);
+ } else if (strncasecmp ("invrclk=", argv[i], 8) == 0) {
+ invrclk = (strcasecmp ("on", argv[i] + 8) == 0);
+ ioctl (fd, SERIAL_SETINVRCLK, &invrclk);
+ } else if (strncasecmp ("invtclk=", argv[i], 8) == 0) {
+ invtclk = (strcasecmp ("on", argv[i] + 8) == 0);
+ ioctl (fd, SERIAL_SETINVTCLK, &invtclk);
+ } else if (strncasecmp ("higain=", argv[i], 7) == 0) {
+ higain = (strcasecmp ("on", argv[i] + 7) == 0);
+ ioctl (fd, SERIAL_SETHIGAIN, &higain);
+ } else if (strncasecmp ("phony=", argv[i], 6) == 0) {
+ phony = (strcasecmp ("on", argv[i] + 6) == 0);
+ ioctl (fd, SERIAL_SETPHONY, &phony);
+ } else if (strncasecmp ("unfram=", argv[i], 7) == 0) {
+ unfram = (strcasecmp ("on", argv[i] + 7) == 0);
+ ioctl (fd, SERIAL_SETUNFRAM, &unfram);
+ } else if (strncasecmp ("scrambler=", argv[i], 10) == 0) {
+ scrambler = (strcasecmp ("on", argv[i] + 10) == 0);
+ ioctl (fd, SERIAL_SETSCRAMBLER, &scrambler);
+ } else if (strncasecmp ("monitor=", argv[i], 8) == 0) {
+ monitor = (strcasecmp ("on", argv[i] + 8) == 0);
+ ioctl (fd, SERIAL_SETMONITOR, &monitor);
+ } else if (strncasecmp ("use16=", argv[i], 6) == 0) {
+ use16 = (strcasecmp ("on", argv[i] + 6) == 0);
+ ioctl (fd, SERIAL_SETUSE16, &use16);
+ } else if (strncasecmp ("crc4=", argv[i], 5) == 0) {
+ crc4 = (strcasecmp ("on", argv[i] + 5) == 0);
+ ioctl (fd, SERIAL_SETCRC4, &crc4);
+ } else if (strncasecmp ("ami=", argv[i], 4) == 0) {
+ ami = (strcasecmp ("on", argv[i] + 4) == 0);
+ ioctl (fd, SERIAL_SETLCODE, &ami);
+ } else if (strncasecmp ("mtu=", argv[i], 4) == 0) {
+ mtu = strtol (argv[i] + 4, 0, 10);
+ ioctl (fd, SERIAL_SETMTU, &mtu);
+ } else if (strncasecmp ("rqlen=", argv[i], 6) == 0) {
+ rqlen = strtol (argv[i] + 6, 0, 10);
+ ioctl (fd, SERIAL_SETRQLEN, &rqlen);
+ } else if (strcasecmp ("syn=int", argv[i]) == 0) {
+ clk = E1CLK_INTERNAL;
+ ioctl (fd, SERIAL_SETCLK, &clk);
+ } else if (strcasecmp ("syn=rcv", argv[i]) == 0) {
+ clk = E1CLK_RECEIVE;
+ ioctl (fd, SERIAL_SETCLK, &clk);
+ } else if (strcasecmp ("syn=rcv0", argv[i]) == 0) {
+ clk = E1CLK_RECEIVE_CHAN0;
+ ioctl (fd, SERIAL_SETCLK, &clk);
+ } else if (strcasecmp ("syn=rcv1", argv[i]) == 0) {
+ clk = E1CLK_RECEIVE_CHAN1;
+ ioctl (fd, SERIAL_SETCLK, &clk);
+ } else if (strcasecmp ("syn=rcv2", argv[i]) == 0) {
+ clk = E1CLK_RECEIVE_CHAN2;
+ ioctl (fd, SERIAL_SETCLK, &clk);
+ } else if (strcasecmp ("syn=rcv3", argv[i]) == 0) {
+ clk = E1CLK_RECEIVE_CHAN3;
+ ioctl (fd, SERIAL_SETCLK, &clk);
+ } else if (strncasecmp ("ts=", argv[i], 3) == 0) {
+ timeslots = scan_timeslots (argv[i] + 3);
+ ioctl (fd, SERIAL_SETTIMESLOTS, &timeslots);
+ } else if (strncasecmp ("pass=", argv[i], 5) == 0) {
+ timeslots = scan_timeslots (argv[i] + 5);
+ ioctl (fd, SERIAL_SETSUBCHAN, &timeslots);
+ } else if (strncasecmp ("dlci", argv[i], 4) == 0) {
+ dlci = strtol (argv[i]+4, 0, 10);
+ ioctl (fd, SERIAL_ADDDLCI, &dlci);
+ } else if (strncasecmp ("dir=", argv[i], 4) == 0) {
+ dir = strtol (argv[i]+4, 0, 10);
+ ioctl (fd, SERIAL_SETDIR, &dir);
+ } else if (strncasecmp ("port=", argv[i], 5) == 0) {
+ if (strncasecmp ("rs232", argv[i]+5, 5) == 0) {
+ port = 0;
+ ioctl (fd, SERIAL_SETPORT, &port);
+ } else if (strncasecmp ("v35", argv[i]+5, 3) == 0) {
+ port = 1;
+ ioctl (fd, SERIAL_SETPORT, &port);
+ } else if (strncasecmp ("rs449", argv[i]+5, 5) == 0) {
+ port = 2;
+ ioctl (fd, SERIAL_SETPORT, &port);
+ } else
+ fprintf (stderr, "invalid port type\n");
+ exit (-1);
+#if 1
+ } else if (strcasecmp ("reset", argv[i]) == 0) {
+ ioctl (fd, SERIAL_RESET, 0);
+ } else if (strcasecmp ("hwreset", argv[i]) == 0) {
+ ioctl (fd, SERIAL_HARDRESET, 0);
+#endif
+ } else if (strncasecmp ("cablen=", argv[i], 7) == 0) {
+ loop = (strcasecmp ("on", argv[i] + 7) == 0);
+ ioctl (fd, SERIAL_SETCABLEN, &cablen);
+ }
+ }
+}
+
+static void
+get_mask (void)
+{
+#ifdef __linux__
+ int fd;
+
+ fd = open ("/dev/serial/ctl0", 0);
+ if (fd < 0) {
+ perror ("/dev/serial/ctl0");
+ exit (-1);
+ }
+ if (ioctl (fd, SERIAL_GETREGISTERED, &mask) < 0) {
+ perror ("getting list of channels");
+ exit (-1);
+ }
+ close (fd);
+#else
+ int fd, fd1, fd2, fd3, i;
+ char buf [80];
+
+ for (i=0, fd=-1; i<12 && fd<0; i++) {
+ sprintf (buf, "/dev/cx%d", i*4);
+ fd = open (buf, 0);
+ }
+
+ for (i=0, fd1=-1; i<3 && fd1<0; i++) {
+ sprintf (buf, "/dev/ct%d", i*2);
+ fd1 = open (buf, 0);
+ }
+
+ for (i=0, fd2=-1; i<3 && fd2<0; i++) {
+ sprintf (buf, "/dev/cp%d", i*4);
+ fd2 = open (buf, 0);
+ }
+
+ /* Try only one */
+ for (i=0, fd3=-1; i<1 && fd3<0; i++) {
+ sprintf (buf, "/dev/ce%d", i*4);
+ fd3 = open (buf, 0);
+ }
+
+ if ((fd < 0) && (fd1 < 0) && (fd2 < 0) && (fd3 < 0)) {
+ fprintf (stderr, "No Cronyx adapters installed\n");
+ exit (-1);
+ }
+
+ if (fd >= 0) {
+ if (ioctl (fd, SERIAL_GETREGISTERED, &mask) < 0) {
+ perror ("getting list of channels");
+ exit (-1);
+ }
+ close (fd);
+ }
+
+ if (fd1 >= 0) {
+ if (ioctl (fd1, SERIAL_GETREGISTERED, (mask+16)) < 0) {
+ perror ("getting list of channels");
+ exit (-1);
+ }
+ close (fd1);
+ }
+
+ if (fd2 >= 0) {
+ if (ioctl (fd2, SERIAL_GETREGISTERED, (mask+32)) < 0) {
+ perror ("getting list of channels");
+ exit (-1);
+ }
+ close (fd2);
+ }
+
+ if (fd3 >= 0) {
+ if (ioctl (fd3, SERIAL_GETREGISTERED, (mask+48)) < 0) {
+ perror ("getting list of channels");
+ exit (-1);
+ }
+ close (fd3);
+ }
+#endif
+}
+
+static int
+open_chan_ctl (int num)
+{
+ char device [80];
+ int fd;
+
+#ifdef __linux__
+ sprintf (device, "/dev/serial/ctl%d", num);
+#else
+ switch (adapter_type) {
+ case 0:
+ sprintf (device, "/dev/cx%d", num);
+ break;
+ case 1:
+ sprintf (device, "/dev/ct%d", num);
+ break;
+ case 2:
+ sprintf (device, "/dev/cp%d", num);
+ break;
+ case 3:
+ sprintf (device, "/dev/ce%d", num);
+ break;
+ }
+#endif
+ fd = open (device, 0);
+ if (fd < 0) {
+ if (errno == ENODEV)
+ fprintf (stderr, "chan%d: not configured\n", num);
+ else
+ perror (device);
+ exit (-1);
+ }
+#ifdef __linux__
+ if (ioctl (fd, SERIAL_GETNAME, &chan_name) < 0)
+ sprintf (chan_name, "chan%d", num);
+#else
+ switch (adapter_type) {
+ case 0: sprintf (chan_name, "cx%d", num); break;
+ case 1: sprintf (chan_name, "ct%d", num); break;
+ case 2: sprintf (chan_name, "cp%d", num); break;
+ case 3: sprintf (chan_name, "ce%d", num); break;
+ }
+#endif
+ return fd;
+}
+
+int
+main (int argc, char **argv)
+{
+ char *p;
+ int fd, need_header, chan_num;
+
+ if (argc > 1 && strcmp(argv[1], "help") == 0)
+ usage();
+
+ for (;;) {
+ switch (getopt (argc, argv, "mseftucviax")) {
+ case EOF:
+ break;
+ case 'a':
+ ++aflag;
+ continue;
+ case 'm':
+ ++mflag;
+ continue;
+ case 's':
+ ++sflag;
+ continue;
+ case 'e':
+ ++eflag;
+ continue;
+ case 'f':
+ ++eflag;
+ ++fflag;
+ continue;
+ case 't':
+ ++tflag;
+ continue;
+ case 'u':
+ ++tflag;
+ ++uflag;
+ continue;
+ case 'c':
+ ++cflag;
+ continue;
+ case 'v':
+ ++vflag;
+ continue;
+ case 'i':
+ ++iflag;
+ continue;
+ case 'x':
+ ++xflag;
+ continue;
+ default:
+ usage();
+ }
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc <= 0) {
+ get_mask ();
+ need_header = 1;
+ adapter_type = 0;
+#ifndef __linux__
+ for (; adapter_type < 4; ++adapter_type)
+#endif
+ {
+ for (chan_num=0; chan_num<MAXCHAN; ++chan_num)
+ if (mask[adapter_type*16+chan_num/8] & 1 << (chan_num & 7)) {
+ fd = open_chan_ctl (chan_num);
+ if (vflag) {
+#ifdef __linux__
+ char buf[256];
+ if (ioctl (fd, SERIAL_GETVERSIONSTRING, &buf) >= 0) {
+ printf ("Version: %s\n", buf);
+ close (fd);
+ return (0);
+ }
+#endif
+ }
+ if (iflag) {
+ print_chan (fd);
+ print_ifconfig (fd);
+ } else if (sflag||xflag)
+ print_stats (fd, need_header);
+ else if (mflag)
+ print_modems (fd, need_header);
+ else if (eflag)
+ print_e1_stats (fd, need_header);
+ else if (tflag)
+ print_e3_stats (fd, need_header);
+ else if (cflag)
+ clear_stats (fd);
+ else
+ print_chan (fd);
+ close (fd);
+ need_header = 0;
+ }
+ }
+ return (0);
+ }
+
+ p = argv[0] + strlen (argv[0]);
+ while (p > argv[0] && p[-1] >= '0' && p[-1] <= '9')
+ --p;
+ chan_num = strtol (p, 0, 10);
+#ifndef __linux__
+ if (strncasecmp ("cx", argv[0], 2)==0)
+ adapter_type = 0;
+ else if (strncasecmp ("ct", argv[0], 2)==0)
+ adapter_type = 1;
+ else if (strncasecmp ("cp", argv[0], 2)==0)
+ adapter_type = 2;
+ else if (strncasecmp ("ce", argv[0], 2)==0)
+ adapter_type = 3;
+ else {
+ fprintf (stderr, "Wrong channel name\n");
+ exit (-1);
+ }
+#endif
+ argc--;
+ argv++;
+
+ fd = open_chan_ctl (chan_num);
+ if (vflag) {
+#ifdef __linux__
+ char buf[256];
+ if (ioctl (fd, SERIAL_GETVERSIONSTRING, &buf) >= 0)
+ printf ("Version: %s\n", buf);
+#endif
+ }
+ if (iflag) {
+ print_chan (fd);
+ print_ifconfig (fd);
+ close (fd);
+ return (0);
+ }
+ if (sflag||xflag) {
+ print_stats (fd, 1);
+ close (fd);
+ return (0);
+ }
+ if (mflag) {
+ print_modems (fd, 1);
+ close (fd);
+ return (0);
+ }
+ if (eflag) {
+ print_e1_stats (fd, 1);
+ close (fd);
+ return (0);
+ }
+ if (tflag) {
+ print_e3_stats (fd, 1);
+ close (fd);
+ return (0);
+ }
+ if (cflag) {
+ clear_stats (fd);
+ close (fd);
+ return (0);
+ }
+ if (argc > 0)
+ setup_chan (fd, argc, argv);
+ else
+ print_chan (fd);
+ close (fd);
+ return (0);
+}
diff --git a/sbin/setkey/Makefile b/sbin/setkey/Makefile
new file mode 100644
index 0000000..794d6dd
--- /dev/null
+++ b/sbin/setkey/Makefile
@@ -0,0 +1,64 @@
+# Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE 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.
+# 3. Neither the name of the project 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 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.
+#
+# $FreeBSD$
+
+PROG= setkey
+MAN= setkey.8
+SRCS= setkey.c parse.y token.l
+
+CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../../lib/libipsec
+YFLAGS= -d
+
+DPADD= ${LIBL} ${LIBY}
+LDADD= -ll -ly
+
+CLEANFILES= y.tab.c y.tab.h key_test.o keytest
+
+# libpfkey
+# ipsec_strerror.c is for avoiding shlib reference to non-exported function.
+.PATH: ${.CURDIR}/../../lib/libipsec ${.CURDIR}/../../sys/netkey
+SRCS+= pfkey.c pfkey_dump.c key_debug.c ipsec_strerror.c
+CFLAGS+= -I${.CURDIR}/../../lib/libipsec -I${.CURDIR}/../../sys/netkey
+
+SRCS+= y.tab.h
+y.tab.h: parse.y
+CFLAGS+= -DIPSEC_DEBUG -DINET6 -DYY_NO_UNPUT -I.
+DPADD+= ${LIBIPSEC}
+LDADD+= -lipsec
+CLEANFILES+= scriptdump y.tab.h
+
+#SCRIPTS= scriptdump
+
+LOCALPREFIX= /usr
+
+WARNS= 1
+
+scriptdump: scriptdump.pl
+ sed -e 's#@LOCALPREFIX@#${LOCALPREFIX}#' < $> > scriptdump
+
+.include <bsd.prog.mk>
diff --git a/sbin/setkey/parse.y b/sbin/setkey/parse.y
new file mode 100644
index 0000000..e90c3a1
--- /dev/null
+++ b/sbin/setkey/parse.y
@@ -0,0 +1,1267 @@
+/* $FreeBSD$ */
+/* $KAME: parse.y,v 1.83 2004/05/18 08:48:23 sakane Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE 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.
+ * 3. Neither the name of the project 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 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.
+ */
+
+%{
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <net/route.h>
+#include <netinet/in.h>
+#include <net/pfkeyv2.h>
+#include <netkey/key_var.h>
+#include <netinet6/ipsec.h>
+#include <arpa/inet.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "libpfkey.h"
+#include "vchar.h"
+
+#define ATOX(c) \
+ (isdigit(c) ? (c - '0') : (isupper(c) ? (c - 'A' + 10) : (c - 'a' + 10)))
+
+u_int32_t p_spi;
+u_int p_ext, p_alg_enc, p_alg_auth, p_replay, p_mode;
+u_int32_t p_reqid;
+u_int p_key_enc_len, p_key_auth_len;
+caddr_t p_key_enc, p_key_auth;
+time_t p_lt_hard, p_lt_soft;
+
+static int p_aiflags = 0, p_aifamily = PF_UNSPEC;
+
+static struct addrinfo *parse_addr __P((char *, char *));
+static int fix_portstr __P((vchar_t *, vchar_t *, vchar_t *));
+static int setvarbuf __P((char *, int *, struct sadb_ext *, int, caddr_t, int));
+void parse_init __P((void));
+void free_buffer __P((void));
+
+int setkeymsg0 __P((struct sadb_msg *, unsigned int, unsigned int, size_t));
+static int setkeymsg_spdaddr __P((unsigned int, unsigned int, vchar_t *,
+ struct addrinfo *, int, struct addrinfo *, int));
+static int setkeymsg_addr __P((unsigned int, unsigned int,
+ struct addrinfo *, struct addrinfo *, int));
+static int setkeymsg_add __P((unsigned int, unsigned int,
+ struct addrinfo *, struct addrinfo *));
+extern int setkeymsg __P((char *, size_t *));
+extern int sendkeymsg __P((char *, size_t));
+
+extern int yylex __P((void));
+extern void yyfatal __P((const char *));
+extern void yyerror __P((const char *));
+%}
+
+%union {
+ int num;
+ unsigned long ulnum;
+ vchar_t val;
+ struct addrinfo *res;
+}
+
+%token EOT SLASH BLCL ELCL
+%token ADD GET DELETE DELETEALL FLUSH DUMP
+%token PR_ESP PR_AH PR_IPCOMP PR_TCP
+%token F_PROTOCOL F_AUTH F_ENC F_REPLAY F_COMP F_RAWCPI
+%token F_MODE MODE F_REQID
+%token F_EXT EXTENSION NOCYCLICSEQ
+%token ALG_AUTH ALG_AUTH_NOKEY
+%token ALG_ENC ALG_ENC_NOKEY ALG_ENC_DESDERIV ALG_ENC_DES32IV ALG_ENC_OLD
+%token ALG_COMP
+%token F_LIFETIME_HARD F_LIFETIME_SOFT
+%token DECSTRING QUOTEDSTRING HEXSTRING STRING ANY
+ /* SPD management */
+%token SPDADD SPDDELETE SPDDUMP SPDFLUSH
+%token F_POLICY PL_REQUESTS
+%token F_AIFLAGS
+%token TAGGED
+
+%type <num> prefix protocol_spec upper_spec
+%type <num> ALG_ENC ALG_ENC_DESDERIV ALG_ENC_DES32IV ALG_ENC_OLD ALG_ENC_NOKEY
+%type <num> ALG_AUTH ALG_AUTH_NOKEY
+%type <num> ALG_COMP
+%type <num> PR_ESP PR_AH PR_IPCOMP PR_TCP
+%type <num> EXTENSION MODE
+%type <ulnum> DECSTRING
+%type <val> PL_REQUESTS portstr key_string
+%type <val> policy_requests
+%type <val> QUOTEDSTRING HEXSTRING STRING
+%type <val> F_AIFLAGS
+%type <val> upper_misc_spec policy_spec
+%type <res> ipaddr
+
+%%
+commands
+ : /*NOTHING*/
+ | commands command
+ {
+ free_buffer();
+ parse_init();
+ }
+ ;
+
+command
+ : add_command
+ | get_command
+ | delete_command
+ | deleteall_command
+ | flush_command
+ | dump_command
+ | spdadd_command
+ | spddelete_command
+ | spddump_command
+ | spdflush_command
+ ;
+ /* commands concerned with management, there is in tail of this file. */
+
+ /* add command */
+add_command
+ : ADD ipaddropts ipaddr ipaddr protocol_spec spi extension_spec algorithm_spec EOT
+ {
+ int status;
+
+ status = setkeymsg_add(SADB_ADD, $5, $3, $4);
+ if (status < 0)
+ return -1;
+ }
+ ;
+
+ /* delete */
+delete_command
+ : DELETE ipaddropts ipaddr ipaddr protocol_spec spi extension_spec EOT
+ {
+ int status;
+
+ if ($3->ai_next || $4->ai_next) {
+ yyerror("multiple address specified");
+ return -1;
+ }
+ if (p_mode != IPSEC_MODE_ANY)
+ yyerror("WARNING: mode is obsolete");
+
+ status = setkeymsg_addr(SADB_DELETE, $5, $3, $4, 0);
+ if (status < 0)
+ return -1;
+ }
+ ;
+
+ /* deleteall command */
+deleteall_command
+ : DELETEALL ipaddropts ipaddr ipaddr protocol_spec EOT
+ {
+ int status;
+
+ status = setkeymsg_addr(SADB_DELETE, $5, $3, $4, 1);
+ if (status < 0)
+ return -1;
+ }
+ ;
+
+ /* get command */
+get_command
+ : GET ipaddropts ipaddr ipaddr protocol_spec spi extension_spec EOT
+ {
+ int status;
+
+ if (p_mode != IPSEC_MODE_ANY)
+ yyerror("WARNING: mode is obsolete");
+
+ status = setkeymsg_addr(SADB_GET, $5, $3, $4, 0);
+ if (status < 0)
+ return -1;
+ }
+ ;
+
+ /* flush */
+flush_command
+ : FLUSH protocol_spec EOT
+ {
+ struct sadb_msg msg;
+ setkeymsg0(&msg, SADB_FLUSH, $2, sizeof(msg));
+ sendkeymsg((char *)&msg, sizeof(msg));
+ }
+ ;
+
+ /* dump */
+dump_command
+ : DUMP protocol_spec EOT
+ {
+ struct sadb_msg msg;
+ setkeymsg0(&msg, SADB_DUMP, $2, sizeof(msg));
+ sendkeymsg((char *)&msg, sizeof(msg));
+ }
+ ;
+
+protocol_spec
+ : /*NOTHING*/
+ {
+ $$ = SADB_SATYPE_UNSPEC;
+ }
+ | PR_ESP
+ {
+ $$ = SADB_SATYPE_ESP;
+ if ($1 == 1)
+ p_ext |= SADB_X_EXT_OLD;
+ else
+ p_ext &= ~SADB_X_EXT_OLD;
+ }
+ | PR_AH
+ {
+ $$ = SADB_SATYPE_AH;
+ if ($1 == 1)
+ p_ext |= SADB_X_EXT_OLD;
+ else
+ p_ext &= ~SADB_X_EXT_OLD;
+ }
+ | PR_IPCOMP
+ {
+ $$ = SADB_X_SATYPE_IPCOMP;
+ }
+ | PR_TCP
+ {
+ $$ = SADB_X_SATYPE_TCPSIGNATURE;
+ }
+ ;
+
+spi
+ : DECSTRING { p_spi = $1; }
+ | HEXSTRING
+ {
+ char *ep;
+ unsigned long v;
+
+ ep = NULL;
+ v = strtoul($1.buf, &ep, 16);
+ if (!ep || *ep) {
+ yyerror("invalid SPI");
+ return -1;
+ }
+ if (v & ~0xffffffff) {
+ yyerror("SPI too big.");
+ return -1;
+ }
+
+ p_spi = v;
+ }
+ ;
+
+algorithm_spec
+ : esp_spec
+ | ah_spec
+ | ipcomp_spec
+ ;
+
+esp_spec
+ : F_ENC enc_alg F_AUTH auth_alg
+ | F_ENC enc_alg
+ ;
+
+ah_spec
+ : F_AUTH auth_alg
+ ;
+
+ipcomp_spec
+ : F_COMP ALG_COMP
+ {
+ if ($2 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ p_alg_enc = $2;
+ }
+ | F_COMP ALG_COMP F_RAWCPI
+ {
+ if ($2 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ p_alg_enc = $2;
+ p_ext |= SADB_X_EXT_RAWCPI;
+ }
+ ;
+
+enc_alg
+ : ALG_ENC_NOKEY {
+ if ($1 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ p_alg_enc = $1;
+
+ p_key_enc_len = 0;
+ p_key_enc = NULL;
+ if (ipsec_check_keylen(SADB_EXT_SUPPORTED_ENCRYPT,
+ p_alg_enc, PFKEY_UNUNIT64(p_key_enc_len)) < 0) {
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+ }
+ | ALG_ENC key_string {
+ if ($1 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ p_alg_enc = $1;
+
+ p_key_enc_len = $2.len;
+ p_key_enc = $2.buf;
+ if (ipsec_check_keylen(SADB_EXT_SUPPORTED_ENCRYPT,
+ p_alg_enc, PFKEY_UNUNIT64(p_key_enc_len)) < 0) {
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+ }
+ | ALG_ENC_OLD {
+ if ($1 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ yyerror("WARNING: obsolete algorithm");
+ p_alg_enc = $1;
+
+ p_key_enc_len = 0;
+ p_key_enc = NULL;
+ if (ipsec_check_keylen(SADB_EXT_SUPPORTED_ENCRYPT,
+ p_alg_enc, PFKEY_UNUNIT64(p_key_enc_len)) < 0) {
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+ }
+ | ALG_ENC_DESDERIV key_string
+ {
+ if ($1 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ p_alg_enc = $1;
+ if (p_ext & SADB_X_EXT_OLD) {
+ yyerror("algorithm mismatched");
+ return -1;
+ }
+ p_ext |= SADB_X_EXT_DERIV;
+
+ p_key_enc_len = $2.len;
+ p_key_enc = $2.buf;
+ if (ipsec_check_keylen(SADB_EXT_SUPPORTED_ENCRYPT,
+ p_alg_enc, PFKEY_UNUNIT64(p_key_enc_len)) < 0) {
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+ }
+ | ALG_ENC_DES32IV key_string
+ {
+ if ($1 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ p_alg_enc = $1;
+ if (!(p_ext & SADB_X_EXT_OLD)) {
+ yyerror("algorithm mismatched");
+ return -1;
+ }
+ p_ext |= SADB_X_EXT_IV4B;
+
+ p_key_enc_len = $2.len;
+ p_key_enc = $2.buf;
+ if (ipsec_check_keylen(SADB_EXT_SUPPORTED_ENCRYPT,
+ p_alg_enc, PFKEY_UNUNIT64(p_key_enc_len)) < 0) {
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+ }
+ ;
+
+auth_alg
+ : ALG_AUTH key_string {
+ if ($1 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ p_alg_auth = $1;
+
+ p_key_auth_len = $2.len;
+ p_key_auth = $2.buf;
+
+ if (p_alg_auth == SADB_X_AALG_TCP_MD5) {
+ if ((p_key_auth_len < 1) || (p_key_auth_len >
+ 80))
+ return -1;
+ } else if (ipsec_check_keylen(SADB_EXT_SUPPORTED_AUTH,
+ p_alg_auth, PFKEY_UNUNIT64(p_key_auth_len)) < 0) {
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+ }
+ | ALG_AUTH_NOKEY {
+ if ($1 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ p_alg_auth = $1;
+
+ p_key_auth_len = 0;
+ p_key_auth = NULL;
+ }
+ ;
+
+key_string
+ : QUOTEDSTRING
+ {
+ $$ = $1;
+ }
+ | HEXSTRING
+ {
+ caddr_t pp_key;
+ caddr_t bp;
+ caddr_t yp = $1.buf;
+ int l;
+
+ l = strlen(yp) % 2 + strlen(yp) / 2;
+ if ((pp_key = malloc(l)) == 0) {
+ yyerror("not enough core");
+ return -1;
+ }
+ memset(pp_key, 0, l);
+
+ bp = pp_key;
+ if (strlen(yp) % 2) {
+ *bp = ATOX(yp[0]);
+ yp++, bp++;
+ }
+ while (*yp) {
+ *bp = (ATOX(yp[0]) << 4) | ATOX(yp[1]);
+ yp += 2, bp++;
+ }
+
+ $$.len = l;
+ $$.buf = pp_key;
+ }
+ ;
+
+extension_spec
+ : /*NOTHING*/
+ | extension_spec extension
+ ;
+
+extension
+ : F_EXT EXTENSION { p_ext |= $2; }
+ | F_EXT NOCYCLICSEQ { p_ext &= ~SADB_X_EXT_CYCSEQ; }
+ | F_MODE MODE { p_mode = $2; }
+ | F_MODE ANY { p_mode = IPSEC_MODE_ANY; }
+ | F_REQID DECSTRING { p_reqid = $2; }
+ | F_REPLAY DECSTRING
+ {
+ if ((p_ext & SADB_X_EXT_OLD) != 0) {
+ yyerror("replay prevention cannot be used with "
+ "ah/esp-old");
+ return -1;
+ }
+ p_replay = $2;
+ }
+ | F_LIFETIME_HARD DECSTRING { p_lt_hard = $2; }
+ | F_LIFETIME_SOFT DECSTRING { p_lt_soft = $2; }
+ ;
+
+ /* definition about command for SPD management */
+ /* spdadd */
+spdadd_command
+ : SPDADD ipaddropts STRING prefix portstr STRING prefix portstr upper_spec upper_misc_spec policy_spec EOT
+ {
+ int status;
+ struct addrinfo *src, *dst;
+
+ /* fixed port fields if ulp is icmpv6 */
+ if ($10.buf != NULL) {
+ if ($9 != IPPROTO_ICMPV6)
+ return -1;
+ free($5.buf);
+ free($8.buf);
+ if (fix_portstr(&$10, &$5, &$8))
+ return -1;
+ }
+
+ src = parse_addr($3.buf, $5.buf);
+ dst = parse_addr($6.buf, $8.buf);
+ if (!src || !dst) {
+ /* yyerror is already called */
+ return -1;
+ }
+ if (src->ai_next || dst->ai_next) {
+ yyerror("multiple address specified");
+ freeaddrinfo(src);
+ freeaddrinfo(dst);
+ return -1;
+ }
+
+ status = setkeymsg_spdaddr(SADB_X_SPDADD, $9, &$11,
+ src, $4, dst, $7);
+ freeaddrinfo(src);
+ freeaddrinfo(dst);
+ if (status < 0)
+ return -1;
+ }
+ | SPDADD TAGGED QUOTEDSTRING policy_spec EOT
+ {
+ return -1;
+ }
+ ;
+
+spddelete_command
+ : SPDDELETE ipaddropts STRING prefix portstr STRING prefix portstr upper_spec upper_misc_spec policy_spec EOT
+ {
+ int status;
+ struct addrinfo *src, *dst;
+
+ /* fixed port fields if ulp is icmpv6 */
+ if ($10.buf != NULL) {
+ if ($9 != IPPROTO_ICMPV6)
+ return -1;
+ free($5.buf);
+ free($8.buf);
+ if (fix_portstr(&$10, &$5, &$8))
+ return -1;
+ }
+
+ src = parse_addr($3.buf, $5.buf);
+ dst = parse_addr($6.buf, $8.buf);
+ if (!src || !dst) {
+ /* yyerror is already called */
+ return -1;
+ }
+ if (src->ai_next || dst->ai_next) {
+ yyerror("multiple address specified");
+ freeaddrinfo(src);
+ freeaddrinfo(dst);
+ return -1;
+ }
+
+ status = setkeymsg_spdaddr(SADB_X_SPDDELETE, $9, &$11,
+ src, $4, dst, $7);
+ freeaddrinfo(src);
+ freeaddrinfo(dst);
+ if (status < 0)
+ return -1;
+ }
+ ;
+
+spddump_command:
+ SPDDUMP EOT
+ {
+ struct sadb_msg msg;
+ setkeymsg0(&msg, SADB_X_SPDDUMP, SADB_SATYPE_UNSPEC,
+ sizeof(msg));
+ sendkeymsg((char *)&msg, sizeof(msg));
+ }
+ ;
+
+spdflush_command:
+ SPDFLUSH EOT
+ {
+ struct sadb_msg msg;
+ setkeymsg0(&msg, SADB_X_SPDFLUSH, SADB_SATYPE_UNSPEC,
+ sizeof(msg));
+ sendkeymsg((char *)&msg, sizeof(msg));
+ }
+ ;
+
+ipaddropts
+ : /* nothing */
+ | ipaddropts ipaddropt
+ ;
+
+ipaddropt
+ : F_AIFLAGS
+ {
+ char *p;
+
+ for (p = $1.buf + 1; *p; p++)
+ switch (*p) {
+ case '4':
+ p_aifamily = AF_INET;
+ break;
+#ifdef INET6
+ case '6':
+ p_aifamily = AF_INET6;
+ break;
+#endif
+ case 'n':
+ p_aiflags = AI_NUMERICHOST;
+ break;
+ default:
+ yyerror("invalid flag");
+ return -1;
+ }
+ }
+ ;
+
+ipaddr
+ : STRING
+ {
+ $$ = parse_addr($1.buf, NULL);
+ if ($$ == NULL) {
+ /* yyerror already called by parse_addr */
+ return -1;
+ }
+ }
+ ;
+
+prefix
+ : /*NOTHING*/ { $$ = -1; }
+ | SLASH DECSTRING { $$ = $2; }
+ ;
+
+portstr
+ : /*NOTHING*/
+ {
+ $$.buf = strdup("0");
+ if (!$$.buf) {
+ yyerror("insufficient memory");
+ return -1;
+ }
+ $$.len = strlen($$.buf);
+ }
+ | BLCL ANY ELCL
+ {
+ $$.buf = strdup("0");
+ if (!$$.buf) {
+ yyerror("insufficient memory");
+ return -1;
+ }
+ $$.len = strlen($$.buf);
+ }
+ | BLCL DECSTRING ELCL
+ {
+ char buf[20];
+ snprintf(buf, sizeof(buf), "%lu", $2);
+ $$.buf = strdup(buf);
+ if (!$$.buf) {
+ yyerror("insufficient memory");
+ return -1;
+ }
+ $$.len = strlen($$.buf);
+ }
+ | BLCL STRING ELCL
+ {
+ $$ = $2;
+ }
+ ;
+
+upper_spec
+ : DECSTRING { $$ = $1; }
+ | ANY { $$ = IPSEC_ULPROTO_ANY; }
+ | PR_TCP { $$ = IPPROTO_TCP; }
+ | STRING
+ {
+ struct protoent *ent;
+
+ ent = getprotobyname($1.buf);
+ if (ent)
+ $$ = ent->p_proto;
+ else {
+ if (strcmp("icmp6", $1.buf) == 0) {
+ $$ = IPPROTO_ICMPV6;
+ } else if(strcmp("ip4", $1.buf) == 0) {
+ $$ = IPPROTO_IPV4;
+ } else {
+ yyerror("invalid upper layer protocol");
+ return -1;
+ }
+ }
+ endprotoent();
+ }
+ ;
+
+upper_misc_spec
+ : /*NOTHING*/
+ {
+ $$.buf = NULL;
+ $$.len = 0;
+ }
+ | STRING
+ {
+ $$.buf = strdup($1.buf);
+ if (!$$.buf) {
+ yyerror("insufficient memory");
+ return -1;
+ }
+ $$.len = strlen($$.buf);
+ }
+ ;
+
+policy_spec
+ : F_POLICY policy_requests
+ {
+ char *policy;
+
+ policy = ipsec_set_policy($2.buf, $2.len);
+ if (policy == NULL) {
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+
+ $$.buf = policy;
+ $$.len = ipsec_get_policylen(policy);
+ }
+ ;
+
+policy_requests
+ : PL_REQUESTS { $$ = $1; }
+ ;
+
+%%
+
+int
+setkeymsg0(msg, type, satype, l)
+ struct sadb_msg *msg;
+ unsigned int type;
+ unsigned int satype;
+ size_t l;
+{
+
+ msg->sadb_msg_version = PF_KEY_V2;
+ msg->sadb_msg_type = type;
+ msg->sadb_msg_errno = 0;
+ msg->sadb_msg_satype = satype;
+ msg->sadb_msg_reserved = 0;
+ msg->sadb_msg_seq = 0;
+ msg->sadb_msg_pid = getpid();
+ msg->sadb_msg_len = PFKEY_UNIT64(l);
+ return 0;
+}
+
+/* XXX NO BUFFER OVERRUN CHECK! BAD BAD! */
+static int
+setkeymsg_spdaddr(type, upper, policy, srcs, splen, dsts, dplen)
+ unsigned int type;
+ unsigned int upper;
+ vchar_t *policy;
+ struct addrinfo *srcs;
+ int splen;
+ struct addrinfo *dsts;
+ int dplen;
+{
+ struct sadb_msg *msg;
+ char buf[BUFSIZ];
+ int l, l0;
+ struct sadb_address m_addr;
+ struct addrinfo *s, *d;
+ int n;
+ int plen;
+ struct sockaddr *sa;
+ int salen;
+
+ msg = (struct sadb_msg *)buf;
+
+ if (!srcs || !dsts)
+ return -1;
+
+ /* fix up length afterwards */
+ setkeymsg0(msg, type, SADB_SATYPE_UNSPEC, 0);
+ l = sizeof(struct sadb_msg);
+
+ memcpy(buf + l, policy->buf, policy->len);
+ l += policy->len;
+
+ l0 = l;
+ n = 0;
+
+ /* do it for all src/dst pairs */
+ for (s = srcs; s; s = s->ai_next) {
+ for (d = dsts; d; d = d->ai_next) {
+ /* rewind pointer */
+ l = l0;
+
+ if (s->ai_addr->sa_family != d->ai_addr->sa_family)
+ continue;
+ switch (s->ai_addr->sa_family) {
+ case AF_INET:
+ plen = sizeof(struct in_addr) << 3;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ plen = sizeof(struct in6_addr) << 3;
+ break;
+#endif
+ default:
+ continue;
+ }
+
+ /* set src */
+ sa = s->ai_addr;
+ salen = s->ai_addr->sa_len;
+ m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) +
+ PFKEY_ALIGN8(salen));
+ m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ m_addr.sadb_address_proto = upper;
+ m_addr.sadb_address_prefixlen =
+ (splen >= 0 ? splen : plen);
+ m_addr.sadb_address_reserved = 0;
+
+ setvarbuf(buf, &l, (struct sadb_ext *)&m_addr,
+ sizeof(m_addr), (caddr_t)sa, salen);
+
+ /* set dst */
+ sa = d->ai_addr;
+ salen = d->ai_addr->sa_len;
+ m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) +
+ PFKEY_ALIGN8(salen));
+ m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ m_addr.sadb_address_proto = upper;
+ m_addr.sadb_address_prefixlen =
+ (dplen >= 0 ? dplen : plen);
+ m_addr.sadb_address_reserved = 0;
+
+ setvarbuf(buf, &l, (struct sadb_ext *)&m_addr,
+ sizeof(m_addr), (caddr_t)sa, salen);
+
+ msg->sadb_msg_len = PFKEY_UNIT64(l);
+
+ sendkeymsg(buf, l);
+
+ n++;
+ }
+ }
+
+ if (n == 0)
+ return -1;
+ else
+ return 0;
+}
+
+/* XXX NO BUFFER OVERRUN CHECK! BAD BAD! */
+static int
+setkeymsg_addr(type, satype, srcs, dsts, no_spi)
+ unsigned int type;
+ unsigned int satype;
+ struct addrinfo *srcs;
+ struct addrinfo *dsts;
+ int no_spi;
+{
+ struct sadb_msg *msg;
+ char buf[BUFSIZ];
+ int l, l0, len;
+ struct sadb_sa m_sa;
+ struct sadb_x_sa2 m_sa2;
+ struct sadb_address m_addr;
+ struct addrinfo *s, *d;
+ int n;
+ int plen;
+ struct sockaddr *sa;
+ int salen;
+
+ msg = (struct sadb_msg *)buf;
+
+ if (!srcs || !dsts)
+ return -1;
+
+ /* fix up length afterwards */
+ setkeymsg0(msg, type, satype, 0);
+ l = sizeof(struct sadb_msg);
+
+ if (!no_spi) {
+ len = sizeof(struct sadb_sa);
+ m_sa.sadb_sa_len = PFKEY_UNIT64(len);
+ m_sa.sadb_sa_exttype = SADB_EXT_SA;
+ m_sa.sadb_sa_spi = htonl(p_spi);
+ m_sa.sadb_sa_replay = p_replay;
+ m_sa.sadb_sa_state = 0;
+ m_sa.sadb_sa_auth = p_alg_auth;
+ m_sa.sadb_sa_encrypt = p_alg_enc;
+ m_sa.sadb_sa_flags = p_ext;
+
+ memcpy(buf + l, &m_sa, len);
+ l += len;
+
+ len = sizeof(struct sadb_x_sa2);
+ m_sa2.sadb_x_sa2_len = PFKEY_UNIT64(len);
+ m_sa2.sadb_x_sa2_exttype = SADB_X_EXT_SA2;
+ m_sa2.sadb_x_sa2_mode = p_mode;
+ m_sa2.sadb_x_sa2_reqid = p_reqid;
+
+ memcpy(buf + l, &m_sa2, len);
+ l += len;
+ }
+
+ l0 = l;
+ n = 0;
+
+ /* do it for all src/dst pairs */
+ for (s = srcs; s; s = s->ai_next) {
+ for (d = dsts; d; d = d->ai_next) {
+ /* rewind pointer */
+ l = l0;
+
+ if (s->ai_addr->sa_family != d->ai_addr->sa_family)
+ continue;
+ switch (s->ai_addr->sa_family) {
+ case AF_INET:
+ plen = sizeof(struct in_addr) << 3;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ plen = sizeof(struct in6_addr) << 3;
+ break;
+#endif
+ default:
+ continue;
+ }
+
+ /* set src */
+ sa = s->ai_addr;
+ salen = s->ai_addr->sa_len;
+ m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) +
+ PFKEY_ALIGN8(salen));
+ m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ m_addr.sadb_address_proto = IPSEC_ULPROTO_ANY;
+ m_addr.sadb_address_prefixlen = plen;
+ m_addr.sadb_address_reserved = 0;
+
+ setvarbuf(buf, &l, (struct sadb_ext *)&m_addr,
+ sizeof(m_addr), (caddr_t)sa, salen);
+
+ /* set dst */
+ sa = d->ai_addr;
+ salen = d->ai_addr->sa_len;
+ m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) +
+ PFKEY_ALIGN8(salen));
+ m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ m_addr.sadb_address_proto = IPSEC_ULPROTO_ANY;
+ m_addr.sadb_address_prefixlen = plen;
+ m_addr.sadb_address_reserved = 0;
+
+ setvarbuf(buf, &l, (struct sadb_ext *)&m_addr,
+ sizeof(m_addr), (caddr_t)sa, salen);
+
+ msg->sadb_msg_len = PFKEY_UNIT64(l);
+
+ sendkeymsg(buf, l);
+
+ n++;
+ }
+ }
+
+ if (n == 0)
+ return -1;
+ else
+ return 0;
+}
+
+/* XXX NO BUFFER OVERRUN CHECK! BAD BAD! */
+static int
+setkeymsg_add(type, satype, srcs, dsts)
+ unsigned int type;
+ unsigned int satype;
+ struct addrinfo *srcs;
+ struct addrinfo *dsts;
+{
+ struct sadb_msg *msg;
+ char buf[BUFSIZ];
+ int l, l0, len;
+ struct sadb_sa m_sa;
+ struct sadb_x_sa2 m_sa2;
+ struct sadb_address m_addr;
+ struct addrinfo *s, *d;
+ int n;
+ int plen;
+ struct sockaddr *sa;
+ int salen;
+
+ msg = (struct sadb_msg *)buf;
+
+ if (!srcs || !dsts)
+ return -1;
+
+ /* fix up length afterwards */
+ setkeymsg0(msg, type, satype, 0);
+ l = sizeof(struct sadb_msg);
+
+ /* set encryption algorithm, if present. */
+ if (satype != SADB_X_SATYPE_IPCOMP && p_key_enc) {
+ struct sadb_key m_key;
+
+ m_key.sadb_key_len =
+ PFKEY_UNIT64(sizeof(m_key)
+ + PFKEY_ALIGN8(p_key_enc_len));
+ m_key.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
+ m_key.sadb_key_bits = p_key_enc_len * 8;
+ m_key.sadb_key_reserved = 0;
+
+ setvarbuf(buf, &l,
+ (struct sadb_ext *)&m_key, sizeof(m_key),
+ (caddr_t)p_key_enc, p_key_enc_len);
+ }
+
+ /* set authentication algorithm, if present. */
+ if (p_key_auth) {
+ struct sadb_key m_key;
+
+ m_key.sadb_key_len =
+ PFKEY_UNIT64(sizeof(m_key)
+ + PFKEY_ALIGN8(p_key_auth_len));
+ m_key.sadb_key_exttype = SADB_EXT_KEY_AUTH;
+ m_key.sadb_key_bits = p_key_auth_len * 8;
+ m_key.sadb_key_reserved = 0;
+
+ setvarbuf(buf, &l,
+ (struct sadb_ext *)&m_key, sizeof(m_key),
+ (caddr_t)p_key_auth, p_key_auth_len);
+ }
+
+ /* set lifetime for HARD */
+ if (p_lt_hard != 0) {
+ struct sadb_lifetime m_lt;
+ u_int slen = sizeof(struct sadb_lifetime);
+
+ m_lt.sadb_lifetime_len = PFKEY_UNIT64(slen);
+ m_lt.sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
+ m_lt.sadb_lifetime_allocations = 0;
+ m_lt.sadb_lifetime_bytes = 0;
+ m_lt.sadb_lifetime_addtime = p_lt_hard;
+ m_lt.sadb_lifetime_usetime = 0;
+
+ memcpy(buf + l, &m_lt, slen);
+ l += slen;
+ }
+
+ /* set lifetime for SOFT */
+ if (p_lt_soft != 0) {
+ struct sadb_lifetime m_lt;
+ u_int slen = sizeof(struct sadb_lifetime);
+
+ m_lt.sadb_lifetime_len = PFKEY_UNIT64(slen);
+ m_lt.sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
+ m_lt.sadb_lifetime_allocations = 0;
+ m_lt.sadb_lifetime_bytes = 0;
+ m_lt.sadb_lifetime_addtime = p_lt_soft;
+ m_lt.sadb_lifetime_usetime = 0;
+
+ memcpy(buf + l, &m_lt, slen);
+ l += slen;
+ }
+
+ len = sizeof(struct sadb_sa);
+ m_sa.sadb_sa_len = PFKEY_UNIT64(len);
+ m_sa.sadb_sa_exttype = SADB_EXT_SA;
+ m_sa.sadb_sa_spi = htonl(p_spi);
+ m_sa.sadb_sa_replay = p_replay;
+ m_sa.sadb_sa_state = 0;
+ m_sa.sadb_sa_auth = p_alg_auth;
+ m_sa.sadb_sa_encrypt = p_alg_enc;
+ m_sa.sadb_sa_flags = p_ext;
+
+ memcpy(buf + l, &m_sa, len);
+ l += len;
+
+ len = sizeof(struct sadb_x_sa2);
+ m_sa2.sadb_x_sa2_len = PFKEY_UNIT64(len);
+ m_sa2.sadb_x_sa2_exttype = SADB_X_EXT_SA2;
+ m_sa2.sadb_x_sa2_mode = p_mode;
+ m_sa2.sadb_x_sa2_reqid = p_reqid;
+
+ memcpy(buf + l, &m_sa2, len);
+ l += len;
+
+ l0 = l;
+ n = 0;
+
+ /* do it for all src/dst pairs */
+ for (s = srcs; s; s = s->ai_next) {
+ for (d = dsts; d; d = d->ai_next) {
+ /* rewind pointer */
+ l = l0;
+
+ if (s->ai_addr->sa_family != d->ai_addr->sa_family)
+ continue;
+ switch (s->ai_addr->sa_family) {
+ case AF_INET:
+ plen = sizeof(struct in_addr) << 3;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ plen = sizeof(struct in6_addr) << 3;
+ break;
+#endif
+ default:
+ continue;
+ }
+
+ /* set src */
+ sa = s->ai_addr;
+ salen = s->ai_addr->sa_len;
+ m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) +
+ PFKEY_ALIGN8(salen));
+ m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ m_addr.sadb_address_proto = IPSEC_ULPROTO_ANY;
+ m_addr.sadb_address_prefixlen = plen;
+ m_addr.sadb_address_reserved = 0;
+
+ setvarbuf(buf, &l, (struct sadb_ext *)&m_addr,
+ sizeof(m_addr), (caddr_t)sa, salen);
+
+ /* set dst */
+ sa = d->ai_addr;
+ salen = d->ai_addr->sa_len;
+ m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) +
+ PFKEY_ALIGN8(salen));
+ m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ m_addr.sadb_address_proto = IPSEC_ULPROTO_ANY;
+ m_addr.sadb_address_prefixlen = plen;
+ m_addr.sadb_address_reserved = 0;
+
+ setvarbuf(buf, &l, (struct sadb_ext *)&m_addr,
+ sizeof(m_addr), (caddr_t)sa, salen);
+
+ msg->sadb_msg_len = PFKEY_UNIT64(l);
+
+ sendkeymsg(buf, l);
+
+ n++;
+ }
+ }
+
+ if (n == 0)
+ return -1;
+ else
+ return 0;
+}
+
+static struct addrinfo *
+parse_addr(host, port)
+ char *host;
+ char *port;
+{
+ struct addrinfo hints, *res = NULL;
+ int error;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = p_aifamily;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_protocol = IPPROTO_UDP; /*dummy*/
+ hints.ai_flags = p_aiflags;
+ error = getaddrinfo(host, port, &hints, &res);
+ if (error != 0) {
+ yyerror(gai_strerror(error));
+ return NULL;
+ }
+ return res;
+}
+
+static int
+fix_portstr(spec, sport, dport)
+ vchar_t *spec, *sport, *dport;
+{
+ char *p, *p2;
+ u_int l;
+
+ l = 0;
+ for (p = spec->buf; *p != ',' && *p != '\0' && l < spec->len; p++, l++)
+ ;
+ if (*p == '\0') {
+ p2 = "0";
+ } else {
+ if (*p == ',') {
+ *p = '\0';
+ p2 = ++p;
+ }
+ for (p = p2; *p != '\0' && l < spec->len; p++, l++)
+ ;
+ if (*p != '\0' || *p2 == '\0') {
+ yyerror("invalid an upper layer protocol spec");
+ return -1;
+ }
+ }
+
+ sport->buf = strdup(spec->buf);
+ if (!sport->buf) {
+ yyerror("insufficient memory");
+ return -1;
+ }
+ sport->len = strlen(sport->buf);
+ dport->buf = strdup(p2);
+ if (!dport->buf) {
+ yyerror("insufficient memory");
+ return -1;
+ }
+ dport->len = strlen(dport->buf);
+
+ return 0;
+}
+
+static int
+setvarbuf(buf, off, ebuf, elen, vbuf, vlen)
+ char *buf;
+ int *off;
+ struct sadb_ext *ebuf;
+ int elen;
+ caddr_t vbuf;
+ int vlen;
+{
+ memset(buf + *off, 0, PFKEY_UNUNIT64(ebuf->sadb_ext_len));
+ memcpy(buf + *off, (caddr_t)ebuf, elen);
+ memcpy(buf + *off + elen, vbuf, vlen);
+ (*off) += PFKEY_ALIGN8(elen + vlen);
+
+ return 0;
+}
+
+void
+parse_init()
+{
+ p_spi = 0;
+
+ p_ext = SADB_X_EXT_CYCSEQ;
+ p_alg_enc = SADB_EALG_NONE;
+ p_alg_auth = SADB_AALG_NONE;
+ p_mode = IPSEC_MODE_ANY;
+ p_reqid = 0;
+ p_replay = 0;
+ p_key_enc_len = p_key_auth_len = 0;
+ p_key_enc = p_key_auth = 0;
+ p_lt_hard = p_lt_soft = 0;
+
+ p_aiflags = 0;
+ p_aifamily = PF_UNSPEC;
+
+ return;
+}
+
+void
+free_buffer()
+{
+ /* we got tons of memory leaks in the parser anyways, leave them */
+
+ return;
+}
diff --git a/sbin/setkey/sample.cf b/sbin/setkey/sample.cf
new file mode 100644
index 0000000..c534fa1
--- /dev/null
+++ b/sbin/setkey/sample.cf
@@ -0,0 +1,219 @@
+# Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE 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.
+# 3. Neither the name of the project 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 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.
+#
+# $FreeBSD$
+
+# There are sample scripts for IPsec configuration by manual keying.
+# A security association is uniquely identified by a triple consisting
+# of a Security Parameter Index (SPI), an IP Destination Address, and a
+# security protocol (AH or ESP) identifier. You must take care of these
+# parameters when you configure by manual keying.
+
+# ESP transport mode is recommended for TCP port number 110 between
+# Host-A and Host-B. Encryption algorithm is blowfish-cbc whose key
+# is "kamekame", and authentication algorithm is hmac-sha1 whose key
+# is "this is the test key".
+#
+# ============ ESP ============
+# | |
+# Host-A Host-B
+# fec0::10 -------------------- fec0::11
+#
+# At Host-A and Host-B,
+spdadd fec0::10[any] fec0::11[110] tcp -P out ipsec
+ esp/transport//use ;
+spdadd fec0::11[110] fec0::10[any] tcp -P in ipsec
+ esp/transport//use ;
+add fec0::10 fec0::11 esp 0x10001
+ -m transport
+ -E blowfish-cbc "kamekame"
+ -A hmac-sha1 "this is the test key" ;
+add fec0::11 fec0::10 esp 0x10002
+ -m transport
+ -E blowfish-cbc "kamekame"
+ -A hmac-sha1 "this is the test key" ;
+
+# "[any]" is wildcard of port number. Note that "[0]" is the number of
+# zero in port number.
+
+# Security protocol is old AH tunnel mode, i.e. RFC1826, with keyed-md5
+# whose key is "this is the test" as authentication algorithm.
+# That protocol takes place between Gateway-A and Gateway-B.
+#
+# ======= AH =======
+# | |
+# Network-A Gateway-A Gateway-B Network-B
+# 10.0.1.0/24 ---- 172.16.0.1 ----- 172.16.0.2 ---- 10.0.2.0/24
+#
+# At Gateway-A:
+spdadd 10.0.1.0/24 10.0.2.0/24 any -P out ipsec
+ ah/tunnel/172.16.0.1-172.16.0.2/require ;
+spdadd 10.0.2.0/24 10.0.1.0/24 any -P in ipsec
+ ah/tunnel/172.16.0.2-172.16.0.1/require ;
+add 172.16.0.1 172.16.0.2 ah-old 0x10003
+ -m any
+ -A keyed-md5 "this is the test" ;
+add 172.16.0.2 172.16.0.1 ah-old 0x10004
+ -m any
+ -A keyed-md5 "this is the test" ;
+
+# If port number field is omitted such above then "[any]" is employed.
+# -m specifies the mode of SA to be used. "-m any" means wildcard of
+# mode of security protocol. You can use this SAs for both tunnel and
+# transport mode.
+
+# At Gateway-B. Attention to the selector and peer's IP address for tunnel.
+spdadd 10.0.2.0/24 10.0.1.0/24 any -P out ipsec
+ ah/tunnel/172.16.0.2-172.16.0.1/require ;
+spdadd 10.0.1.0/24 10.0.2.0/24 any -P in ipsec
+ ah/tunnel/172.16.0.1-172.16.0.2/require ;
+add 172.16.0.1 172.16.0.2 ah-old 0x10003
+ -m tunnel
+ -A keyed-md5 "this is the test" ;
+add 172.16.0.2 172.16.0.1 ah-old 0x10004
+ -m tunnel
+ -A keyed-md5 "this is the test" ;
+
+# AH transport mode followed by ESP tunnel mode is required between
+# Gateway-A and Gateway-B.
+# Encryption algorithm is 3des-cbc, and authentication algorithm for ESP
+# is hmac-sha1. Authentication algorithm for AH is hmac-md5.
+#
+# ========== AH =========
+# | ======= ESP ===== |
+# | | | |
+# Network-A Gateway-A Gateway-B Network-B
+# fec0:0:0:1::/64 --- fec0:0:0:1::1 ---- fec0:0:0:2::1 --- fec0:0:0:2::/64
+#
+# At Gateway-A:
+spdadd fec0:0:0:1::/64 fec0:0:0:2::/64 any -P out ipsec
+ esp/tunnel/fec0:0:0:1::1-fec0:0:0:2::1/require
+ ah/transport//require ;
+spdadd fec0:0:0:2::/64 fec0:0:0:1::/64 any -P in ipsec
+ esp/tunnel/fec0:0:0:2::1-fec0:0:0:1::1/require
+ ah/transport//require ;
+add fec0:0:0:1::1 fec0:0:0:2::1 esp 0x10001
+ -m tunnel
+ -E 3des-cbc "kamekame12341234kame1234"
+ -A hmac-sha1 "this is the test key" ;
+add fec0:0:0:1::1 fec0:0:0:2::1 ah 0x10001
+ -m transport
+ -A hmac-md5 "this is the test" ;
+add fec0:0:0:2::1 fec0:0:0:1::1 esp 0x10001
+ -m tunnel
+ -E 3des-cbc "kamekame12341234kame1234"
+ -A hmac-sha1 "this is the test key" ;
+add fec0:0:0:2::1 fec0:0:0:1::1 ah 0x10001
+ -m transport
+ -A hmac-md5 "this is the test" ;
+
+# ESP tunnel mode is required between Host-A and Gateway-A.
+# Encryption algorithm is cast128-cbc, and authentication algorithm
+# for ESP is hmac-sha1.
+# ESP transport mode is recommended between Host-A and Host-B.
+# Encryption algorithm is rc5-cbc, and authentication algorithm
+# for ESP is hmac-md5.
+#
+# ================== ESP =================
+# | ======= ESP ======= |
+# | | | |
+# Host-A Gateway-A Host-B
+# fec0:0:0:1::1 ---- fec0:0:0:2::1 ---- fec0:0:0:2::2
+#
+# At Host-A:
+spdadd fec0:0:0:1::1[any] fec0:0:0:2::2[80] tcp -P out ipsec
+ esp/transport//use
+ esp/tunnel/fec0:0:0:1::1-fec0:0:0:2::1/require ;
+spdadd fec0:0:0:2::1[80] fec0:0:0:1::1[any] tcp -P in ipsec
+ esp/transport//use
+ esp/tunnel/fec0:0:0:2::1-fec0:0:0:1::1/require ;
+add fec0:0:0:1::1 fec0:0:0:2::2 esp 0x10001
+ -m transport
+ -E cast128-cbc "12341234"
+ -A hmac-sha1 "this is the test key" ;
+add fec0:0:0:1::1 fec0:0:0:2::1 esp 0x10002
+ -E rc5-cbc "kamekame"
+ -A hmac-md5 "this is the test" ;
+add fec0:0:0:2::2 fec0:0:0:1::1 esp 0x10003
+ -m transport
+ -E cast128-cbc "12341234"
+ -A hmac-sha1 "this is the test key" ;
+add fec0:0:0:2::1 fec0:0:0:1::1 esp 0x10004
+ -E rc5-cbc "kamekame"
+ -A hmac-md5 "this is the test" ;
+
+# By "get" command, you can get a entry of either SP or SA.
+get fec0:0:0:1::1 fec0:0:0:2::2 ah 0x10004 ;
+
+# Also delete command, you can delete a entry of either SP or SA.
+spddelete fec0:0:0:1::/64 fec0:0:0:2::/64 any -P out;
+delete fec0:0:0:1::1 fec0:0:0:2::2 ah 0x10004 ;
+
+# By dump command, you can dump all entry of either SP or SA.
+dump ;
+spddump ;
+dump esp ;
+flush esp ;
+
+# By flush command, you can flush all entry of either SP or SA.
+flush ;
+spdflush ;
+
+# "flush" and "dump" commands can specify a security protocol.
+dump esp ;
+flush ah ;
+
+# XXX
+add ::1 ::1 esp 10001 -m transport -E null ;
+add ::1 ::1 esp 10002 -m transport -E des-deriv "12341234" ;
+add ::1 ::1 esp-old 10003 -m transport -E des-32iv "12341234" ;
+add ::1 ::1 esp 10004 -m transport -E null -A null ;
+add ::1 ::1 esp 10005 -m transport -E null -A hmac-md5 "1234123412341234" ;
+add ::1 ::1 esp 10006 -m tunnel -E null -A hmac-sha1 "12341234123412341234" ;
+add ::1 ::1 esp 10007 -m transport -E null -A keyed-md5 "1234123412341234" ;
+add ::1 ::1 esp 10008 -m any -E null -A keyed-sha1 "12341234123412341234" ;
+add ::1 ::1 esp 10009 -m transport -E des-cbc "testtest" ;
+add ::1 ::1 esp 10010 -m transport -E 3des-cbc "testtest12341234testtest" ;
+add ::1 ::1 esp 10011 -m tunnel -E cast128-cbc "testtest1234" ;
+add ::1 ::1 esp 10012 -m tunnel -E blowfish-cbc "testtest1234" ;
+add ::1 ::1 esp 10013 -m tunnel -E rc5-cbc "testtest1234" ;
+add ::1 ::1 esp 10014 -m any -E rc5-cbc "testtest1234" ;
+add ::1 ::1 esp 10015 -m transport -f zero-pad -E null ;
+add ::1 ::1 esp 10016 -m tunnel -f random-pad -r 8 -lh 100 -ls 80 -E null ;
+add ::1 ::1 esp 10017 -m transport -f seq-pad -f nocyclic-seq -E null ;
+add ::1 ::1 esp 10018 -m transport -E null ;
+#add ::1 ::1 ah 20000 -m transport -A null ;
+add ::1 ::1 ah 20001 -m any -A hmac-md5 "1234123412341234";
+add ::1 ::1 ah 20002 -m tunnel -A hmac-sha1 "12341234123412341234";
+add ::1 ::1 ah 20003 -m transport -A keyed-md5 "1234123412341234";
+add ::1 ::1 ah-old 20004 -m transport -A keyed-md5 "1234123412341234";
+add ::1 ::1 ah 20005 -m transport -A keyed-sha1 "12341234123412341234";
+#add ::1 ::1 ipcomp 30000 -C oui ;
+add ::1 ::1 ipcomp 30001 -C deflate ;
+#add ::1 ::1 ipcomp 30002 -C lzs ;
+
+# enjoy.
diff --git a/sbin/setkey/scriptdump.pl b/sbin/setkey/scriptdump.pl
new file mode 100644
index 0000000..a1d8adb
--- /dev/null
+++ b/sbin/setkey/scriptdump.pl
@@ -0,0 +1,56 @@
+#! @LOCALPREFIX@/bin/perl
+# $FreeBSD$
+
+if ($< != 0) {
+ print STDERR "must be root to invoke this\n";
+ exit 1;
+}
+
+$mode = 'add';
+while ($i = shift @ARGV) {
+ if ($i eq '-d') {
+ $mode = 'delete';
+ } else {
+ print STDERR "usage: scriptdump [-d]\n";
+ exit 1;
+ }
+}
+
+open(IN, "setkey -D |") || die;
+foreach $_ (<IN>) {
+ if (/^[^\t]/) {
+ ($src, $dst) = split(/\s+/, $_);
+ } elsif (/^\t(esp|ah) mode=(\S+) spi=(\d+).*reqid=(\d+)/) {
+ ($proto, $ipsecmode, $spi, $reqid) = ($1, $2, $3, $4);
+ } elsif (/^\tE: (\S+) (.*)/) {
+ $ealgo = $1;
+ $ekey = $2;
+ $ekey =~ s/\s//g;
+ $ekey =~ s/^/0x/g;
+ } elsif (/^\tA: (\S+) (.*)/) {
+ $aalgo = $1;
+ $akey = $2;
+ $akey =~ s/\s//g;
+ $akey =~ s/^/0x/g;
+ } elsif (/^\tseq=(0x\d+) replay=(\d+) flags=(0x\d+) state=/) {
+ print "$mode $src $dst $proto $spi";
+ $replay = $2;
+ print " -u $reqid" if $reqid;
+ if ($mode eq 'add') {
+ print " -m $ipsecmode -r $replay" if $replay;
+ if ($proto eq 'esp') {
+ print " -E $ealgo $ekey" if $ealgo;
+ print " -A $aalgo $akey" if $aalgo;
+ } elsif ($proto eq 'ah') {
+ print " -A $aalgo $akey" if $aalgo;
+ }
+ }
+ print ";\n";
+
+ $src = $dst = $upper = $proxy = '';
+ $ealgo = $ekey = $aalgo = $akey = '';
+ }
+}
+close(IN);
+
+exit 0;
diff --git a/sbin/setkey/setkey.8 b/sbin/setkey/setkey.8
new file mode 100644
index 0000000..370b350
--- /dev/null
+++ b/sbin/setkey/setkey.8
@@ -0,0 +1,723 @@
+.\" $KAME: setkey.8,v 1.89 2003/09/07 22:17:41 itojun Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE 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.
+.\" 3. Neither the name of the project 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 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 8, 2006
+.Dt SETKEY 8
+.Os
+.\"
+.Sh NAME
+.Nm setkey
+.Nd "manually manipulate the IPsec SA/SP database"
+.\"
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Fl c
+.Nm
+.Op Fl v
+.Fl f Ar filename
+.Nm
+.Op Fl aPlv
+.Fl D
+.Nm
+.Op Fl Pv
+.Fl F
+.Nm
+.Op Fl h
+.Fl x
+.\"
+.Sh DESCRIPTION
+The
+.Nm
+utility adds, updates, dumps, or flushes
+Security Association Database (SAD) entries
+as well as Security Policy Database (SPD) entries in the kernel.
+.Pp
+The
+.Nm
+utility takes a series of operations from the standard input
+(if invoked with
+.Fl c )
+or the file named
+.Ar filename
+(if invoked with
+.Fl f Ar filename ) .
+.Bl -tag -width indent
+.It Fl D
+Dump the SAD entries.
+If with
+.Fl P ,
+the SPD entries are dumped.
+.It Fl F
+Flush the SAD entries.
+If with
+.Fl P ,
+the SPD entries are flushed.
+.It Fl a
+The
+.Nm
+utility
+usually does not display dead SAD entries with
+.Fl D .
+If with
+.Fl a ,
+the dead SAD entries will be displayed as well.
+A dead SAD entry means that
+it has been expired but remains in the system
+because it is referenced by some SPD entries.
+.It Fl h
+Add hexadecimal dump on
+.Fl x
+mode.
+.It Fl l
+Loop forever with short output on
+.Fl D .
+.It Fl v
+Be verbose.
+The program will dump messages exchanged on
+.Dv PF_KEY
+socket, including messages sent from other processes to the kernel.
+.It Fl x
+Loop forever and dump all the messages transmitted to
+.Dv PF_KEY
+socket.
+.Fl xx
+makes each timestamps unformatted.
+.El
+.Ss Configuration syntax
+With
+.Fl c
+or
+.Fl f
+on the command line,
+.Nm
+accepts the following configuration syntax.
+Lines starting with hash signs
+.Pq Ql #
+are treated as comment lines.
+.Bl -tag -width indent
+.It Xo
+.Li add
+.Op Fl 46n
+.Ar src Ar dst Ar protocol Ar spi
+.Op Ar extensions
+.Ar algorithm ...
+.Li ;
+.Xc
+Add an SAD entry.
+.Li add
+can fail with multiple reasons,
+including when the key length does not match the specified algorithm.
+.\"
+.It Xo
+.Li get
+.Op Fl 46n
+.Ar src Ar dst Ar protocol Ar spi
+.Li ;
+.Xc
+Show an SAD entry.
+.\"
+.It Xo
+.Li delete
+.Op Fl 46n
+.Ar src Ar dst Ar protocol Ar spi
+.Li ;
+.Xc
+Remove an SAD entry.
+.\"
+.It Xo
+.Li deleteall
+.Op Fl 46n
+.Ar src Ar dst Ar protocol
+.Li ;
+.Xc
+Remove all SAD entries that match the specification.
+.\"
+.It Xo
+.Li flush
+.Op Ar protocol
+.Li ;
+.Xc
+Clear all SAD entries matched by the options.
+.Fl F
+on the command line achieves the same functionality.
+.\"
+.It Xo
+.Li dump
+.Op Ar protocol
+.Li ;
+.Xc
+Dumps all SAD entries matched by the options.
+.Fl D
+on the command line achieves the same functionality.
+.\"
+.It Xo
+.Li spdadd
+.Op Fl 46n
+.Ar src_range Ar dst_range Ar upperspec Ar policy
+.Li ;
+.Xc
+Add an SPD entry.
+.\"
+.It Xo
+.Li spddelete
+.Op Fl 46n
+.Ar src_range Ar dst_range Ar upperspec Fl P Ar direction
+.Li ;
+.Xc
+Delete an SPD entry.
+.\"
+.It Xo
+.Li spdflush
+.Li ;
+.Xc
+Clear all SPD entries.
+.Fl FP
+on the command line achieves the same functionality.
+.\"
+.It Xo
+.Li spddump
+.Li ;
+.Xc
+Dumps all SPD entries.
+.Fl DP
+on the command line achieves the same functionality.
+.El
+.\"
+.Pp
+Meta-arguments are as follows:
+.Pp
+.Bl -tag -compact -width indent
+.It Ar src
+.It Ar dst
+Source/destination of the secure communication is specified as
+IPv4/v6 address.
+The
+.Nm
+utility
+can resolve a FQDN into numeric addresses.
+If the FQDN resolves into multiple addresses,
+.Nm
+will install multiple SAD/SPD entries into the kernel
+by trying all possible combinations.
+.Fl 4 ,
+.Fl 6
+and
+.Fl n
+restricts the address resolution of FQDN in certain ways.
+.Fl 4
+and
+.Fl 6
+restrict results into IPv4/v6 addresses only, respectively.
+.Fl n
+avoids FQDN resolution and requires addresses to be numeric addresses.
+.\"
+.Pp
+.It Ar protocol
+.Ar protocol
+is one of following:
+.Bl -tag -width Fl -compact
+.It Li esp
+ESP based on rfc2406
+.It Li esp-old
+ESP based on rfc1827
+.It Li ah
+AH based on rfc2402
+.It Li ah-old
+AH based on rfc1826
+.It Li ipcomp
+IPComp
+.It Li tcp
+TCP-MD5 based on rfc2385
+.El
+.\"
+.Pp
+.It Ar spi
+Security Parameter Index
+(SPI)
+for the SAD and the SPD.
+.Ar spi
+must be a decimal number, or a hexadecimal number with
+.Ql 0x
+prefix.
+SPI values between 0 and 255 are reserved for future use by IANA
+and they cannot be used.
+TCP-MD5 associations must use 0x1000 and therefore only have per-host
+granularity at this time.
+.\"
+.Pp
+.It Ar extensions
+take some of the following:
+.Bl -tag -width Fl -compact
+.\"
+.It Fl m Ar mode
+Specify a security protocol mode for use.
+.Ar mode
+is one of following:
+.Li transport , tunnel
+or
+.Li any .
+The default value is
+.Li any .
+.\"
+.It Fl r Ar size
+Specify window size of bytes for replay prevention.
+.Ar size
+must be decimal number in 32-bit word.
+If
+.Ar size
+is zero or not specified, replay check does not take place.
+.\"
+.It Fl u Ar id
+Specify the identifier of the policy entry in SPD.
+See
+.Ar policy .
+.\"
+.It Fl f Ar pad_option
+defines the content of the ESP padding.
+.Ar pad_option
+is one of following:
+.Bl -tag -width random-pad -compact
+.It Li zero-pad
+All of the padding are zero.
+.It Li random-pad
+A series of randomized values are set.
+.It Li seq-pad
+A series of sequential increasing numbers started from 1 are set.
+.El
+.\"
+.It Fl f Li nocyclic-seq
+Do not allow cyclic sequence number.
+.\"
+.It Fl lh Ar time
+.It Fl ls Ar time
+Specify hard/soft life time duration of the SA.
+.El
+.\"
+.Pp
+.It Ar algorithm
+.Bl -tag -width Fl -compact
+.It Fl E Ar ealgo Ar key
+Specify an encryption algorithm
+.Ar ealgo
+for ESP.
+.It Xo
+.Fl E Ar ealgo Ar key
+.Fl A Ar aalgo Ar key
+.Xc
+Specify a encryption algorithm
+.Ar ealgo ,
+as well as a payload authentication algorithm
+.Ar aalgo ,
+for ESP.
+.It Fl A Ar aalgo Ar key
+Specify an authentication algorithm for AH.
+.It Fl C Ar calgo Op Fl R
+Specify a compression algorithm for IPComp.
+If
+.Fl R
+is specified, the
+.Ar spi
+field value will be used as the IPComp CPI
+(compression parameter index)
+on wire as is.
+If
+.Fl R
+is not specified,
+the kernel will use well-known CPI on wire, and
+.Ar spi
+field will be used only as an index for kernel internal usage.
+.El
+.Pp
+.Ar key
+must be double-quoted character string, or a series of hexadecimal digits
+preceded by
+.Ql 0x .
+.Pp
+Possible values for
+.Ar ealgo ,
+.Ar aalgo
+and
+.Ar calgo
+are specified in separate section.
+.\"
+.Pp
+.It Ar src_range
+.It Ar dst_range
+These are selections of the secure communication specified as
+IPv4/v6 address or IPv4/v6 address range, and it may accompany
+TCP/UDP port specification.
+This takes the following form:
+.Bd -unfilled
+.Ar address
+.Ar address/prefixlen
+.Ar address[port]
+.Ar address/prefixlen[port]
+.Ed
+.Pp
+.Ar prefixlen
+and
+.Ar port
+must be a decimal number.
+The square brackets around
+.Ar port
+are necessary and are not manpage metacharacters.
+For FQDN resolution, the rules applicable to
+.Ar src
+and
+.Ar dst
+apply here as well.
+.\"
+.Pp
+.It Ar upperspec
+The upper layer protocol to be used.
+You can use one of the words in
+.Pa /etc/protocols
+as
+.Ar upperspec ,
+as well as
+.Li icmp6 ,
+.Li ip4 ,
+or
+.Li any .
+.Li Any
+stands for
+.Dq any protocol .
+The protocol number may also be used to specify the
+.Ar upperspec .
+A type and code related to ICMPv6 may also be specified as an
+.Ar upperspec .
+The type is specified first, followed by a comma and then the relevant
+code.
+The specification must be placed after
+.Li icmp6 .
+The kernel considers a zero to be a wildcard but
+cannot distinguish between a wildcard and an ICMPv6
+type which is zero.
+The following example shows a policy where IPSec is not required for
+inbound Neighbor Solicitations:
+.Pp
+.Dl "spdadd ::/0 ::/0 icmp6 135,0 -P in none;"
+.Pp
+NOTE:
+.Ar upperspec
+does not work in the forwarding case at this moment,
+as it requires extra reassembly at forwarding node,
+which is not implemented at this moment.
+Although there are many protocols in
+.Pa /etc/protocols ,
+protocols other than TCP, UDP and ICMP may not be suitable to use with IPsec.
+.\"
+.Pp
+.It Ar policy
+.Ar policy
+is expressed in one of the following three formats:
+.Bd -ragged -offset indent
+.It Fl P Ar direction Li discard
+.It Fl P Ar direction Li none
+.It Xo Fl P Ar direction Li ipsec
+.Ar protocol/mode/src-dst/level Op ...
+.Xc
+.Ed
+.Pp
+The direction of a policy must be specified as
+one of:
+.Li out ,
+.Li in ,
+.Li discard
+.Li none ,
+or
+.Li ipsec .
+.Li Discard
+means that packets matching the supplied indices will be discarded
+while
+.Li none
+means that IPsec operations will not take place on the packet and
+.Li ipsec
+means that IPsec operation will take place onto the packet.
+The
+.Ar protocol/mode/src-dst/level
+statement gives the rule for how to process the packet.
+.Ar Protocol is specified as
+.Li ah ,
+.Li esp
+or
+.Li ipcomp
+The
+.Ar mode
+is either
+.Li transport
+or
+.Li tunnel .
+If
+.Ar mode
+is
+.Li tunnel ,
+you must specify the end-point addresses of the SA as
+.Ar src
+and
+.Ar dst
+with a dash,
+.Sq - ,
+between the addresses.
+If
+.Ar mode
+is
+.Li transport ,
+both
+.Ar src
+and
+.Ar dst
+can be omitted.
+The
+.Ar level
+is one of the following:
+.Li default , use , require
+or
+.Li unique .
+If the SA is not available in every level, the kernel will request
+the SA from the key exchange daemon.
+A value of
+.Li default
+tells the kernel to use the system wide default protocol
+e.g. the one from the
+.Li esp_trans_deflev
+sysctl variable, when the kernel processes the packet.
+.Li Use
+means that the kernel will use an SA if it is available,
+otherwise the kernel will pass the packet as it would normally.
+.Li Require
+means that an SA is required whenever the kernel sends a packet matched
+that matches the policy.
+The
+.Li unique
+level is the same as
+.Li require
+but, in addition, it allows the policy to bind with the unique out-bound SA.
+For example, if you specify the policy level
+.Li unique ,
+.Xr racoon 8
+will configure the SA for the policy.
+If you configure the SA by manual keying for that policy,
+you can put the decimal number as the policy identifier after
+.Li unique
+separated by colon
+.Ql :\&
+as in the following example:
+.Li unique:number .
+In order to bind this policy to the SA,
+.Li number
+must be between 1 and 32767,
+which corresponds to
+.Ar extensions Fl u
+of manual SA configuration.
+.Pp
+When you want to use an SA bundle, you can define multiple rules. For
+example, if an IP header was followed by an AH header followed by an
+ESP header followed by an upper layer protocol header, the rule would
+be:
+.Dl esp/transport//require ah/transport//require ;
+The rule order is very important.
+.Pp
+Note that
+.Dq Li discard
+and
+.Dq Li none
+are not in the syntax described in
+.Xr ipsec_set_policy 3 .
+There are small, but important, differences in the syntax.
+See
+.Xr ipsec_set_policy 3
+for details.
+.Pp
+.El
+.Pp
+.\"
+.Sh ALGORITHMS
+The following list shows the supported algorithms.
+The
+.Sy protocol
+and
+.Sy algorithm
+are almost completely orthogonal.
+The following list of authentication algorithms can be used as
+.Ar aalgo
+in the
+.Fl A Ar aalgo
+of the
+.Ar protocol
+parameter:
+.Pp
+.Bd -literal -offset indent
+algorithm keylen (bits) comment
+hmac-md5 128 ah: rfc2403
+ 128 ah-old: rfc2085
+hmac-sha1 160 ah: rfc2404
+ 160 ah-old: 128bit ICV (no document)
+keyed-md5 128 ah: 96bit ICV (no document)
+ 128 ah-old: rfc1828
+keyed-sha1 160 ah: 96bit ICV (no document)
+ 160 ah-old: 128bit ICV (no document)
+null 0 to 2048 for debugging
+hmac-sha2-256 256 ah: 96bit ICV
+ (draft-ietf-ipsec-ciph-sha-256-00)
+ 256 ah-old: 128bit ICV (no document)
+hmac-sha2-384 384 ah: 96bit ICV (no document)
+ 384 ah-old: 128bit ICV (no document)
+hmac-sha2-512 512 ah: 96bit ICV (no document)
+ 512 ah-old: 128bit ICV (no document)
+hmac-ripemd160 160 ah: 96bit ICV (RFC2857)
+ ah-old: 128bit ICV (no document)
+aes-xcbc-mac 128 ah: 96bit ICV (RFC3566)
+ 128 ah-old: 128bit ICV (no document)
+tcp-md5 8 to 640 tcp: rfc2385
+.Ed
+.Pp
+The following is the list of encryption algorithms that can be used as the
+.Ar ealgo
+in the
+.Fl E Ar ealgo
+of the
+.Ar protocol
+parameter:
+.Pp
+.Bd -literal -offset indent
+algorithm keylen (bits) comment
+des-cbc 64 esp-old: rfc1829, esp: rfc2405
+3des-cbc 192 rfc2451
+null 0 to 2048 rfc2410
+blowfish-cbc 40 to 448 rfc2451
+cast128-cbc 40 to 128 rfc2451
+des-deriv 64 ipsec-ciph-des-derived-01
+3des-deriv 192 no document
+rijndael-cbc 128/192/256 rfc3602
+aes-ctr 160/224/288 draft-ietf-ipsec-ciph-aes-ctr-03
+.Ed
+.Pp
+Note that the first 128 bits of a key for
+.Li aes-ctr
+will be used as AES key, and remaining 32 bits will be used as nonce.
+.Pp
+The following are the list of compression algorithms that can be used
+as the
+.Ar calgo
+in the
+.Fl C Ar calgo
+of the
+.Ar protocol
+parameter:
+.Pp
+.Bd -literal -offset indent
+algorithm comment
+deflate rfc2394
+.Ed
+.\"
+.Sh EXIT STATUS
+.Ex -std
+.\"
+.Sh EXAMPLES
+Add an ESP SA between two IPv6 addresses using the
+des-cbc encryption algorithm.
+.Bd -literal -offset
+add 3ffe:501:4819::1 3ffe:501:481d::1 esp 123457
+ -E des-cbc 0x3ffe05014819ffff ;
+
+.Ed
+.\"
+Add an authentication SA between two FQDN specified hosts:
+.Bd -literal -offset
+add -6 myhost.example.com yourhost.example.com ah 123456
+ -A hmac-sha1 "AH SA configuration!" ;
+
+.Ed
+Use both ESP and AH between two numerically specified hosts:
+.Bd -literal -offset
+add 10.0.11.41 10.0.11.33 esp 0x10001
+ -E des-cbc 0x3ffe05014819ffff
+ -A hmac-md5 "authentication!!" ;
+
+.Ed
+Get the SA information assocaited with first example above:
+.Bd -literal -offset
+get 3ffe:501:4819::1 3ffe:501:481d::1 ah 123456 ;
+
+.Ed
+Flush all entries from the database:
+.Bd -literal -offset
+flush ;
+
+.Ed
+Dump the ESP entries from the database:
+.Bd -literal -offset
+dump esp ;
+
+.Ed
+Add a security policy between two networks that uses ESP in tunnel mode:
+.Bd -literal -offset
+spdadd 10.0.11.41/32[21] 10.0.11.33/32[any] any
+ -P out ipsec esp/tunnel/192.168.0.1-192.168.1.2/require ;
+
+.Ed
+Use TCP MD5 between two numerically specified hosts:
+.Bd -literal -offset
+add 10.1.10.34 10.1.10.36 tcp 0x1000 -A tcp-md5 "TCP-MD5 BGP secret" ;
+
+.Ed
+.\"
+.Sh SEE ALSO
+.Xr ipsec_set_policy 3 ,
+.Xr racoon 8 ,
+.Xr sysctl 8
+.Rs
+.%T "Changed manual key configuration for IPsec"
+.%O "http://www.kame.net/newsletter/19991007/"
+.%D "October 1999"
+.Re
+.\"
+.Sh HISTORY
+The
+.Nm
+utility first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+The utility was completely re-designed in June 1998.
+.\"
+.Sh BUGS
+The
+.Nm
+utility
+should report and handle syntax errors better.
+.Pp
+For IPsec gateway configuration,
+.Ar src_range
+and
+.Ar dst_range
+with TCP/UDP port number do not work, as the gateway does not reassemble
+packets
+(cannot inspect upper-layer headers).
diff --git a/sbin/setkey/setkey.c b/sbin/setkey/setkey.c
new file mode 100644
index 0000000..5bdd6df
--- /dev/null
+++ b/sbin/setkey/setkey.c
@@ -0,0 +1,632 @@
+/* $FreeBSD$ */
+/* $KAME: setkey.c,v 1.28 2003/06/27 07:15:45 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE 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.
+ * 3. Neither the name of the project 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 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.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <err.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <net/pfkeyv2.h>
+#include <netkey/keydb.h>
+#include <netkey/key_debug.h>
+#include <netinet6/ipsec.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+
+#include "libpfkey.h"
+
+void usage __P((void));
+int main __P((int, char **));
+int get_supported __P((void));
+void sendkeyshort __P((u_int));
+void promisc __P((void));
+int sendkeymsg __P((char *, size_t));
+int postproc __P((struct sadb_msg *, int));
+const char *numstr __P((int));
+void shortdump_hdr __P((void));
+void shortdump __P((struct sadb_msg *));
+static void printdate __P((void));
+static int32_t gmt2local __P((time_t));
+
+#define MODE_SCRIPT 1
+#define MODE_CMDDUMP 2
+#define MODE_CMDFLUSH 3
+#define MODE_PROMISC 4
+
+int so;
+
+int f_forever = 0;
+int f_all = 0;
+int f_verbose = 0;
+int f_mode = 0;
+int f_cmddump = 0;
+int f_policy = 0;
+int f_hexdump = 0;
+int f_tflag = 0;
+static time_t thiszone;
+
+extern int lineno;
+
+extern int parse __P((FILE **));
+
+void
+usage()
+{
+
+ printf("usage: setkey [-v] -c\n");
+ printf(" setkey [-v] -f filename\n");
+ printf(" setkey [-Palv] -D\n");
+ printf(" setkey [-Pv] -F\n");
+ printf(" setkey [-h] -x\n");
+ exit(1);
+}
+
+int
+main(ac, av)
+ int ac;
+ char **av;
+{
+ FILE *fp = stdin;
+ int c;
+
+ if (ac == 1) {
+ usage();
+ /* NOTREACHED */
+ }
+
+ thiszone = gmt2local(0);
+
+ while ((c = getopt(ac, av, "acdf:hlvxDFP")) != -1) {
+ switch (c) {
+ case 'c':
+ f_mode = MODE_SCRIPT;
+ fp = stdin;
+ break;
+ case 'f':
+ f_mode = MODE_SCRIPT;
+ if ((fp = fopen(optarg, "r")) == NULL) {
+ err(-1, "fopen");
+ /*NOTREACHED*/
+ }
+ break;
+ case 'D':
+ f_mode = MODE_CMDDUMP;
+ break;
+ case 'F':
+ f_mode = MODE_CMDFLUSH;
+ break;
+ case 'a':
+ f_all = 1;
+ break;
+ case 'l':
+ f_forever = 1;
+ break;
+ case 'h':
+ f_hexdump = 1;
+ break;
+ case 'x':
+ f_mode = MODE_PROMISC;
+ f_tflag++;
+ break;
+ case 'P':
+ f_policy = 1;
+ break;
+ case 'v':
+ f_verbose = 1;
+ break;
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+
+ so = pfkey_open();
+ if (so < 0) {
+ perror("pfkey_open");
+ exit(1);
+ }
+
+ switch (f_mode) {
+ case MODE_CMDDUMP:
+ sendkeyshort(f_policy ? SADB_X_SPDDUMP: SADB_DUMP);
+ break;
+ case MODE_CMDFLUSH:
+ sendkeyshort(f_policy ? SADB_X_SPDFLUSH: SADB_FLUSH);
+ break;
+ case MODE_SCRIPT:
+ if (get_supported() < 0) {
+ errx(-1, "%s", ipsec_strerror());
+ /*NOTREACHED*/
+ }
+ if (parse(&fp))
+ exit (1);
+ break;
+ case MODE_PROMISC:
+ promisc();
+ /*NOTREACHED*/
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+
+ exit(0);
+}
+
+int
+get_supported()
+{
+
+ if (pfkey_send_register(so, SADB_SATYPE_UNSPEC) < 0)
+ return -1;
+
+ if (pfkey_recv_register(so) < 0)
+ return -1;
+
+ return 0;
+}
+
+void
+sendkeyshort(type)
+ u_int type;
+{
+ struct sadb_msg msg;
+
+ msg.sadb_msg_version = PF_KEY_V2;
+ msg.sadb_msg_type = type;
+ msg.sadb_msg_errno = 0;
+ msg.sadb_msg_satype = SADB_SATYPE_UNSPEC;
+ msg.sadb_msg_len = PFKEY_UNIT64(sizeof(msg));
+ msg.sadb_msg_reserved = 0;
+ msg.sadb_msg_seq = 0;
+ msg.sadb_msg_pid = getpid();
+
+ sendkeymsg((char *)&msg, sizeof(msg));
+
+ return;
+}
+
+void
+promisc()
+{
+ struct sadb_msg msg;
+ u_char rbuf[1024 * 32]; /* XXX: Enough ? Should I do MSG_PEEK ? */
+ ssize_t l;
+
+ msg.sadb_msg_version = PF_KEY_V2;
+ msg.sadb_msg_type = SADB_X_PROMISC;
+ msg.sadb_msg_errno = 0;
+ msg.sadb_msg_satype = 1;
+ msg.sadb_msg_len = PFKEY_UNIT64(sizeof(msg));
+ msg.sadb_msg_reserved = 0;
+ msg.sadb_msg_seq = 0;
+ msg.sadb_msg_pid = getpid();
+
+ if ((l = send(so, &msg, sizeof(msg), 0)) < 0) {
+ err(1, "send");
+ /*NOTREACHED*/
+ }
+
+ while (1) {
+ struct sadb_msg *base;
+
+ if ((l = recv(so, rbuf, sizeof(*base), MSG_PEEK)) < 0) {
+ err(1, "recv");
+ /*NOTREACHED*/
+ }
+
+ if (l != sizeof(*base))
+ continue;
+
+ base = (struct sadb_msg *)rbuf;
+ if ((l = recv(so, rbuf, PFKEY_UNUNIT64(base->sadb_msg_len),
+ 0)) < 0) {
+ err(1, "recv");
+ /*NOTREACHED*/
+ }
+ printdate();
+ if (f_hexdump) {
+ int i;
+ for (i = 0; i < l; i++) {
+ if (i % 16 == 0)
+ printf("%08x: ", i);
+ printf("%02x ", rbuf[i] & 0xff);
+ if (i % 16 == 15)
+ printf("\n");
+ }
+ if (l % 16)
+ printf("\n");
+ }
+ /* adjust base pointer for promisc mode */
+ if (base->sadb_msg_type == SADB_X_PROMISC) {
+ if ((ssize_t)sizeof(*base) < l)
+ base++;
+ else
+ base = NULL;
+ }
+ if (base) {
+ kdebug_sadb(base);
+ printf("\n");
+ fflush(stdout);
+ }
+ }
+}
+
+int
+sendkeymsg(buf, len)
+ char *buf;
+ size_t len;
+{
+ u_char rbuf[1024 * 32]; /* XXX: Enough ? Should I do MSG_PEEK ? */
+ ssize_t l;
+ struct sadb_msg *msg;
+
+ {
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ if (setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
+ perror("setsockopt");
+ goto end;
+ }
+ }
+
+ if (f_forever)
+ shortdump_hdr();
+again:
+ if (f_verbose) {
+ kdebug_sadb((struct sadb_msg *)buf);
+ printf("\n");
+ }
+ if (f_hexdump) {
+ int i;
+ for (i = 0; i < len; i++) {
+ if (i % 16 == 0)
+ printf("%08x: ", i);
+ printf("%02x ", buf[i] & 0xff);
+ if (i % 16 == 15)
+ printf("\n");
+ }
+ if (len % 16)
+ printf("\n");
+ }
+
+ if ((l = send(so, buf, len, 0)) < 0) {
+ perror("send");
+ goto end;
+ }
+
+ msg = (struct sadb_msg *)rbuf;
+ do {
+ if ((l = recv(so, rbuf, sizeof(rbuf), 0)) < 0) {
+ perror("recv");
+ goto end;
+ }
+
+ if (PFKEY_UNUNIT64(msg->sadb_msg_len) != l) {
+ warnx("invalid keymsg length");
+ break;
+ }
+
+ if (f_verbose) {
+ kdebug_sadb((struct sadb_msg *)rbuf);
+ printf("\n");
+ }
+ if (postproc(msg, l) < 0)
+ break;
+ } while (msg->sadb_msg_errno || msg->sadb_msg_seq);
+
+ if (f_forever) {
+ fflush(stdout);
+ sleep(1);
+ goto again;
+ }
+
+end:
+ return(0);
+}
+
+int
+postproc(msg, len)
+ struct sadb_msg *msg;
+ int len;
+{
+
+ if (msg->sadb_msg_errno != 0) {
+ char inf[80];
+ const char *errmsg = NULL;
+
+ if (f_mode == MODE_SCRIPT)
+ snprintf(inf, sizeof(inf), "The result of line %d: ", lineno);
+ else
+ inf[0] = '\0';
+
+ switch (msg->sadb_msg_errno) {
+ case ENOENT:
+ switch (msg->sadb_msg_type) {
+ case SADB_DELETE:
+ case SADB_GET:
+ case SADB_X_SPDDELETE:
+ errmsg = "No entry";
+ break;
+ case SADB_DUMP:
+ errmsg = "No SAD entries";
+ break;
+ case SADB_X_SPDDUMP:
+ errmsg = "No SPD entries";
+ break;
+ }
+ break;
+ default:
+ errmsg = strerror(msg->sadb_msg_errno);
+ }
+ printf("%s%s.\n", inf, errmsg);
+ return(-1);
+ }
+
+ switch (msg->sadb_msg_type) {
+ case SADB_GET:
+ pfkey_sadump(msg);
+ break;
+
+ case SADB_DUMP:
+ /* filter out DEAD SAs */
+ if (!f_all) {
+ caddr_t mhp[SADB_EXT_MAX + 1];
+ struct sadb_sa *sa;
+ pfkey_align(msg, mhp);
+ pfkey_check(mhp);
+ if ((sa = (struct sadb_sa *)mhp[SADB_EXT_SA]) != NULL) {
+ if (sa->sadb_sa_state == SADB_SASTATE_DEAD)
+ break;
+ }
+ }
+ if (f_forever)
+ shortdump(msg);
+ else
+ pfkey_sadump(msg);
+ msg = (struct sadb_msg *)((caddr_t)msg +
+ PFKEY_UNUNIT64(msg->sadb_msg_len));
+ if (f_verbose) {
+ kdebug_sadb((struct sadb_msg *)msg);
+ printf("\n");
+ }
+ break;
+
+ case SADB_X_SPDDUMP:
+ pfkey_spdump(msg);
+ if (msg->sadb_msg_seq == 0) break;
+ msg = (struct sadb_msg *)((caddr_t)msg +
+ PFKEY_UNUNIT64(msg->sadb_msg_len));
+ if (f_verbose) {
+ kdebug_sadb((struct sadb_msg *)msg);
+ printf("\n");
+ }
+ break;
+ }
+
+ return(0);
+}
+
+/*------------------------------------------------------------*/
+static const char *satype[] = {
+ NULL, NULL, "ah", "esp"
+};
+static const char *sastate[] = {
+ "L", "M", "D", "d"
+};
+static const char *ipproto[] = {
+/*0*/ "ip", "icmp", "igmp", "ggp", "ip4",
+ NULL, "tcp", NULL, "egp", NULL,
+/*10*/ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, "udp", NULL, NULL,
+/*20*/ NULL, NULL, "idp", NULL, NULL,
+ NULL, NULL, NULL, NULL, "tp",
+/*30*/ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+/*40*/ NULL, "ip6", NULL, "rt6", "frag6",
+ NULL, "rsvp", "gre", NULL, NULL,
+/*50*/ "esp", "ah", NULL, NULL, NULL,
+ NULL, NULL, NULL, "icmp6", "none",
+/*60*/ "dst6",
+};
+
+#define STR_OR_ID(x, tab) \
+ (((x) < sizeof(tab)/sizeof(tab[0]) && tab[(x)]) ? tab[(x)] : numstr(x))
+
+const char *
+numstr(x)
+ int x;
+{
+ static char buf[20];
+ snprintf(buf, sizeof(buf), "#%d", x);
+ return buf;
+}
+
+void
+shortdump_hdr()
+{
+ printf("%-4s %-3s %-1s %-8s %-7s %s -> %s\n",
+ "time", "p", "s", "spi", "ltime", "src", "dst");
+}
+
+void
+shortdump(msg)
+ struct sadb_msg *msg;
+{
+ caddr_t mhp[SADB_EXT_MAX + 1];
+ char buf[NI_MAXHOST], pbuf[NI_MAXSERV];
+ struct sadb_sa *sa;
+ struct sadb_address *saddr;
+ struct sadb_lifetime *lts, *lth, *ltc;
+ struct sockaddr *s;
+ u_int t;
+ time_t cur = time(0);
+
+ pfkey_align(msg, mhp);
+ pfkey_check(mhp);
+
+ printf("%02lu%02lu", (u_long)(cur % 3600) / 60, (u_long)(cur % 60));
+
+ printf(" %-3s", STR_OR_ID(msg->sadb_msg_satype, satype));
+
+ if ((sa = (struct sadb_sa *)mhp[SADB_EXT_SA]) != NULL) {
+ printf(" %-1s", STR_OR_ID(sa->sadb_sa_state, sastate));
+ printf(" %08x", (u_int32_t)ntohl(sa->sadb_sa_spi));
+ } else
+ printf("%-1s %-8s", "?", "?");
+
+ lts = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_SOFT];
+ lth = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_HARD];
+ ltc = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_CURRENT];
+ if (lts && lth && ltc) {
+ if (ltc->sadb_lifetime_addtime == 0)
+ t = (u_long)0;
+ else
+ t = (u_long)(cur - ltc->sadb_lifetime_addtime);
+ if (t >= 1000)
+ strlcpy(buf, " big/", sizeof(buf));
+ else
+ snprintf(buf, sizeof(buf), " %3lu/", (u_long)t);
+ printf("%s", buf);
+
+ t = (u_long)lth->sadb_lifetime_addtime;
+ if (t >= 1000)
+ strlcpy(buf, "big", sizeof(buf));
+ else
+ snprintf(buf, sizeof(buf), "%-3lu", (u_long)t);
+ printf("%s", buf);
+ } else
+ printf(" ??\?/???"); /* backslash to avoid trigraph ??/ */
+
+ printf(" ");
+
+ if ((saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]) != NULL) {
+ if (saddr->sadb_address_proto)
+ printf("%s ", STR_OR_ID(saddr->sadb_address_proto, ipproto));
+ s = (struct sockaddr *)(saddr + 1);
+ getnameinfo(s, s->sa_len, buf, sizeof(buf),
+ pbuf, sizeof(pbuf), NI_NUMERICHOST|NI_NUMERICSERV);
+ if (strcmp(pbuf, "0") != 0)
+ printf("%s[%s]", buf, pbuf);
+ else
+ printf("%s", buf);
+ } else
+ printf("?");
+
+ printf(" -> ");
+
+ if ((saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]) != NULL) {
+ if (saddr->sadb_address_proto)
+ printf("%s ", STR_OR_ID(saddr->sadb_address_proto, ipproto));
+
+ s = (struct sockaddr *)(saddr + 1);
+ getnameinfo(s, s->sa_len, buf, sizeof(buf),
+ pbuf, sizeof(pbuf), NI_NUMERICHOST|NI_NUMERICSERV);
+ if (strcmp(pbuf, "0") != 0)
+ printf("%s[%s]", buf, pbuf);
+ else
+ printf("%s", buf);
+ } else
+ printf("?");
+
+ printf("\n");
+}
+
+/* From: tcpdump(1):gmt2local.c and util.c */
+/*
+ * Print the timestamp
+ */
+static void
+printdate()
+{
+ struct timeval tp;
+ int s;
+
+ if (gettimeofday(&tp, NULL) == -1) {
+ perror("gettimeofday");
+ return;
+ }
+
+ if (f_tflag == 1) {
+ /* Default */
+ s = (tp.tv_sec + thiszone ) % 86400;
+ (void)printf("%02d:%02d:%02d.%06u ",
+ s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tp.tv_usec);
+ } else if (f_tflag > 1) {
+ /* Unix timeval style */
+ (void)printf("%u.%06u ",
+ (u_int32_t)tp.tv_sec, (u_int32_t)tp.tv_usec);
+ }
+
+ printf("\n");
+}
+
+/*
+ * Returns the difference between gmt and local time in seconds.
+ * Use gmtime() and localtime() to keep things simple.
+ */
+int32_t
+gmt2local(time_t t)
+{
+ register int dt, dir;
+ register struct tm *gmt, *loc;
+ struct tm sgmt;
+
+ if (t == 0)
+ t = time(NULL);
+ gmt = &sgmt;
+ *gmt = *gmtime(&t);
+ loc = localtime(&t);
+ dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 +
+ (loc->tm_min - gmt->tm_min) * 60;
+
+ /*
+ * If the year or julian day is different, we span 00:00 GMT
+ * and must add or subtract a day. Check the year first to
+ * avoid problems when the julian day wraps.
+ */
+ dir = loc->tm_year - gmt->tm_year;
+ if (dir == 0)
+ dir = loc->tm_yday - gmt->tm_yday;
+ dt += dir * 24 * 60 * 60;
+
+ return (dt);
+}
diff --git a/sbin/setkey/test-pfkey.c b/sbin/setkey/test-pfkey.c
new file mode 100644
index 0000000..b1fb238
--- /dev/null
+++ b/sbin/setkey/test-pfkey.c
@@ -0,0 +1,531 @@
+/* $FreeBSD$ */
+/* $KAME: test-pfkey.c,v 1.4 2000/06/07 00:29:14 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE 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.
+ * 3. Neither the name of the project 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 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.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <net/pfkeyv2.h>
+#include <netinet/in.h>
+#include <netkey/keydb.h>
+#include <netkey/key_var.h>
+#include <netkey/key_debug.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+
+u_char m_buf[BUFSIZ];
+u_int m_len;
+char *pname;
+
+void Usage __P((void));
+int sendkeymsg __P((void));
+void key_setsadbmsg __P((u_int));
+void key_setsadbsens __P((void));
+void key_setsadbprop __P((void));
+void key_setsadbid __P((u_int, caddr_t));
+void key_setsadblft __P((u_int, u_int));
+void key_setspirange __P((void));
+void key_setsadbkey __P((u_int, caddr_t));
+void key_setsadbsa __P((void));
+void key_setsadbaddr __P((u_int, u_int, caddr_t));
+void key_setsadbextbuf __P((caddr_t, int, caddr_t, int, caddr_t, int));
+
+void
+Usage()
+{
+ printf("Usage:\t%s number\n", pname);
+ exit(0);
+}
+
+int
+main(ac, av)
+ int ac;
+ char **av;
+{
+ pname = *av;
+
+ if (ac == 1) Usage();
+
+ key_setsadbmsg(atoi(*(av+1)));
+ sendkeymsg();
+
+ exit(0);
+}
+
+/* %%% */
+int
+sendkeymsg()
+{
+ u_char rbuf[1024 * 32]; /* XXX: Enough ? Should I do MSG_PEEK ? */
+ int so, len;
+
+ if ((so = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) < 0) {
+ perror("socket(PF_KEY)");
+ goto end;
+ }
+#if 0
+ {
+#include <sys/time.h>
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ if (setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
+ perror("setsockopt");
+ goto end;
+ }
+ }
+#endif
+
+ pfkey_sadump((struct sadb_msg *)m_buf);
+
+ if ((len = send(so, m_buf, m_len, 0)) < 0) {
+ perror("send");
+ goto end;
+ }
+
+ if ((len = recv(so, rbuf, sizeof(rbuf), 0)) < 0) {
+ perror("recv");
+ goto end;
+ }
+
+ pfkey_sadump((struct sadb_msg *)rbuf);
+
+end:
+ (void)close(so);
+ return(0);
+}
+
+void
+key_setsadbmsg(type)
+ u_int type;
+{
+ struct sadb_msg m_msg;
+
+ memset(&m_msg, 0, sizeof(m_msg));
+ m_msg.sadb_msg_version = PF_KEY_V2;
+ m_msg.sadb_msg_type = type;
+ m_msg.sadb_msg_errno = 0;
+ m_msg.sadb_msg_satype = SADB_SATYPE_ESP;
+#if 0
+ m_msg.sadb_msg_reserved = 0;
+#endif
+ m_msg.sadb_msg_seq = 0;
+ m_msg.sadb_msg_pid = getpid();
+
+ m_len = sizeof(struct sadb_msg);
+ memcpy(m_buf, &m_msg, m_len);
+
+ switch (type) {
+ case SADB_GETSPI:
+ /*<base, address(SD), SPI range>*/
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "10.0.3.4");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "127.0.0.1");
+ key_setspirange();
+ /*<base, SA(*), address(SD)>*/
+ break;
+
+ case SADB_ADD:
+ /* <base, SA, (lifetime(HSC),) address(SD), (address(P),)
+ key(AE), (identity(SD),) (sensitivity)> */
+ key_setsadbaddr(SADB_EXT_ADDRESS_PROXY, AF_INET6, "3ffe::1");
+ case SADB_UPDATE:
+ key_setsadbsa();
+ key_setsadblft(SADB_EXT_LIFETIME_HARD, 10);
+ key_setsadblft(SADB_EXT_LIFETIME_SOFT, 5);
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "192.168.1.1");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "10.0.3.4");
+ /* XXX key_setsadbkey(SADB_EXT_KEY_AUTH, "abcde"); */
+ key_setsadbkey(SADB_EXT_KEY_AUTH, "1234567812345678");
+ key_setsadbkey(SADB_EXT_KEY_ENCRYPT, "12345678");
+ key_setsadbid(SADB_EXT_IDENTITY_SRC, "hoge1234@hoge.com");
+ key_setsadbid(SADB_EXT_IDENTITY_DST, "hage5678@hage.net");
+ key_setsadbsens();
+ /* <base, SA, (lifetime(HSC),) address(SD), (address(P),)
+ (identity(SD),) (sensitivity)> */
+ break;
+
+ case SADB_DELETE:
+ /* <base, SA(*), address(SDP)> */
+ key_setsadbsa();
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "192.168.1.1");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "10.0.3.4");
+ key_setsadbaddr(SADB_EXT_ADDRESS_PROXY, AF_INET6, "3ffe::1");
+ /* <base, SA(*), address(SDP)> */
+ break;
+
+ case SADB_GET:
+ /* <base, SA(*), address(SDP)> */
+ key_setsadbsa();
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "192.168.1.1");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "10.0.3.4");
+ key_setsadbaddr(SADB_EXT_ADDRESS_PROXY, AF_INET6, "3ffe::1");
+ /* <base, SA, (lifetime(HSC),) address(SD), (address(P),)
+ key(AE), (identity(SD),) (sensitivity)> */
+ break;
+
+ case SADB_ACQUIRE:
+ /* <base, address(SD), (address(P),) (identity(SD),)
+ (sensitivity,) proposal> */
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "192.168.1.1");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "10.0.3.4");
+ key_setsadbaddr(SADB_EXT_ADDRESS_PROXY, AF_INET6, "3ffe::1");
+ key_setsadbid(SADB_EXT_IDENTITY_SRC, "hoge1234@hoge.com");
+ key_setsadbid(SADB_EXT_IDENTITY_DST, "hage5678@hage.net");
+ key_setsadbsens();
+ key_setsadbprop();
+ /* <base, address(SD), (address(P),) (identity(SD),)
+ (sensitivity,) proposal> */
+ break;
+
+ case SADB_REGISTER:
+ /* <base> */
+ /* <base, supported> */
+ break;
+
+ case SADB_EXPIRE:
+ case SADB_FLUSH:
+ break;
+
+ case SADB_DUMP:
+ break;
+
+ case SADB_X_PROMISC:
+ /* <base> */
+ /* <base, base(, others)> */
+ break;
+
+ case SADB_X_PCHANGE:
+ break;
+
+ /* for SPD management */
+ case SADB_X_SPDFLUSH:
+ case SADB_X_SPDDUMP:
+ break;
+
+ case SADB_X_SPDADD:
+#if 0
+ {
+ struct sadb_x_policy m_policy;
+
+ m_policy.sadb_x_policy_len = PFKEY_UNIT64(sizeof(m_policy));
+ m_policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY;
+ m_policy.sadb_x_policy_type = SADB_X_PL_IPSEC;
+ m_policy.sadb_x_policy_esp_trans = 1;
+ m_policy.sadb_x_policy_ah_trans = 2;
+ m_policy.sadb_x_policy_esp_network = 3;
+ m_policy.sadb_x_policy_ah_network = 4;
+ m_policy.sadb_x_policy_reserved = 0;
+
+ memcpy(m_buf + m_len, &m_policy, sizeof(struct sadb_x_policy));
+ m_len += sizeof(struct sadb_x_policy);
+ }
+#endif
+
+ case SADB_X_SPDDELETE:
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "192.168.1.1");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "10.0.3.4");
+ break;
+ }
+
+ ((struct sadb_msg *)m_buf)->sadb_msg_len = PFKEY_UNIT64(m_len);
+
+ return;
+}
+
+void
+key_setsadbsens()
+{
+ struct sadb_sens m_sens;
+ u_char buf[64];
+ u_int s, i, slen, ilen, len;
+
+ /* make sens & integ */
+ s = htonl(0x01234567);
+ i = htonl(0x89abcdef);
+ slen = sizeof(s);
+ ilen = sizeof(i);
+ memcpy(buf, &s, slen);
+ memcpy(buf + slen, &i, ilen);
+
+ len = sizeof(m_sens) + PFKEY_ALIGN8(slen) + PFKEY_ALIGN8(ilen);
+ m_sens.sadb_sens_len = PFKEY_UNIT64(len);
+ m_sens.sadb_sens_exttype = SADB_EXT_SENSITIVITY;
+ m_sens.sadb_sens_dpd = 1;
+ m_sens.sadb_sens_sens_level = 2;
+ m_sens.sadb_sens_sens_len = PFKEY_ALIGN8(slen);
+ m_sens.sadb_sens_integ_level = 3;
+ m_sens.sadb_sens_integ_len = PFKEY_ALIGN8(ilen);
+ m_sens.sadb_sens_reserved = 0;
+
+ key_setsadbextbuf(m_buf, m_len,
+ (caddr_t)&m_sens, sizeof(struct sadb_sens),
+ buf, slen + ilen);
+ m_len += len;
+
+ return;
+}
+
+void
+key_setsadbprop()
+{
+ struct sadb_prop m_prop;
+ struct sadb_comb *m_comb;
+ u_char buf[256];
+ u_int len = sizeof(m_prop) + sizeof(m_comb) * 2;
+
+ /* make prop & comb */
+ m_prop.sadb_prop_len = PFKEY_UNIT64(len);
+ m_prop.sadb_prop_exttype = SADB_EXT_PROPOSAL;
+ m_prop.sadb_prop_replay = 0;
+ m_prop.sadb_prop_reserved[0] = 0;
+ m_prop.sadb_prop_reserved[1] = 0;
+ m_prop.sadb_prop_reserved[2] = 0;
+
+ /* the 1st is ESP DES-CBC HMAC-MD5 */
+ m_comb = (struct sadb_comb *)buf;
+ m_comb->sadb_comb_auth = SADB_AALG_MD5HMAC;
+ m_comb->sadb_comb_encrypt = SADB_EALG_DESCBC;
+ m_comb->sadb_comb_flags = 0;
+ m_comb->sadb_comb_auth_minbits = 8;
+ m_comb->sadb_comb_auth_maxbits = 96;
+ m_comb->sadb_comb_encrypt_minbits = 64;
+ m_comb->sadb_comb_encrypt_maxbits = 64;
+ m_comb->sadb_comb_reserved = 0;
+ m_comb->sadb_comb_soft_allocations = 0;
+ m_comb->sadb_comb_hard_allocations = 0;
+ m_comb->sadb_comb_soft_bytes = 0;
+ m_comb->sadb_comb_hard_bytes = 0;
+ m_comb->sadb_comb_soft_addtime = 0;
+ m_comb->sadb_comb_hard_addtime = 0;
+ m_comb->sadb_comb_soft_usetime = 0;
+ m_comb->sadb_comb_hard_usetime = 0;
+
+ /* the 2st is ESP 3DES-CBC and AH HMAC-SHA1 */
+ m_comb = (struct sadb_comb *)(buf + sizeof(*m_comb));
+ m_comb->sadb_comb_auth = SADB_AALG_SHA1HMAC;
+ m_comb->sadb_comb_encrypt = SADB_EALG_3DESCBC;
+ m_comb->sadb_comb_flags = 0;
+ m_comb->sadb_comb_auth_minbits = 8;
+ m_comb->sadb_comb_auth_maxbits = 96;
+ m_comb->sadb_comb_encrypt_minbits = 64;
+ m_comb->sadb_comb_encrypt_maxbits = 64;
+ m_comb->sadb_comb_reserved = 0;
+ m_comb->sadb_comb_soft_allocations = 0;
+ m_comb->sadb_comb_hard_allocations = 0;
+ m_comb->sadb_comb_soft_bytes = 0;
+ m_comb->sadb_comb_hard_bytes = 0;
+ m_comb->sadb_comb_soft_addtime = 0;
+ m_comb->sadb_comb_hard_addtime = 0;
+ m_comb->sadb_comb_soft_usetime = 0;
+ m_comb->sadb_comb_hard_usetime = 0;
+
+ key_setsadbextbuf(m_buf, m_len,
+ (caddr_t)&m_prop, sizeof(struct sadb_prop),
+ buf, sizeof(*m_comb) * 2);
+ m_len += len;
+
+ return;
+}
+
+void
+key_setsadbid(ext, str)
+ u_int ext;
+ caddr_t str;
+{
+ struct sadb_ident m_id;
+ u_int idlen = strlen(str), len;
+
+ len = sizeof(m_id) + PFKEY_ALIGN8(idlen);
+ m_id.sadb_ident_len = PFKEY_UNIT64(len);
+ m_id.sadb_ident_exttype = ext;
+ m_id.sadb_ident_type = SADB_IDENTTYPE_USERFQDN;
+ m_id.sadb_ident_reserved = 0;
+ m_id.sadb_ident_id = getpid();
+
+ key_setsadbextbuf(m_buf, m_len,
+ (caddr_t)&m_id, sizeof(struct sadb_ident),
+ str, idlen);
+ m_len += len;
+
+ return;
+}
+
+void
+key_setsadblft(ext, time)
+ u_int ext, time;
+{
+ struct sadb_lifetime m_lft;
+
+ m_lft.sadb_lifetime_len = PFKEY_UNIT64(sizeof(m_lft));
+ m_lft.sadb_lifetime_exttype = ext;
+ m_lft.sadb_lifetime_allocations = 0x2;
+ m_lft.sadb_lifetime_bytes = 0x1000;
+ m_lft.sadb_lifetime_addtime = time;
+ m_lft.sadb_lifetime_usetime = 0x0020;
+
+ memcpy(m_buf + m_len, &m_lft, sizeof(struct sadb_lifetime));
+ m_len += sizeof(struct sadb_lifetime);
+
+ return;
+}
+
+void
+key_setspirange()
+{
+ struct sadb_spirange m_spi;
+
+ m_spi.sadb_spirange_len = PFKEY_UNIT64(sizeof(m_spi));
+ m_spi.sadb_spirange_exttype = SADB_EXT_SPIRANGE;
+ m_spi.sadb_spirange_min = 0x00001000;
+ m_spi.sadb_spirange_max = 0x00002000;
+ m_spi.sadb_spirange_reserved = 0;
+
+ memcpy(m_buf + m_len, &m_spi, sizeof(struct sadb_spirange));
+ m_len += sizeof(struct sadb_spirange);
+
+ return;
+}
+
+void
+key_setsadbkey(ext, str)
+ u_int ext;
+ caddr_t str;
+{
+ struct sadb_key m_key;
+ u_int keylen = strlen(str);
+ u_int len;
+
+ len = sizeof(struct sadb_key) + PFKEY_ALIGN8(keylen);
+ m_key.sadb_key_len = PFKEY_UNIT64(len);
+ m_key.sadb_key_exttype = ext;
+ m_key.sadb_key_bits = keylen * 8;
+ m_key.sadb_key_reserved = 0;
+
+ key_setsadbextbuf(m_buf, m_len,
+ (caddr_t)&m_key, sizeof(struct sadb_key),
+ str, keylen);
+ m_len += len;
+
+ return;
+}
+
+void
+key_setsadbsa()
+{
+ struct sadb_sa m_sa;
+
+ m_sa.sadb_sa_len = PFKEY_UNIT64(sizeof(struct sadb_sa));
+ m_sa.sadb_sa_exttype = SADB_EXT_SA;
+ m_sa.sadb_sa_spi = htonl(0x12345678);
+ m_sa.sadb_sa_replay = 4;
+ m_sa.sadb_sa_state = 0;
+ m_sa.sadb_sa_auth = SADB_AALG_MD5HMAC;
+ m_sa.sadb_sa_encrypt = SADB_EALG_DESCBC;
+ m_sa.sadb_sa_flags = 0;
+
+ memcpy(m_buf + m_len, &m_sa, sizeof(struct sadb_sa));
+ m_len += sizeof(struct sadb_sa);
+
+ return;
+}
+
+void
+key_setsadbaddr(ext, af, str)
+ u_int ext, af;
+ caddr_t str;
+{
+ struct sadb_address m_addr;
+ u_int len;
+ struct addrinfo hints, *res;
+ const char *serv;
+ int plen;
+
+ switch (af) {
+ case AF_INET:
+ plen = sizeof(struct in_addr) << 3;
+ break;
+ case AF_INET6:
+ plen = sizeof(struct in6_addr) << 3;
+ break;
+ default:
+ /* XXX bark */
+ exit(1);
+ }
+
+ /* make sockaddr buffer */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+ serv = (ext == SADB_EXT_ADDRESS_PROXY ? "0" : "4660"); /*0x1234*/
+ if (getaddrinfo(str, serv, &hints, &res) != 0 || res->ai_next) {
+ /* XXX bark */
+ exit(1);
+ }
+
+ len = sizeof(struct sadb_address) + PFKEY_ALIGN8(res->ai_addrlen);
+ m_addr.sadb_address_len = PFKEY_UNIT64(len);
+ m_addr.sadb_address_exttype = ext;
+ m_addr.sadb_address_proto =
+ (ext == SADB_EXT_ADDRESS_PROXY ? 0 : IPPROTO_TCP);
+ m_addr.sadb_address_prefixlen = plen;
+ m_addr.sadb_address_reserved = 0;
+
+ key_setsadbextbuf(m_buf, m_len,
+ (caddr_t)&m_addr, sizeof(struct sadb_address),
+ (caddr_t)res->ai_addr, res->ai_addrlen);
+ m_len += len;
+
+ freeaddrinfo(res);
+
+ return;
+}
+
+void
+key_setsadbextbuf(dst, off, ebuf, elen, vbuf, vlen)
+ caddr_t dst, ebuf, vbuf;
+ int off, elen, vlen;
+{
+ memset(dst + off, 0, elen + vlen);
+ memcpy(dst + off, (caddr_t)ebuf, elen);
+ memcpy(dst + off + elen, vbuf, vlen);
+
+ return;
+}
+
diff --git a/sbin/setkey/test-policy.c b/sbin/setkey/test-policy.c
new file mode 100644
index 0000000..27cd478
--- /dev/null
+++ b/sbin/setkey/test-policy.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE 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.
+ * 3. Neither the name of the project 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet6/in6.h>
+#include <netkey/keyv2.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <netinet6/ipsec.h>
+
+char *requests[] = {
+"must_error", /* must be error */
+"ipsec must_error", /* must be error */
+"ipsec esp/must_error", /* must be error */
+"discard",
+"none",
+"entrust",
+"bypass", /* may be error */
+"ipsec esp", /* must be error */
+"ipsec ah/require",
+"ipsec ah/use/",
+"ipsec esp/require ah/default/203.178.141.194",
+"ipsec ah/use/203.178.141.195 esp/use/203.178.141.194",
+"ipsec esp/elf.wide.ydc.co.jp esp/www.wide.ydc.co.jp"
+"
+ipsec esp/require ah/use esp/require/10.0.0.1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1ah/use/3ffe:501:481d::1
+",
+};
+
+u_char *p_secpolicy;
+
+int test(char *buf, int family);
+char *setpolicy(char *req);
+
+main()
+{
+ int i;
+ char *buf;
+
+ for (i = 0; i < sizeof(requests)/sizeof(requests[0]); i++) {
+ printf("* requests:[%s]\n", requests[i]);
+ if ((buf = setpolicy(requests[i])) == NULL)
+ continue;
+ printf("\tsetlen:%d\n", PFKEY_EXTLEN(buf));
+
+ printf("\tPF_INET:\n");
+ test(buf, PF_INET);
+
+ printf("\tPF_INET6:\n");
+ test(buf, PF_INET6);
+ free(buf);
+ }
+}
+
+int test(char *policy, int family)
+{
+ int so, proto, optname;
+ int len;
+ char getbuf[1024];
+
+ switch (family) {
+ case PF_INET:
+ proto = IPPROTO_IP;
+ optname = IP_IPSEC_POLICY;
+ break;
+ case PF_INET6:
+ proto = IPPROTO_IPV6;
+ optname = IPV6_IPSEC_POLICY;
+ break;
+ }
+
+ if ((so = socket(family, SOCK_DGRAM, 0)) < 0)
+ perror("socket");
+
+ if (setsockopt(so, proto, optname, policy, PFKEY_EXTLEN(policy)) < 0)
+ perror("setsockopt");
+
+ len = sizeof(getbuf);
+ memset(getbuf, 0, sizeof(getbuf));
+ if (getsockopt(so, proto, optname, getbuf, &len) < 0)
+ perror("getsockopt");
+
+ {
+ char *buf = NULL;
+
+ printf("\tgetlen:%d\n", len);
+
+ if ((buf = ipsec_dump_policy(getbuf, NULL)) == NULL)
+ ipsec_strerror();
+ else
+ printf("\t[%s]\n", buf);
+
+ free(buf);
+ }
+
+ close (so);
+}
+
+char *setpolicy(char *req)
+{
+ int len;
+ char *buf;
+
+ if ((len = ipsec_get_policylen(req)) < 0) {
+ printf("ipsec_get_policylen: %s\n", ipsec_strerror());
+ return NULL;
+ }
+
+ if ((buf = malloc(len)) == NULL) {
+ perror("malloc");
+ return NULL;
+ }
+
+ if ((len = ipsec_set_policy(buf, len, req)) < 0) {
+ printf("ipsec_set_policy: %s\n", ipsec_strerror());
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
diff --git a/sbin/setkey/token.l b/sbin/setkey/token.l
new file mode 100644
index 0000000..74c1e17
--- /dev/null
+++ b/sbin/setkey/token.l
@@ -0,0 +1,286 @@
+/* $FreeBSD$ */
+/* $KAME: token.l,v 1.43 2003/07/25 09:35:28 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE 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.
+ * 3. Neither the name of the project 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 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.
+ */
+
+%{
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <net/pfkeyv2.h>
+#include <netkey/keydb.h>
+#include <netkey/key_debug.h>
+#include <netinet/in.h>
+#include <netinet6/ipsec.h>
+
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+
+#include "vchar.h"
+#include "y.tab.h"
+
+int lineno = 1;
+
+extern u_char m_buf[BUFSIZ];
+extern u_int m_len;
+extern int f_debug;
+
+int yylex __P((void));
+void yyfatal __P((const char *s));
+void yyerror __P((const char *s));
+extern void parse_init __P((void));
+int parse __P((FILE **));
+int yyparse __P((void));
+%}
+
+/* common section */
+nl \n
+ws [ \t]+
+digit [0-9]
+letter [0-9A-Za-z]
+hexdigit [0-9A-Fa-f]
+dot \.
+hyphen \-
+slash \/
+blcl \[
+elcl \]
+semi \;
+comment \#.*
+quotedstring \"[^"]*\"
+decstring {digit}+
+hexstring 0[xX]{hexdigit}+
+ipaddress [a-fA-F0-9:]([a-fA-F0-9:\.]*|[a-fA-F0-9:\.]*%[a-zA-Z0-9]*)
+ipaddrmask {slash}{digit}{1,3}
+name {letter}(({letter}|{digit}|{hyphen})*({letter}|{digit}))*
+hostname {name}(({dot}{name})+{dot}?)?
+
+%s S_PL S_AUTHALG S_ENCALG
+
+%%
+
+add { return(ADD); }
+delete { return(DELETE); }
+deleteall { return(DELETEALL); }
+get { return(GET); }
+flush { return(FLUSH); }
+dump { return(DUMP); }
+
+ /* for management SPD */
+spdadd { return(SPDADD); }
+spddelete { return(SPDDELETE); }
+spddump { return(SPDDUMP); }
+spdflush { return(SPDFLUSH); }
+tagged { return(TAGGED); }
+{hyphen}P { BEGIN S_PL; return(F_POLICY); }
+<S_PL>[a-zA-Z0-9:\.\-_/ \n\t][a-zA-Z0-9:\.%\-_/ \n\t]* {
+ yymore();
+
+ /* count up for nl */
+ {
+ char *p;
+ for (p = yytext; *p != '\0'; p++)
+ if (*p == '\n')
+ lineno++;
+ }
+
+ yylval.val.len = strlen(yytext);
+ yylval.val.buf = strdup(yytext);
+ if (!yylval.val.buf)
+ yyfatal("insufficient memory");
+
+ return(PL_REQUESTS);
+ }
+<S_PL>{semi} { BEGIN INITIAL; return(EOT); }
+
+ /* address resolution flags */
+{hyphen}[n46][n46]* {
+ yylval.val.len = strlen(yytext);
+ yylval.val.buf = strdup(yytext);
+ if (!yylval.val.buf)
+ yyfatal("insufficient memory");
+ return(F_AIFLAGS);
+ }
+
+ /* security protocols */
+ah { yylval.num = 0; return(PR_AH); }
+esp { yylval.num = 0; return(PR_ESP); }
+ah-old { yylval.num = 1; return(PR_AH); }
+esp-old { yylval.num = 1; return(PR_ESP); }
+ipcomp { yylval.num = 0; return(PR_IPCOMP); }
+tcp { yylval.num = 0; return(PR_TCP); }
+
+ /* authentication alogorithm */
+{hyphen}A { BEGIN S_AUTHALG; return(F_AUTH); }
+<S_AUTHALG>hmac-md5 { yylval.num = SADB_AALG_MD5HMAC; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>hmac-sha1 { yylval.num = SADB_AALG_SHA1HMAC; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>keyed-md5 { yylval.num = SADB_X_AALG_MD5; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>keyed-sha1 { yylval.num = SADB_X_AALG_SHA; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>hmac-sha2-256 { yylval.num = SADB_X_AALG_SHA2_256; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>hmac-sha2-384 { yylval.num = SADB_X_AALG_SHA2_384; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>hmac-sha2-512 { yylval.num = SADB_X_AALG_SHA2_512; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>hmac-ripemd160 { yylval.num = SADB_X_AALG_RIPEMD160HMAC; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>aes-xcbc-mac { yylval.num = SADB_X_AALG_AES_XCBC_MAC; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>tcp-md5 { yylval.num = SADB_X_AALG_TCP_MD5; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>null { yylval.num = SADB_X_AALG_NULL; BEGIN INITIAL; return(ALG_AUTH_NOKEY); }
+
+ /* encryption alogorithm */
+{hyphen}E { BEGIN S_ENCALG; return(F_ENC); }
+<S_ENCALG>des-cbc { yylval.num = SADB_EALG_DESCBC; BEGIN INITIAL; return(ALG_ENC); }
+<S_ENCALG>3des-cbc { yylval.num = SADB_EALG_3DESCBC; BEGIN INITIAL; return(ALG_ENC); }
+<S_ENCALG>null { yylval.num = SADB_EALG_NULL; BEGIN INITIAL; return(ALG_ENC_NOKEY); }
+<S_ENCALG>simple { yylval.num = SADB_EALG_NULL; BEGIN INITIAL; return(ALG_ENC_OLD); }
+<S_ENCALG>blowfish-cbc { yylval.num = SADB_X_EALG_BLOWFISHCBC; BEGIN INITIAL; return(ALG_ENC); }
+<S_ENCALG>cast128-cbc { yylval.num = SADB_X_EALG_CAST128CBC; BEGIN INITIAL; return(ALG_ENC); }
+<S_ENCALG>des-deriv { yylval.num = SADB_EALG_DESCBC; BEGIN INITIAL; return(ALG_ENC_DESDERIV); }
+<S_ENCALG>des-32iv { yylval.num = SADB_EALG_DESCBC; BEGIN INITIAL; return(ALG_ENC_DES32IV); }
+<S_ENCALG>rijndael-cbc { yylval.num = SADB_X_EALG_RIJNDAELCBC; BEGIN INITIAL; return(ALG_ENC); }
+<S_ENCALG>aes-ctr { yylval.num = SADB_X_EALG_AESCTR; BEGIN INITIAL; return(ALG_ENC); }
+
+ /* compression algorithms */
+{hyphen}C { return(F_COMP); }
+oui { yylval.num = SADB_X_CALG_OUI; return(ALG_COMP); }
+deflate { yylval.num = SADB_X_CALG_DEFLATE; return(ALG_COMP); }
+lzs { yylval.num = SADB_X_CALG_LZS; return(ALG_COMP); }
+{hyphen}R { return(F_RAWCPI); }
+
+ /* extension */
+{hyphen}m { return(F_MODE); }
+transport { yylval.num = IPSEC_MODE_TRANSPORT; return(MODE); }
+tunnel { yylval.num = IPSEC_MODE_TUNNEL; return(MODE); }
+{hyphen}u { return(F_REQID); }
+{hyphen}f { return(F_EXT); }
+random-pad { yylval.num = SADB_X_EXT_PRAND; return(EXTENSION); }
+seq-pad { yylval.num = SADB_X_EXT_PSEQ; return(EXTENSION); }
+zero-pad { yylval.num = SADB_X_EXT_PZERO; return(EXTENSION); }
+nocyclic-seq { return(NOCYCLICSEQ); }
+{hyphen}r { return(F_REPLAY); }
+{hyphen}lh { return(F_LIFETIME_HARD); }
+{hyphen}ls { return(F_LIFETIME_SOFT); }
+
+ /* ... */
+any { return(ANY); }
+{ws} { }
+{nl} { lineno++; }
+{comment}
+{semi} { return(EOT); }
+
+ /* for address parameters: /prefix, [port] */
+{slash} { return SLASH; }
+{blcl} { return BLCL; }
+{elcl} { return ELCL; }
+
+ /* parameter */
+{decstring} {
+ char *bp;
+
+ yylval.ulnum = strtoul(yytext, &bp, 10);
+ return(DECSTRING);
+ }
+
+{hexstring} {
+ yylval.val.buf = strdup(yytext + 2);
+ if (!yylval.val.buf)
+ yyfatal("insufficient memory");
+ yylval.val.len = strlen(yylval.val.buf);
+
+ return(HEXSTRING);
+ }
+
+{quotedstring} {
+ char *p = yytext;
+ while (*++p != '"') ;
+ *p = '\0';
+ yytext++;
+ yylval.val.len = yyleng - 2;
+ yylval.val.buf = strdup(yytext);
+ if (!yylval.val.buf)
+ yyfatal("insufficient memory");
+
+ return(QUOTEDSTRING);
+ }
+
+[A-Za-z0-9:][A-Za-z0-9:%\.-]* {
+ yylval.val.len = yyleng;
+ yylval.val.buf = strdup(yytext);
+ if (!yylval.val.buf)
+ yyfatal("insufficient memory");
+ return(STRING);
+ }
+
+[0-9,]+ {
+ yylval.val.len = yyleng;
+ yylval.val.buf = strdup(yytext);
+ if (!yylval.val.buf)
+ yyfatal("insufficient memory");
+ return(STRING);
+ }
+
+. {
+ yyfatal("Syntax error");
+ /*NOTREACHED*/
+ }
+
+%%
+
+void
+yyfatal(s)
+ const char *s;
+{
+ yyerror(s);
+ exit(1);
+}
+
+void
+yyerror(s)
+ const char *s;
+{
+ printf("line %d: %s at [%s]\n", lineno, s, yytext);
+}
+
+int
+parse(fp)
+ FILE **fp;
+{
+ yyin = *fp;
+
+ parse_init();
+
+ if (yyparse()) {
+ printf("parse failed, line %d.\n", lineno);
+ return(-1);
+ }
+
+ return(0);
+}
diff --git a/sbin/setkey/vchar.h b/sbin/setkey/vchar.h
new file mode 100644
index 0000000..f3251c7
--- /dev/null
+++ b/sbin/setkey/vchar.h
@@ -0,0 +1,36 @@
+/* $FreeBSD$ */
+/* $KAME: vchar.h,v 1.2 2000/06/07 00:29:14 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE 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.
+ * 3. Neither the name of the project 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 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.
+ */
+
+typedef struct {
+ u_int len;
+ caddr_t buf;
+} vchar_t;
diff --git a/sbin/shutdown/Makefile b/sbin/shutdown/Makefile
new file mode 100644
index 0000000..b7f232d
--- /dev/null
+++ b/sbin/shutdown/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= shutdown
+MAN= shutdown.8
+
+WARNS?= 6
+
+BINOWN= root
+BINGRP= operator
+BINMODE=4550
+
+.include <bsd.prog.mk>
diff --git a/sbin/shutdown/shutdown.8 b/sbin/shutdown/shutdown.8
new file mode 100644
index 0000000..e68687f
--- /dev/null
+++ b/sbin/shutdown/shutdown.8
@@ -0,0 +1,190 @@
+.\" Copyright (c) 1988, 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.
+.\" 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 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.
+.\"
+.\" @(#)shutdown.8 8.2 (Berkeley) 4/27/95
+.\" $FreeBSD$
+.\"
+.Dd December 11, 1998
+.Dt SHUTDOWN 8
+.Os
+.Sh NAME
+.Nm shutdown
+.Nd "close down the system at a given time"
+.Sh SYNOPSIS
+.Nm
+.Op Fl
+.Oo
+.Fl h | Fl p |
+.Fl r | Fl k
+.Oc
+.Oo
+.Fl o
+.Op Fl n
+.Oc
+.Ar time
+.Op Ar warning-message ...
+.Sh DESCRIPTION
+The
+.Nm
+utility provides an automated shutdown procedure for super-users
+to nicely notify users when the system is shutting down,
+saving them from system administrators, hackers, and gurus, who
+would otherwise not bother with such niceties.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl h
+The system is halted at the specified
+.Ar time .
+.It Fl p
+The system is halted and the power is turned off
+(hardware support required)
+at the specified
+.Ar time .
+.It Fl r
+The system is rebooted at the specified
+.Ar time .
+.It Fl k
+Kick everybody off.
+The
+.Fl k
+option
+does not actually halt the system, but leaves the
+system multi-user with logins disabled (for all but super-user).
+.It Fl o
+If one of the
+.Fl h ,
+.Fl p
+or
+.Fl r
+is specified,
+.Nm
+will execute
+.Xr halt 8
+or
+.Xr reboot 8
+instead of sending signal to
+.Xr init 8 .
+.It Fl n
+If the
+.Fl o
+is specified, prevent the file system cache from being flushed by passing
+.Fl n
+option to
+.Xr halt 8
+or
+.Xr reboot 8 .
+This option should probably not be used.
+.It Ar time
+.Ar Time
+is the time at which
+.Nm
+will bring the system down and
+may be the word
+.Ar now
+(indicating an immediate shutdown) or
+specify a future time in one of two formats:
+.Ar +number ,
+or
+.Ar yymmddhhmm ,
+where the year, month, and day may be defaulted
+to the current system values.
+The first form brings the system down in
+.Ar number
+minutes and the second at the absolute time specified.
+.It Ar warning-message
+Any other arguments comprise the warning message that is broadcast
+to users currently logged into the system.
+.It Fl
+If
+.Sq Fl
+is supplied as an option, the warning message is read from the standard
+input.
+.El
+.Pp
+At intervals, becoming more frequent as apocalypse approaches
+and starting at ten hours before shutdown, warning messages are displayed
+on the terminals of all users logged in.
+Five minutes before
+shutdown, or immediately if shutdown is in less than 5 minutes,
+logins are disabled by creating
+.Pa /var/run/nologin
+and copying the
+warning message there.
+If this file exists when a user attempts to
+log in,
+.Xr login 1
+prints its contents and exits.
+The file is
+removed just before
+.Nm
+exits.
+.Pp
+At shutdown time a message is written to the system log, containing the
+time of shutdown, the person who initiated the shutdown and the reason.
+Corresponding signal is then sent to
+.Xr init 8
+to respectively halt, reboot or bring the system down to single-user state
+(depending on the above options).
+The time of the shutdown and the warning message
+are placed in
+.Pa /var/run/nologin
+and should be used to
+inform the users about when the system will be back up
+and why it is going down (or anything else).
+.Pp
+A scheduled shutdown can be canceled by killing the
+.Nm
+process (a
+.Dv SIGTERM
+should suffice).
+The
+.Pa /var/run/nologin
+file that
+.Nm
+created will be removed automatically.
+.Sh FILES
+.Bl -tag -width /var/run/nologin -compact
+.It Pa /var/run/nologin
+tells login not to let anyone log in
+.El
+.Sh COMPATIBILITY
+The hours and minutes in the second time format may be separated by
+a colon (``:'') for backward compatibility.
+.Sh SEE ALSO
+.Xr kill 1 ,
+.Xr login 1 ,
+.Xr wall 1 ,
+.Xr nologin 5 ,
+.Xr halt 8 ,
+.Xr init 8 ,
+.Xr reboot 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.0 .
diff --git a/sbin/shutdown/shutdown.c b/sbin/shutdown/shutdown.c
new file mode 100644
index 0000000..c4f3f41
--- /dev/null
+++ b/sbin/shutdown/shutdown.c
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 1988, 1990, 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1988, 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/syslog.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef DEBUG
+#undef _PATH_NOLOGIN
+#define _PATH_NOLOGIN "./nologin"
+#endif
+
+#define H *60*60
+#define M *60
+#define S *1
+#define NOLOG_TIME 5*60
+struct interval {
+ int timeleft, timetowait;
+} tlist[] = {
+ { 10 H, 5 H },
+ { 5 H, 3 H },
+ { 2 H, 1 H },
+ { 1 H, 30 M },
+ { 30 M, 10 M },
+ { 20 M, 10 M },
+ { 10 M, 5 M },
+ { 5 M, 3 M },
+ { 2 M, 1 M },
+ { 1 M, 30 S },
+ { 30 S, 30 S },
+ { 0 , 0 }
+};
+#undef H
+#undef M
+#undef S
+
+static time_t offset, shuttime;
+static int dohalt, dopower, doreboot, killflg, mbuflen, oflag;
+static char mbuf[BUFSIZ];
+static const char *nosync, *whom;
+
+void badtime(void);
+void die_you_gravy_sucking_pig_dog(void);
+void finish(int);
+void getoffset(char *);
+void loop(void);
+void nolog(void);
+void timeout(int);
+void timewarn(int);
+void usage(const char *);
+
+extern const char **environ;
+
+int
+main(int argc, char **argv)
+{
+ char *p, *endp;
+ struct passwd *pw;
+ int arglen, ch, len, readstdin;
+
+#ifndef DEBUG
+ if (geteuid())
+ errx(1, "NOT super-user");
+#endif
+ nosync = NULL;
+ readstdin = 0;
+ while ((ch = getopt(argc, argv, "-hknopr")) != -1)
+ switch (ch) {
+ case '-':
+ readstdin = 1;
+ break;
+ case 'h':
+ dohalt = 1;
+ break;
+ case 'k':
+ killflg = 1;
+ break;
+ case 'n':
+ nosync = "-n";
+ break;
+ case 'o':
+ oflag = 1;
+ break;
+ case 'p':
+ dopower = 1;
+ break;
+ case 'r':
+ doreboot = 1;
+ break;
+ case '?':
+ default:
+ usage((char *)NULL);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage((char *)NULL);
+
+ if (killflg + doreboot + dohalt + dopower > 1)
+ usage("incompatible switches -h, -k, -p and -r");
+
+ if (oflag && !(dohalt || dopower || doreboot))
+ usage("-o requires -h, -p or -r");
+
+ if (nosync != NULL && !oflag)
+ usage("-n requires -o");
+
+ getoffset(*argv++);
+
+ if (*argv) {
+ for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) {
+ arglen = strlen(*argv);
+ if ((len -= arglen) <= 2)
+ break;
+ if (p != mbuf)
+ *p++ = ' ';
+ memmove(p, *argv, arglen);
+ p += arglen;
+ }
+ *p = '\n';
+ *++p = '\0';
+ }
+
+ if (readstdin) {
+ p = mbuf;
+ endp = mbuf + sizeof(mbuf) - 2;
+ for (;;) {
+ if (!fgets(p, endp - p + 1, stdin))
+ break;
+ for (; *p && p < endp; ++p);
+ if (p == endp) {
+ *p = '\n';
+ *++p = '\0';
+ break;
+ }
+ }
+ }
+ mbuflen = strlen(mbuf);
+
+ if (offset)
+ (void)printf("Shutdown at %.24s.\n", ctime(&shuttime));
+ else
+ (void)printf("Shutdown NOW!\n");
+
+ if (!(whom = getlogin()))
+ whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
+
+#ifdef DEBUG
+ (void)putc('\n', stdout);
+#else
+ (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
+ {
+ int forkpid;
+
+ forkpid = fork();
+ if (forkpid == -1)
+ err(1, "fork");
+ if (forkpid)
+ errx(0, "[pid %d]", forkpid);
+ }
+ setsid();
+#endif
+ openlog("shutdown", LOG_CONS, LOG_AUTH);
+ loop();
+ return(0);
+}
+
+void
+loop()
+{
+ struct interval *tp;
+ u_int sltime;
+ int logged;
+
+ if (offset <= NOLOG_TIME) {
+ logged = 1;
+ nolog();
+ }
+ else
+ logged = 0;
+ tp = tlist;
+ if (tp->timeleft < offset)
+ (void)sleep((u_int)(offset - tp->timeleft));
+ else {
+ while (tp->timeleft && offset < tp->timeleft)
+ ++tp;
+ /*
+ * Warn now, if going to sleep more than a fifth of
+ * the next wait time.
+ */
+ if ((sltime = offset - tp->timeleft)) {
+ if (sltime > (u_int)(tp->timetowait / 5))
+ timewarn(offset);
+ (void)sleep(sltime);
+ }
+ }
+ for (;; ++tp) {
+ timewarn(tp->timeleft);
+ if (!logged && tp->timeleft <= NOLOG_TIME) {
+ logged = 1;
+ nolog();
+ }
+ (void)sleep((u_int)tp->timetowait);
+ if (!tp->timeleft)
+ break;
+ }
+ die_you_gravy_sucking_pig_dog();
+}
+
+static jmp_buf alarmbuf;
+
+static const char *restricted_environ[] = {
+ "PATH=" _PATH_STDPATH,
+ NULL
+};
+
+void
+timewarn(int timeleft)
+{
+ static int first;
+ static char hostname[MAXHOSTNAMELEN + 1];
+ FILE *pf;
+ char wcmd[MAXPATHLEN + 4];
+
+ if (!first++)
+ (void)gethostname(hostname, sizeof(hostname));
+
+ /* undoc -n option to wall suppresses normal wall banner */
+ (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL);
+ environ = restricted_environ;
+ if (!(pf = popen(wcmd, "w"))) {
+ syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL);
+ return;
+ }
+
+ (void)fprintf(pf,
+ "\007*** %sSystem shutdown message from %s@%s ***\007\n",
+ timeleft ? "": "FINAL ", whom, hostname);
+
+ if (timeleft > 10*60)
+ (void)fprintf(pf, "System going down at %5.5s\n\n",
+ ctime(&shuttime) + 11);
+ else if (timeleft > 59)
+ (void)fprintf(pf, "System going down in %d minute%s\n\n",
+ timeleft / 60, (timeleft > 60) ? "s" : "");
+ else if (timeleft)
+ (void)fprintf(pf, "System going down in 30 seconds\n\n");
+ else
+ (void)fprintf(pf, "System going down IMMEDIATELY\n\n");
+
+ if (mbuflen)
+ (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf);
+
+ /*
+ * play some games, just in case wall doesn't come back
+ * probably unnecessary, given that wall is careful.
+ */
+ if (!setjmp(alarmbuf)) {
+ (void)signal(SIGALRM, timeout);
+ (void)alarm((u_int)30);
+ (void)pclose(pf);
+ (void)alarm((u_int)0);
+ (void)signal(SIGALRM, SIG_DFL);
+ }
+}
+
+void
+timeout(int signo __unused)
+{
+ longjmp(alarmbuf, 1);
+}
+
+void
+die_you_gravy_sucking_pig_dog()
+{
+ char *empty_environ[] = { NULL };
+
+ syslog(LOG_NOTICE, "%s by %s: %s",
+ doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" :
+ "shutdown", whom, mbuf);
+ (void)sleep(2);
+
+ (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n");
+ if (killflg) {
+ (void)printf("\rbut you'll have to do it yourself\r\n");
+ exit(0);
+ }
+#ifdef DEBUG
+ if (doreboot)
+ (void)printf("reboot");
+ else if (dohalt)
+ (void)printf("halt");
+ else if (dopower)
+ (void)printf("power-down");
+ if (nosync != NULL)
+ (void)printf(" no sync");
+ (void)printf("\nkill -HUP 1\n");
+#else
+ if (!oflag) {
+ (void)kill(1, doreboot ? SIGINT : /* reboot */
+ dohalt ? SIGUSR1 : /* halt */
+ dopower ? SIGUSR2 : /* power-down */
+ SIGTERM); /* single-user */
+ } else {
+ if (doreboot) {
+ execle(_PATH_REBOOT, "reboot", "-l", nosync,
+ (char *)NULL, empty_environ);
+ syslog(LOG_ERR, "shutdown: can't exec %s: %m.",
+ _PATH_REBOOT);
+ warn(_PATH_REBOOT);
+ }
+ else if (dohalt) {
+ execle(_PATH_HALT, "halt", "-l", nosync,
+ (char *)NULL, empty_environ);
+ syslog(LOG_ERR, "shutdown: can't exec %s: %m.",
+ _PATH_HALT);
+ warn(_PATH_HALT);
+ }
+ else if (dopower) {
+ execle(_PATH_HALT, "halt", "-l", "-p", nosync,
+ (char *)NULL, empty_environ);
+ syslog(LOG_ERR, "shutdown: can't exec %s: %m.",
+ _PATH_HALT);
+ warn(_PATH_HALT);
+ }
+ (void)kill(1, SIGTERM); /* to single-user */
+ }
+#endif
+ finish(0);
+}
+
+#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2;
+
+void
+getoffset(char *timearg)
+{
+ struct tm *lt;
+ char *p;
+ time_t now;
+ int this_year;
+
+ (void)time(&now);
+
+ if (!strcasecmp(timearg, "now")) { /* now */
+ offset = 0;
+ shuttime = now;
+ return;
+ }
+
+ if (*timearg == '+') { /* +minutes */
+ if (!isdigit(*++timearg))
+ badtime();
+ if ((offset = atoi(timearg) * 60) < 0)
+ badtime();
+ shuttime = now + offset;
+ return;
+ }
+
+ /* handle hh:mm by getting rid of the colon */
+ for (p = timearg; *p; ++p)
+ if (!isascii(*p) || !isdigit(*p)) {
+ if (*p == ':' && strlen(p) == 3) {
+ p[0] = p[1];
+ p[1] = p[2];
+ p[2] = '\0';
+ }
+ else
+ badtime();
+ }
+
+ unsetenv("TZ"); /* OUR timezone */
+ lt = localtime(&now); /* current time val */
+
+ switch(strlen(timearg)) {
+ case 10:
+ this_year = lt->tm_year;
+ lt->tm_year = ATOI2(timearg);
+ /*
+ * check if the specified year is in the next century.
+ * allow for one year of user error as many people will
+ * enter n - 1 at the start of year n.
+ */
+ if (lt->tm_year < (this_year % 100) - 1)
+ lt->tm_year += 100;
+ /* adjust for the year 2000 and beyond */
+ lt->tm_year += (this_year - (this_year % 100));
+ /* FALLTHROUGH */
+ case 8:
+ lt->tm_mon = ATOI2(timearg);
+ if (--lt->tm_mon < 0 || lt->tm_mon > 11)
+ badtime();
+ /* FALLTHROUGH */
+ case 6:
+ lt->tm_mday = ATOI2(timearg);
+ if (lt->tm_mday < 1 || lt->tm_mday > 31)
+ badtime();
+ /* FALLTHROUGH */
+ case 4:
+ lt->tm_hour = ATOI2(timearg);
+ if (lt->tm_hour < 0 || lt->tm_hour > 23)
+ badtime();
+ lt->tm_min = ATOI2(timearg);
+ if (lt->tm_min < 0 || lt->tm_min > 59)
+ badtime();
+ lt->tm_sec = 0;
+ if ((shuttime = mktime(lt)) == -1)
+ badtime();
+ if ((offset = shuttime - now) < 0)
+ errx(1, "that time is already past.");
+ break;
+ default:
+ badtime();
+ }
+}
+
+#define NOMSG "\n\nNO LOGINS: System going down at "
+void
+nolog()
+{
+ int logfd;
+ char *ct;
+
+ (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */
+ (void)signal(SIGINT, finish);
+ (void)signal(SIGHUP, finish);
+ (void)signal(SIGQUIT, finish);
+ (void)signal(SIGTERM, finish);
+ if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
+ 0664)) >= 0) {
+ (void)write(logfd, NOMSG, sizeof(NOMSG) - 1);
+ ct = ctime(&shuttime);
+ (void)write(logfd, ct + 11, 5);
+ (void)write(logfd, "\n\n", 2);
+ (void)write(logfd, mbuf, strlen(mbuf));
+ (void)close(logfd);
+ }
+}
+
+void
+finish(int signo __unused)
+{
+ if (!killflg)
+ (void)unlink(_PATH_NOLOGIN);
+ exit(0);
+}
+
+void
+badtime()
+{
+ errx(1, "bad time format");
+}
+
+void
+usage(const char *cp)
+{
+ if (cp != NULL)
+ warnx("%s", cp);
+ (void)fprintf(stderr,
+ "usage: shutdown [-] [-h | -p | -r | -k] [-o [-n]]"
+ " time [warning-message ...]\n");
+ exit(1);
+}
diff --git a/sbin/slattach/Makefile b/sbin/slattach/Makefile
new file mode 100644
index 0000000..84b0c12
--- /dev/null
+++ b/sbin/slattach/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 5.4 (Berkeley) 5/11/90
+#
+# $FreeBSD$
+
+PROG= slattach
+WARNS?= 0
+MAN= slattach.8
+MLINKS= slattach.8 slip.8
+LDADD= -lutil
+DPADD= ${LIBUTIL}
+
+.include <bsd.prog.mk>
diff --git a/sbin/slattach/slattach.8 b/sbin/slattach/slattach.8
new file mode 100644
index 0000000..a359dd2
--- /dev/null
+++ b/sbin/slattach/slattach.8
@@ -0,0 +1,271 @@
+.\" Copyright (c) 1986, 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.
+.\" 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 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.
+.\"
+.\" @(#)slattach.8 6.4 (Berkeley) 3/16/91
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 4, 1993
+.Dt SLATTACH 8
+.Os
+.Sh NAME
+.Nm slattach
+.Nd attach serial lines as network interfaces
+.Sh SYNOPSIS
+.Nm
+.Op Fl acfhLlnz
+.Op Fl e Ar exit-command
+.Op Fl K Ar keepalive
+.Op Fl O Ar outfill
+.Op Fl r Ar redial-command
+.Op Fl S Ar unit
+.Op Fl s Ar baudrate
+.Op Fl u Ar unit-command
+.Ar ttyname
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to assign a tty line to a network interface,
+and to define the network source and destination addresses.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+Autoenable the VJ header compression option, if the other end of the link
+is capable of VJ header compression then it will be used otherwise normal
+headers will be used.
+.It Fl c
+Enable the VJ header compression option.
+Note that both ends of the link
+must be able to use VJ header compression for this to work.
+.It Fl e Ar exit-command
+Specify a command to be invoked within a shell
+.Ql sh \-c Ar exit-command
+before
+.Nm
+exits.
+.It Fl f
+Disable the invocation of daemon() to run
+.Nm
+in the background.
+.It Fl h
+Turn on cts/rts style flow control on the slip port, by default no flow
+control is done.
+.It Fl l
+Disable modem control (CLOCAL) and ignore carrier detect on the slip
+port.
+By default the
+.Ar redial-command
+is invoked upon carrier drop and
+.Nm
+aborts if no
+.Ar redial-command
+is specified.
+.It Fl n
+Throw away ICMP packets.
+The slip interface will ignore ICMP packets
+to prevent slow lines being saturated by ICMP responses.
+.It Fl r Ar redial-command
+Specify a command to be invoked within a shell
+.Ql sh \-c Ar redial-command
+whenever carrier is lost on the modem line.
+Empty
+.Ar redial-command
+(i.e.\&
+.Fl r Qq "" )
+cause connection reestablishing on leased line
+without any external command invoked.
+.It Fl s Ar baudrate
+Specify the speed of the connection.
+If not specified, the
+default of 9600 is used.
+.It Fl u Ar unit-command
+When the line is switched to slip discipline, run
+.Dq Nm "sh -c" Ar unit-command old new
+where
+.Ar old
+and
+.Ar new
+are the slip unit numbers when the line was
+last opened and the unit number of the current slip connection
+respectively.
+The unit number can change after redialing if you are
+using more than one slip line.
+When
+.Nm
+is connected for the first time,
+.Dq Nm "sh -c" Ar unit-command \-1 new
+is run.
+When
+.Nm
+is disconnected,
+.Dq Nm "sh -c" Ar unit-command old \-1
+is run.
+The
+.Nm
+utility will abort if the unit number
+changes and
+.Dq Fl u Ar \%unit-command
+was not specified.
+.It Fl z
+Force redial
+.Ar redial-command
+upon startup irrespective of carrier.
+.It Fl L
+Use uucp-style device locking.
+You need it unless you start
+.Nm
+from external program which already does uucp locking.
+Default case is no uucp locking to satisfy such programs.
+.It Fl K Ar keepalive
+Set SLIP "keep alive" timeout in seconds.
+If FRAME_END is not received in
+this amount of time, re-connect occurs.
+The default value is no timeout.
+.It Fl O Ar outfill
+Set SLIP "out fill" timeout in seconds.
+It forces at least one FRAME_END
+to be sent during this time period, which is necessary for the "keep alive"
+timeout on the remote side.
+The default value is no timeout.
+.It Fl S Ar unit
+Set the SLIP unit number directly.
+Use with caution, because no check is made
+for two interfaces with same number.
+By default sliplogin dynamically assigns the unit number.
+.It Ar ttyname
+Specify the name of the tty device.
+.Ar Ttyname
+should be a string of the form
+.Ql ttyXX
+or
+.Ql /dev/ttyXX .
+.El
+.Pp
+Only the super-user may attach a network interface.
+.Pp
+To detach the interface, use
+.Dq Li ifconfig interface-name down
+after killing off the
+.Nm
+process using
+.Ql kill -INT .
+.Ar Interface-name
+is the name that is shown by
+.Xr netstat 1 .
+.Pp
+To setup
+.Nm
+to redial the phone when carrier is lost, use the
+.Fl r Ar redial-command
+option to specify a script or executable that will reconnect the
+serial line to the slip server.
+For example, the script could redial
+the server and log in, etc.
+.Pp
+To reconfigure the network interface in case the slip unit number
+changes, use the
+.Fl u Ar unit-command
+option to specify a script or executable that will be invoked as
+.Ql sh \-c Ar unit-command old new
+where
+.Ar old
+and
+.Ar new
+are the slip unit numbers before and after
+reconnecting the line.
+The unit number can change if you have more
+than one line disconnect at the same time.
+The first to succeed in
+reconnecting will get the lowest unit number.
+.Pp
+To kill
+.Nm
+use
+.Ql kill -INT
+(SIGINT) which causes it to close the tty and exit.
+.Pp
+To force a redial, use
+.Ql kill -HUP
+(SIGHUP) which causes
+.Nm
+to think carrier was lost and thus invoke
+.Ql sh \-c Ar redial-command
+to reconnect to the server.
+.Pp
+If you use a hard-wired connection rather than a modem, invoke
+.Nm
+with the
+.Fl l
+option in order to ignore carrier on the slip line.
+.Sh FILES
+.Bl -tag -width /usr/share/examples/slattach/* -compact
+.It Pa /var/run/slattach.<tty>.pid
+with
+.Ar tty
+replaced by the terminal path name component of
+.Ar ttyname .
+This file contains the numerical process ID of the
+.Nm
+process and can be examined by scripts in order to send a signal to
+.Nm
+.It Pa /usr/share/examples/slattach/*
+.El
+.Sh EXAMPLES
+.Bd -literal -offset indent -compact
+slattach ttyd8
+slattach \-s 4800 /dev/ttyd1
+slattach \-c \-s 38400 /dev/cuad1
+slattach \-r 'kermit -y dial.script >kermit.log 2>&1'
+.Ed
+.Sh DIAGNOSTICS
+Look for error messages in
+.Pa /var/log/messages
+.No ( Nm
+is a daemon).
+Messages indicating the specified interface does not exit, the
+requested address is unknown, the user is not privileged and tried to
+alter an interface's configuration are logged there.
+The
+.Nm
+utility
+also logs failure to set the controlling terminal or failure to install
+signal handlers.
+Upon connection and redial the ttyname and baud rate
+are logged and on shutdown the ttyname is logged.
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr startslip 1 ,
+.Xr netintro 4 ,
+.Xr ifconfig 8 ,
+.Xr rc 8 ,
+.Xr sliplogin 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 .
diff --git a/sbin/slattach/slattach.c b/sbin/slattach/slattach.c
new file mode 100644
index 0000000..afdfce8
--- /dev/null
+++ b/sbin/slattach/slattach.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Adams.
+ *
+ * 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1988 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "from: @(#)slattach.c 4.6 (Berkeley) 6/1/90";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <net/if.h>
+#include <net/slip.h>
+
+#define DEFAULT_BAUD 9600
+
+void sighup_handler(); /* SIGHUP handler */
+void sigint_handler(); /* SIGINT handler */
+void sigterm_handler(); /* SIGTERM handler */
+void sigurg_handler(); /* SIGURG handler */
+void exit_handler(int ret); /* run exit_cmd iff specified upon exit. */
+void setup_line(int cflag); /* configure terminal settings */
+void slip_discipline(); /* switch to slip line discipline */
+void configure_network(); /* configure slip interface */
+void acquire_line(); /* get tty device as controlling terminal */
+static void usage(void);
+
+int fd = -1;
+char *dev = (char *)0; /* path name of the tty (e.g. /dev/tty01) */
+char *dvname; /* basename of dev */
+int locked = 0; /* uucp lock active */
+int flow_control = 0; /* non-zero to enable hardware flow control. */
+int modem_control = HUPCL; /* !CLOCAL+HUPCL iff we watch carrier. */
+int comstate; /* TIOCMGET current state of serial driver */
+int redial_on_startup = 0; /* iff non-zero execute redial_cmd on startup */
+speed_t speed = DEFAULT_BAUD; /* baud rate of tty */
+int slflags = 0; /* compression flags */
+int unit = -1; /* slip device unit number */
+int foreground = 0; /* act as daemon if zero, else don't fork. */
+int keepal = 0; /* keepalive timeout */
+int outfill = 0; /* outfill timeout */
+int sl_unit = -1; /* unit number */
+int uucp_lock = 0; /* do uucp locking */
+int exiting = 0; /* already running exit_handler */
+
+struct termios tty; /* tty configuration/state */
+
+char tty_path[32]; /* path name of the tty (e.g. /dev/tty01) */
+char pidfilename[40]; /* e.g. /var/run/slattach.tty01.pid */
+char *redial_cmd = 0; /* command to exec upon shutdown. */
+char *config_cmd = 0; /* command to exec if slip unit changes. */
+char *exit_cmd = 0; /* command to exec before exiting. */
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+"usage: slattach [-acfhLlnz] [-e exit-command] [-K keepalive] [-O outfill]",
+" [-r redial-command] [-S unit] [-s baudrate] [-u unit-command]",
+" ttyname");
+ /* do not exit here */
+}
+
+int
+main(int argc, char **argv)
+{
+ int option;
+
+ while ((option = getopt(argc, argv, "ace:fhlnr:s:u:zLK:O:S:")) != -1) {
+ switch (option) {
+ case 'a':
+ slflags |= IFF_LINK2;
+ slflags &= ~IFF_LINK0;
+ break;
+ case 'c':
+ slflags |= IFF_LINK0;
+ slflags &= ~IFF_LINK2;
+ break;
+ case 'e':
+ exit_cmd = strdup (optarg);
+ break;
+ case 'f':
+ foreground = 1;
+ break;
+ case 'h':
+ flow_control |= CRTSCTS;
+ break;
+ case 'l':
+ modem_control = CLOCAL; /* clear HUPCL too */
+ break;
+ case 'n':
+ slflags |= IFF_LINK1;
+ break;
+ case 'r':
+ redial_cmd = strdup (optarg);
+ break;
+ case 's':
+ speed = atoi(optarg);
+ break;
+ case 'u':
+ config_cmd = strdup (optarg);
+ break;
+ case 'z':
+ redial_on_startup = 1;
+ break;
+ case 'L':
+ uucp_lock = 1;
+ break;
+ case 'K':
+ keepal = atoi(optarg);
+ break;
+ case 'O':
+ outfill = atoi(optarg);
+ break;
+ case 'S':
+ sl_unit = atoi(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ exit_handler(1);
+ }
+ }
+
+ if (optind == argc - 1)
+ dev = argv[optind];
+
+ if (optind < (argc - 1))
+ warnx("too many args, first='%s'", argv[optind]);
+ if (optind > (argc - 1))
+ warnx("not enough args");
+ if (dev == (char *)0) {
+ usage();
+ exit_handler(2);
+ }
+ if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) {
+ strcpy(tty_path, _PATH_DEV);
+ strcat(tty_path, "/");
+ strncat(tty_path, dev, 10);
+ dev = tty_path;
+ }
+ dvname = strrchr(dev, '/'); /* always succeeds */
+ dvname++; /* trailing tty pathname component */
+ snprintf(pidfilename, sizeof(pidfilename),
+ "%sslattach.%s.pid", _PATH_VARRUN, dvname);
+ printf("%s\n",pidfilename);
+
+ if (!foreground)
+ daemon(0,0); /* fork, setsid, chdir /, and close std*. */
+ /* daemon() closed stderr, so log errors from here on. */
+ openlog("slattach",LOG_CONS|LOG_PID,LOG_DAEMON);
+
+ acquire_line(); /* get tty device as controlling terminal */
+ setup_line(0); /* configure for slip line discipline */
+ slip_discipline(); /* switch to slip line discipline */
+
+ /* upon INT log a timestamp and exit. */
+ if (signal(SIGINT,sigint_handler) == SIG_ERR)
+ syslog(LOG_NOTICE,"cannot install SIGINT handler: %m");
+ /* upon TERM log a timestamp and exit. */
+ if (signal(SIGTERM,sigterm_handler) == SIG_ERR)
+ syslog(LOG_NOTICE,"cannot install SIGTERM handler: %m");
+ /* upon HUP redial and reconnect. */
+ if (signal(SIGHUP,sighup_handler) == SIG_ERR)
+ syslog(LOG_NOTICE,"cannot install SIGHUP handler: %m");
+
+ if (redial_on_startup)
+ sighup_handler();
+ else if (!(modem_control & CLOCAL)) {
+ if (ioctl(fd, TIOCMGET, &comstate) < 0)
+ syslog(LOG_NOTICE,"cannot get carrier state: %m");
+ if (!(comstate & TIOCM_CD)) { /* check for carrier */
+ /* force a redial if no carrier */
+ kill (getpid(), SIGHUP);
+ } else
+ configure_network();
+ } else
+ configure_network(); /* configure the network if needed. */
+
+ for (;;) {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigsuspend(&mask);
+ }
+}
+
+/* Close all FDs, fork, reopen tty port as 0-2, and make it the
+ controlling terminal for our process group. */
+void acquire_line()
+{
+ int ttydisc = TTYDISC;
+ int oflags;
+ FILE *pidfile;
+
+ /* reset to tty discipline */
+ if (fd >= 0 && ioctl(fd, TIOCSETD, &ttydisc) < 0) {
+ syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+ exit_handler(1);
+ }
+
+ (void)close(STDIN_FILENO); /* close FDs before forking. */
+ (void)close(STDOUT_FILENO);
+ (void)close(STDERR_FILENO);
+ if (fd > 2)
+ (void)close(fd);
+
+ signal(SIGHUP, SIG_IGN); /* ignore HUP signal when parent dies. */
+ if (daemon(0,0)) { /* fork, setsid, chdir /, and close std*. */
+ syslog(LOG_ERR, "daemon(0,0): %m");
+ exit_handler(1);
+ }
+
+ while (getppid () != 1)
+ sleep (1); /* Wait for parent to die. */
+
+ /* create PID file */
+ if((pidfile = fopen(pidfilename, "w"))) {
+ fprintf(pidfile, "%ld\n", (long)getpid());
+ fclose(pidfile);
+ }
+
+ if (signal(SIGHUP,sighup_handler) == SIG_ERR) /* Re-enable HUP signal */
+ syslog(LOG_NOTICE,"cannot install SIGHUP handler: %m");
+
+ if (uucp_lock) {
+ /* unlock not needed here, always re-lock with new pid */
+ int res;
+ if ((res = uu_lock(dvname)) != UU_LOCK_OK) {
+ if (res != UU_LOCK_INUSE)
+ syslog(LOG_ERR, "uu_lock: %s", uu_lockerr(res));
+ syslog(LOG_ERR, "can't lock %s", dev);
+ exit_handler(1);
+ }
+ locked = 1;
+ }
+
+ if ((fd = open(dev, O_RDWR | O_NONBLOCK, 0)) < 0) {
+ syslog(LOG_ERR, "open(%s) %m", dev);
+ exit_handler(1);
+ }
+ /* Turn off O_NONBLOCK for dumb redialers, if any. */
+ if ((oflags = fcntl(fd, F_GETFL)) == -1) {
+ syslog(LOG_ERR, "fcntl(F_GETFL) failed: %m");
+ exit_handler(1);
+ }
+ if (fcntl(fd, F_SETFL, oflags & ~O_NONBLOCK) == -1) {
+ syslog(LOG_ERR, "fcntl(F_SETFL) failed: %m");
+ exit_handler(1);
+ }
+ (void)dup2(fd, STDIN_FILENO);
+ (void)dup2(fd, STDOUT_FILENO);
+ (void)dup2(fd, STDERR_FILENO);
+ if (fd > 2)
+ (void)close (fd);
+ fd = STDIN_FILENO;
+
+ /* acquire the serial line as a controlling terminal. */
+ if (ioctl(fd, TIOCSCTTY, 0) < 0) {
+ syslog(LOG_ERR,"ioctl(TIOCSCTTY): %m");
+ exit_handler(1);
+ }
+ /* Make us the foreground process group associated with the
+ slip line which is our controlling terminal. */
+ if (tcsetpgrp(fd, getpid()) < 0)
+ syslog(LOG_NOTICE,"tcsetpgrp failed: %m");
+}
+
+/* Set the tty flags and set DTR. */
+/* Call as setup_line(CLOCAL) to force clocal assertion. */
+void setup_line(int cflag)
+{
+ tty.c_lflag = tty.c_iflag = tty.c_oflag = 0;
+ tty.c_cflag = CREAD | CS8 | flow_control | modem_control | cflag;
+ cfsetispeed(&tty, speed);
+ cfsetospeed(&tty, speed);
+ /* set the line speed and flow control */
+ if (tcsetattr(fd, TCSAFLUSH, &tty) < 0) {
+ syslog(LOG_ERR, "tcsetattr(TCSAFLUSH): %m");
+ exit_handler(1);
+ }
+ /* set data terminal ready */
+ if (ioctl(fd, TIOCSDTR) < 0) {
+ syslog(LOG_ERR, "ioctl(TIOCSDTR): %m");
+ exit_handler(1);
+ }
+}
+
+/* Put the line in slip discipline. */
+void slip_discipline()
+{
+ struct ifreq ifr;
+ int slipdisc = SLIPDISC;
+ int s, tmp_unit = -1;
+
+ /* Switch to slip line discipline. */
+ if (ioctl(fd, TIOCSETD, &slipdisc) < 0) {
+ syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+ exit_handler(1);
+ }
+
+ if (sl_unit >= 0 && ioctl(fd, SLIOCSUNIT, &sl_unit) < 0) {
+ syslog(LOG_ERR, "ioctl(SLIOCSUNIT): %m");
+ exit_handler(1);
+ }
+
+ /* find out what unit number we were assigned */
+ if (ioctl(fd, SLIOCGUNIT, (caddr_t)&tmp_unit) < 0) {
+ syslog(LOG_ERR, "ioctl(SLIOCGUNIT): %m");
+ exit_handler(1);
+ }
+
+ if (tmp_unit < 0) {
+ syslog(LOG_ERR, "bad unit (%d) from ioctl(SLIOCGUNIT)",tmp_unit);
+ exit_handler(1);
+ }
+
+ if (keepal > 0) {
+ signal(SIGURG, sigurg_handler);
+ if (ioctl(fd, SLIOCSKEEPAL, &keepal) < 0) {
+ syslog(LOG_ERR, "ioctl(SLIOCSKEEPAL): %m");
+ exit_handler(1);
+ }
+ }
+ if (outfill > 0 && ioctl(fd, SLIOCSOUTFILL, &outfill) < 0) {
+ syslog(LOG_ERR, "ioctl(SLIOCSOUTFILL): %m");
+ exit_handler(1);
+ }
+
+ /* open a socket as the handle to the interface */
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ exit_handler(1);
+ }
+ sprintf(ifr.ifr_name, "sl%d", tmp_unit);
+
+ /* get the flags for the interface */
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
+ exit_handler(1);
+ }
+
+ /* Assert any compression or no-icmp flags. */
+#define SLMASK (~(IFF_LINK0 | IFF_LINK1 | IFF_LINK2))
+ ifr.ifr_flags &= SLMASK;
+ ifr.ifr_flags |= slflags;
+ if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "ioctl (SIOCSIFFLAGS): %m");
+ exit_handler(1);
+ }
+ close(s);
+}
+
+/* configure the interface, e.g. by passing the unit number to a script. */
+void configure_network()
+{
+ int new_unit;
+
+ /* find out what unit number we were assigned */
+ if (ioctl(fd, SLIOCGUNIT, (caddr_t)&new_unit) < 0) {
+ syslog(LOG_ERR, "ioctl(SLIOCGUNIT): %m");
+ exit_handler(1);
+ }
+ /* iff the unit number changes either invoke config_cmd or punt. */
+ if (config_cmd) {
+ char *s;
+ s = (char*) malloc(strlen(config_cmd) + 32);
+ if (s == NULL) {
+ syslog(LOG_ERR, "malloc failed");
+ exit(1);
+ }
+ sprintf (s, "%s %d %d", config_cmd, unit, new_unit);
+ syslog(LOG_NOTICE, "configuring %s (sl%d):", dev, unit);
+ syslog(LOG_NOTICE, " '%s'", s);
+ system(s);
+ free (s);
+ unit = new_unit;
+ } else {
+ /* don't compare unit numbers if this is the first time to attach. */
+ if (unit < 0)
+ unit = new_unit;
+ if (new_unit != unit) {
+ syslog(LOG_ERR,
+ "slip unit changed from sl%d to sl%d, but no -u CMD was specified!",
+ unit, new_unit);
+ exit_handler(1);
+ }
+ syslog(LOG_NOTICE,"sl%d connected to %s at %d baud",unit,dev,speed);
+ }
+}
+
+/* sighup_handler() is invoked when carrier drops, eg. before redial. */
+void sighup_handler()
+{
+ if(exiting) return;
+
+ if (redial_cmd == NULL) {
+ syslog(LOG_NOTICE,"SIGHUP on %s (sl%d); exiting", dev, unit);
+ exit_handler(1);
+ }
+again:
+ /* invoke a shell for redial_cmd or punt. */
+ if (*redial_cmd) { /* Non-empty redial command */
+ syslog(LOG_NOTICE,"SIGHUP on %s (sl%d); running '%s'",
+ dev, unit, redial_cmd);
+ acquire_line(); /* reopen dead line */
+ setup_line(CLOCAL);
+ if (locked) {
+ if (uucp_lock)
+ uu_unlock(dvname); /* for redial */
+ locked = 0;
+ }
+ if (system(redial_cmd))
+ goto again;
+ if (uucp_lock) {
+ int res;
+ if ((res = uu_lock(dvname)) != UU_LOCK_OK) {
+ if (res != UU_LOCK_INUSE)
+ syslog(LOG_ERR, "uu_lock: %s", uu_lockerr(res));
+ syslog(LOG_ERR, "can't relock %s after %s, aborting",
+ dev, redial_cmd);
+ exit_handler(1);
+ }
+ locked = 1;
+ }
+ /* Now check again for carrier (dial command is done): */
+ if (!(modem_control & CLOCAL)) {
+ tty.c_cflag &= ~CLOCAL;
+ if (tcsetattr(fd, TCSAFLUSH, &tty) < 0) {
+ syslog(LOG_ERR, "tcsetattr(TCSAFLUSH): %m");
+ exit_handler(1);
+ }
+ ioctl(fd, TIOCMGET, &comstate);
+ if (!(comstate & TIOCM_CD)) { /* check for carrier */
+ /* force a redial if no carrier */
+ goto again;
+ }
+ } else
+ setup_line(0);
+ } else { /* Empty redial command */
+ syslog(LOG_NOTICE,"SIGHUP on %s (sl%d); reestablish connection",
+ dev, unit);
+ acquire_line(); /* reopen dead line */
+ setup_line(0); /* restore ospeed from hangup (B0) */
+ /* If modem control, just wait for carrier before attaching.
+ If no modem control, just fall through immediately. */
+ if (!(modem_control & CLOCAL)) {
+ int carrier = 0;
+
+ syslog(LOG_NOTICE, "waiting for carrier on %s (sl%d)",
+ dev, unit);
+ /* Now wait for carrier before attaching line. */
+ /* We must poll since CLOCAL prevents signal. */
+ while (! carrier) {
+ sleep(2);
+ ioctl(fd, TIOCMGET, &comstate);
+ if (comstate & TIOCM_CD)
+ carrier = 1;
+ }
+ syslog(LOG_NOTICE, "carrier now present on %s (sl%d)",
+ dev, unit);
+ }
+ }
+ slip_discipline();
+ configure_network();
+}
+/* Signal handler for SIGINT. We just log and exit. */
+void sigint_handler()
+{
+ if(exiting) return;
+ syslog(LOG_NOTICE,"SIGINT on %s (sl%d); exiting",dev,unit);
+ exit_handler(0);
+}
+/* Signal handler for SIGURG. */
+void sigurg_handler()
+{
+ int ttydisc = TTYDISC;
+
+ signal(SIGURG, SIG_IGN);
+ if(exiting) return;
+ syslog(LOG_NOTICE,"SIGURG on %s (sl%d); hangup",dev,unit);
+ if (ioctl(fd, TIOCSETD, &ttydisc) < 0) {
+ syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+ exit_handler(1);
+ }
+ cfsetospeed(&tty, B0);
+ if (tcsetattr(fd, TCSANOW, &tty) < 0) {
+ syslog(LOG_ERR, "tcsetattr(TCSANOW): %m");
+ exit_handler(1);
+ }
+ /* Need to go to sighup handler in any case */
+ if (modem_control & CLOCAL)
+ kill (getpid(), SIGHUP);
+
+}
+/* Signal handler for SIGTERM. We just log and exit. */
+void sigterm_handler()
+{
+ if(exiting) return;
+ syslog(LOG_NOTICE,"SIGTERM on %s (sl%d); exiting",dev,unit);
+ exit_handler(0);
+}
+/* Run config_cmd if specified before exiting. */
+void exit_handler(int ret)
+{
+ if(exiting) return;
+ exiting = 1;
+ /*
+ * First close the slip line in case exit_cmd wants it (like to hang
+ * up a modem or something).
+ */
+ if (fd != -1)
+ close(fd);
+ if (uucp_lock && locked)
+ uu_unlock(dvname);
+
+ /* Remove the PID file */
+ (void)unlink(pidfilename);
+
+ if (config_cmd) {
+ char *s;
+ s = (char*) malloc(strlen(config_cmd) + 32);
+ if (s == NULL) {
+ syslog(LOG_ERR, "malloc failed");
+ exit(1);
+ }
+ sprintf (s, "%s %d -1", config_cmd, unit);
+ syslog(LOG_NOTICE, "deconfiguring %s (sl%d):", dev, unit);
+ syslog(LOG_NOTICE, " '%s'", s);
+ system(s);
+ free (s);
+ }
+ /* invoke a shell for exit_cmd. */
+ if (exit_cmd) {
+ syslog(LOG_NOTICE,"exiting after running %s", exit_cmd);
+ system(exit_cmd);
+ }
+ exit(ret);
+}
+
+/* local variables: */
+/* c-indent-level: 8 */
+/* c-argdecl-indent: 0 */
+/* c-label-offset: -8 */
+/* c-continued-statement-offset: 8 */
+/* c-brace-offset: 0 */
+/* comment-column: 32 */
+/* end: */
diff --git a/sbin/spppcontrol/Makefile b/sbin/spppcontrol/Makefile
new file mode 100644
index 0000000..b394c39
--- /dev/null
+++ b/sbin/spppcontrol/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= spppcontrol
+MAN= spppcontrol.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/spppcontrol/spppcontrol.8 b/sbin/spppcontrol/spppcontrol.8
new file mode 100644
index 0000000..4389c8f
--- /dev/null
+++ b/sbin/spppcontrol/spppcontrol.8
@@ -0,0 +1,275 @@
+.\" Copyright (C) 1997, 2001 by Joerg Wunsch, Dresden
+.\" 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(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, 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 30, 2001
+.Os
+.Dt SPPPCONTROL 8
+.Sh NAME
+.Nm spppcontrol
+.Nd display or set parameters for an sppp interface
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Ar ifname
+.Op Ar parameter Ns Op Li = Ns Ar value
+.Op Ar ...
+.Sh DESCRIPTION
+The
+.Xr sppp 4
+driver might require a number of additional arguments or optional
+parameters besides the settings that can be adjusted with
+.Xr ifconfig 8 .
+These are things like authentication protocol parameters, but also
+other tunable configuration variables.
+The
+.Nm
+utility can be used to display the current settings, or adjust these
+parameters as required.
+.Pp
+For whatever intent
+.Nm
+is being called, at least the parameter
+.Ar ifname
+needs to be specified, naming the interface for which the settings
+are to be performed or displayed.
+Use
+.Xr ifconfig 8 ,
+or
+.Xr netstat 1
+to see which interfaces are available.
+.Pp
+If no other parameter is given,
+.Nm
+will just list the current settings for
+.Ar ifname
+and exit.
+The reported settings include the current PPP phase the
+interface is in, which can be one of the names
+.Em dead ,
+.Em establish ,
+.Em authenticate ,
+.Em network ,
+or
+.Em terminate .
+If an authentication protocol is configured for the interface, the
+name of the protocol to be used, as well as the system name to be used
+or expected will be displayed, plus any possible options to the
+authentication protocol if applicable.
+Note that the authentication
+secrets (sometimes also called
+.Em keys )
+are not being returned by the underlying system call, and are thus not
+displayed.
+.Pp
+If any additional parameter is supplied, superuser privileges are
+required, and the command works in the
+.Dq set
+mode.
+This is normally done quietly, unless the option
+.Fl v
+is also enabled, which will cause a final printout of the settings as
+described above once all other actions have been taken.
+Use of this
+mode will be rejected if the interface is currently in any other phase
+than
+.Em dead .
+Note that you can force an interface into
+.Em dead
+phase by calling
+.Xr ifconfig 8
+with the parameter
+.Cm down .
+.Pp
+The currently supported parameters include:
+.Bl -tag -offset indent -width indent
+.It Va authproto Ns Li = Ns Ar protoname
+Set both, his and my authentication protocol to
+.Ar protoname .
+The protocol name can be one of
+.Dq Li chap ,
+.Dq Li pap ,
+or
+.Dq Li none .
+In the latter case, the use of an authentication protocol will be
+turned off for the named interface.
+This has the side-effect of
+clearing the other authentication-related parameters for this
+interface as well (i.e., system name and authentication secret will
+be forgotten).
+.It Va myauthproto Ns Li = Ns Ar protoname
+Same as above, but only for my end of the link.
+I.e., this is the
+protocol when remote is authenticator, and I am the peer required to
+authenticate.
+.It Va hisauthproto Ns Li = Ns Ar protoname
+Same as above, but only for his end of the link.
+.It Va myauthname Ns Li = Ns Ar name
+Set my system name for the authentication protocol.
+.It Va hisauthname Ns Li = Ns Ar name
+Set his system name for the authentication protocol.
+For CHAP, this
+will only be used as a hint, causing a warning message if remote did
+supply a different name.
+For PAP, it is the name remote must use to
+authenticate himself (in connection with his secret).
+.It Va myauthsecret Ns Li = Ns Ar secret
+Set my secret (key, password) for use in the authentication phase.
+For CHAP, this will be used to compute the response hash value, based
+on remote's challenge.
+For PAP, it will be transmitted as plain text
+together with the system name.
+Do not forget to quote the secrets from
+the shell if they contain shell metacharacters (or white space).
+.It Va myauthkey Ns Li = Ns Ar secret
+Same as above.
+.It Va hisauthsecret Ns Li = Ns Ar secret
+Same as above, to be used if we are an authenticator and the remote peer
+needs to authenticate.
+.It Va hisauthkey Ns Li = Ns Va secret
+Same as above.
+.It Va callin
+Require remote to authenticate himself only when he is calling in, but
+not when we are caller.
+This is required for some peers that do not
+implement the authentication protocols symmetrically (like Ascend
+routers, for example).
+.It Va always
+The opposite of
+.Va callin .
+Require remote to always authenticate, regardless of which side is
+placing the call.
+This is the default, and will not be explicitly
+displayed in the
+.Dq list
+mode.
+.It Va norechallenge
+Only meaningful with CHAP.
+Do not re-challenge peer once the initial
+CHAP handshake was successful.
+Used to work around broken peer
+implementations that cannot grok being re-challenged once the
+connection is up.
+.It Ar rechallenge
+With CHAP, send re-challenges at random intervals while the connection
+is in network phase.
+(The intervals are currently in the range of 300
+through approximately 800 seconds.)
+This is the default, and will not
+be explicitly displayed in the
+.Dq list
+mode.
+.It Va lcp-timeout Ns Li = Ns Ar timeout-value
+Allows to change the value of the LCP restart timer.
+Values are
+specified in milliseconds.
+The value must be between 10 and 20000 ms,
+defaulting to 3000 ms.
+.It Va enable-vj
+Enable negotiation of Van Jacobsen header compression.
+(Enabled by default.)
+.It Va disable-vj
+Disable negotiation of Van Jacobsen header compression.
+.It Va enable-ipv6
+Enable negotiation of the IPv6 network control protocol.
+(Enabled by default if the kernel has IPv6 enabled.)
+.It Va disable-ipv6
+Disable negotiation of the IPv6 network control protocol.
+Since every
+IPv4 interface in an IPv6-enabled kernel automatically gets an IPv6
+address assigned, this option provides for a way to administratively
+prevent the link from attempting to negotiate IPv6.
+Note that
+initialization of an IPv6 interface causes a multicast packet to be
+sent, which can cause unwanted traffic costs (for dial-on-demand
+interfaces).
+.El
+.Sh EXAMPLES
+.Bd -literal
+# spppcontrol bppp0
+bppp0: phase=dead
+ myauthproto=chap myauthname="uriah"
+ hisauthproto=chap hisauthname="ifb-gw" norechallenge
+ lcp-timeout=3000
+ enable-vj
+ enable-ipv6
+.Ed
+.Pp
+Display the settings for
+.Li bppp0 .
+The interface is currently in
+.Em dead
+phase, i.e., the LCP layer is down, and no traffic is possible.
+Both
+ends of the connection use the CHAP protocol, my end tells remote the
+system name
+.Dq Li uriah ,
+and remote is expected to authenticate by the name
+.Dq Li ifb-gw .
+Once the initial CHAP handshake was successful, no further CHAP
+challenges will be transmitted.
+There are supposedly some known CHAP
+secrets for both ends of the link which are not being shown.
+.Bd -literal
+# spppcontrol bppp0 \e
+ authproto=chap \e
+ myauthname=uriah myauthsecret='some secret' \e
+ hisauthname=ifb-gw hisauthsecret='another' \e
+ norechallenge
+.Ed
+.Pp
+A possible call to
+.Nm
+that could have been used to bring the interface into the state shown
+by the previous example.
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr sppp 4 ,
+.Xr ifconfig 8
+.Rs
+.%A B. Lloyd
+.%A W. Simpson
+.%T "PPP Authentication Protocols"
+.%O RFC 1334
+.Re
+.Rs
+.%A W. Simpson, Editor
+.%T "The Point-to-Point Protocol (PPP)"
+.%O RFC 1661
+.Re
+.Rs
+.%A W. Simpson
+.%T "PPP Challenge Handshake Authentication Protocol (CHAP)"
+.%O RFC 1994
+.Re
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+The program was written by
+.An J\(:org Wunsch ,
+Dresden.
diff --git a/sbin/spppcontrol/spppcontrol.c b/sbin/spppcontrol/spppcontrol.c
new file mode 100644
index 0000000..a1df0f4
--- /dev/null
+++ b/sbin/spppcontrol/spppcontrol.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 1997, 2001 Joerg Wunsch
+ *
+ * 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 DEVELOPERS ``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 DEVELOPERS 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_sppp.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+static void usage(void);
+void print_vals(const char *ifname, struct spppreq *sp);
+const char *phase_name(enum ppp_phase phase);
+const char *proto_name(u_short proto);
+const char *authflags(u_short flags);
+
+#define PPP_PAP 0xc023
+#define PPP_CHAP 0xc223
+
+int
+main(int argc, char **argv)
+{
+ int s, c;
+ int errs = 0, verbose = 0;
+ size_t off;
+ long to;
+ char *endp;
+ const char *ifname, *cp;
+ struct ifreq ifr;
+ struct spppreq spr;
+
+ while ((c = getopt(argc, argv, "v")) != -1)
+ switch (c) {
+ case 'v':
+ verbose++;
+ break;
+
+ default:
+ errs++;
+ break;
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (errs || argc < 1)
+ usage();
+
+ ifname = argv[0];
+ strncpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name);
+
+ /* use a random AF to create the socket */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ err(EX_UNAVAILABLE, "ifconfig: socket");
+
+ argc--;
+ argv++;
+
+ spr.cmd = (int)SPPPIOGDEFS;
+ ifr.ifr_data = (caddr_t)&spr;
+
+ if (ioctl(s, SIOCGIFGENERIC, &ifr) == -1)
+ err(EX_OSERR, "SIOCGIFGENERIC(SPPPIOGDEFS)");
+
+ if (argc == 0) {
+ /* list only mode */
+ print_vals(ifname, &spr);
+ return 0;
+ }
+
+#define startswith(s) strncmp(argv[0], s, (off = strlen(s))) == 0
+
+ while (argc > 0) {
+ if (startswith("authproto=")) {
+ cp = argv[0] + off;
+ if (strcmp(cp, "pap") == 0)
+ spr.defs.myauth.proto =
+ spr.defs.hisauth.proto = PPP_PAP;
+ else if (strcmp(cp, "chap") == 0)
+ spr.defs.myauth.proto =
+ spr.defs.hisauth.proto = PPP_CHAP;
+ else if (strcmp(cp, "none") == 0)
+ spr.defs.myauth.proto =
+ spr.defs.hisauth.proto = 0;
+ else
+ errx(EX_DATAERR, "bad auth proto: %s", cp);
+ } else if (startswith("myauthproto=")) {
+ cp = argv[0] + off;
+ if (strcmp(cp, "pap") == 0)
+ spr.defs.myauth.proto = PPP_PAP;
+ else if (strcmp(cp, "chap") == 0)
+ spr.defs.myauth.proto = PPP_CHAP;
+ else if (strcmp(cp, "none") == 0)
+ spr.defs.myauth.proto = 0;
+ else
+ errx(EX_DATAERR, "bad auth proto: %s", cp);
+ } else if (startswith("myauthname="))
+ strncpy(spr.defs.myauth.name, argv[0] + off,
+ AUTHNAMELEN);
+ else if (startswith("myauthsecret=") ||
+ startswith("myauthkey="))
+ strncpy(spr.defs.myauth.secret, argv[0] + off,
+ AUTHKEYLEN);
+ else if (startswith("hisauthproto=")) {
+ cp = argv[0] + off;
+ if (strcmp(cp, "pap") == 0)
+ spr.defs.hisauth.proto = PPP_PAP;
+ else if (strcmp(cp, "chap") == 0)
+ spr.defs.hisauth.proto = PPP_CHAP;
+ else if (strcmp(cp, "none") == 0)
+ spr.defs.hisauth.proto = 0;
+ else
+ errx(EX_DATAERR, "bad auth proto: %s", cp);
+ } else if (startswith("hisauthname="))
+ strncpy(spr.defs.hisauth.name, argv[0] + off,
+ AUTHNAMELEN);
+ else if (startswith("hisauthsecret=") ||
+ startswith("hisauthkey="))
+ strncpy(spr.defs.hisauth.secret, argv[0] + off,
+ AUTHKEYLEN);
+ else if (strcmp(argv[0], "callin") == 0)
+ spr.defs.hisauth.flags |= AUTHFLAG_NOCALLOUT;
+ else if (strcmp(argv[0], "always") == 0)
+ spr.defs.hisauth.flags &= ~AUTHFLAG_NOCALLOUT;
+ else if (strcmp(argv[0], "norechallenge") == 0)
+ spr.defs.hisauth.flags |= AUTHFLAG_NORECHALLENGE;
+ else if (strcmp(argv[0], "rechallenge") == 0)
+ spr.defs.hisauth.flags &= ~AUTHFLAG_NORECHALLENGE;
+ else if (startswith("lcp-timeout=")) {
+ cp = argv[0] + off;
+ to = strtol(cp, &endp, 10);
+ if (*cp == '\0' || *endp != '\0' ||
+ /*
+ * NB: 10 ms is the minimal possible value for
+ * hz=100. We assume no kernel has less clock
+ * frequency than that...
+ */
+ to < 10 || to > 20000)
+ errx(EX_DATAERR, "bad lcp timeout value: %s",
+ cp);
+ spr.defs.lcp.timeout = to;
+ } else if (strcmp(argv[0], "enable-vj") == 0)
+ spr.defs.enable_vj = 1;
+ else if (strcmp(argv[0], "disable-vj") == 0)
+ spr.defs.enable_vj = 0;
+ else if (strcmp(argv[0], "enable-ipv6") == 0)
+ spr.defs.enable_ipv6 = 1;
+ else if (strcmp(argv[0], "disable-ipv6") == 0)
+ spr.defs.enable_ipv6 = 0;
+ else
+ errx(EX_DATAERR, "bad parameter: \"%s\"", argv[0]);
+
+ argv++;
+ argc--;
+ }
+
+ spr.cmd = (int)SPPPIOSDEFS;
+
+ if (ioctl(s, SIOCSIFGENERIC, &ifr) == -1)
+ err(EX_OSERR, "SIOCSIFGENERIC(SPPPIOSDEFS)");
+
+ if (verbose)
+ print_vals(ifname, &spr);
+
+ return 0;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: spppcontrol [-v] ifname [{my|his}auth{proto|name|secret}=...]",
+ " spppcontrol [-v] ifname callin|always");
+ exit(EX_USAGE);
+}
+
+void
+print_vals(const char *ifname, struct spppreq *sp)
+{
+ printf("%s:\tphase=%s\n", ifname, phase_name(sp->defs.pp_phase));
+ if (sp->defs.myauth.proto) {
+ printf("\tmyauthproto=%s myauthname=\"%.*s\"\n",
+ proto_name(sp->defs.myauth.proto),
+ AUTHNAMELEN, sp->defs.myauth.name);
+ }
+ if (sp->defs.hisauth.proto) {
+ printf("\thisauthproto=%s hisauthname=\"%.*s\"%s\n",
+ proto_name(sp->defs.hisauth.proto),
+ AUTHNAMELEN, sp->defs.hisauth.name,
+ authflags(sp->defs.hisauth.flags));
+ }
+ printf("\tlcp-timeout=%d ms\n", sp->defs.lcp.timeout);
+ printf("\t%sable-vj\n", sp->defs.enable_vj? "en": "dis");
+ printf("\t%sable-ipv6\n", sp->defs.enable_ipv6? "en": "dis");
+}
+
+const char *
+phase_name(enum ppp_phase phase)
+{
+ switch (phase) {
+ case PHASE_DEAD: return "dead";
+ case PHASE_ESTABLISH: return "establish";
+ case PHASE_TERMINATE: return "terminate";
+ case PHASE_AUTHENTICATE: return "authenticate";
+ case PHASE_NETWORK: return "network";
+ }
+ return "illegal";
+}
+
+const char *
+proto_name(u_short proto)
+{
+ static char buf[12];
+ switch (proto) {
+ case PPP_PAP: return "pap";
+ case PPP_CHAP: return "chap";
+ }
+ sprintf(buf, "0x%x", (unsigned)proto);
+ return buf;
+}
+
+const char *
+authflags(u_short flags)
+{
+ static char buf[30];
+ buf[0] = '\0';
+ if (flags & AUTHFLAG_NOCALLOUT)
+ strcat(buf, " callin");
+ if (flags & AUTHFLAG_NORECHALLENGE)
+ strcat(buf, " norechallenge");
+ return buf;
+}
diff --git a/sbin/startslip/Makefile b/sbin/startslip/Makefile
new file mode 100644
index 0000000..1bf91dd
--- /dev/null
+++ b/sbin/startslip/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= startslip
+WARNS?= 3
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+.include <bsd.prog.mk>
diff --git a/sbin/startslip/startslip.1 b/sbin/startslip/startslip.1
new file mode 100644
index 0000000..2d6f43b
--- /dev/null
+++ b/sbin/startslip/startslip.1
@@ -0,0 +1,213 @@
+.\" Copyright (c) 1990, 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.
+.\" 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 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.
+.\"
+.\" @(#)startslip.1 8.1 (Berkeley) 6/5/93
+.\" $FreeBSD$
+.\"
+.Dd June 5, 1993
+.Dt STARTSLIP 1
+.Os
+.Sh NAME
+.Nm startslip
+.Nd dial up and login to a slip server
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl b Ar speed
+.Oo
+.Fl s Ar string1
+.Op Fl s Ar string2 Op Ar ...\&
+.Oc
+.Op Fl h
+.Op Fl l
+.Op Fl L
+.Op Fl A Ar annexname
+.Op Fl U Ar upscript
+.Op Fl D Ar downscript
+.Op Fl t Ar script_timeout
+.Op Fl W Ar maxtries
+.Op Fl w Ar retry_pause
+.Op Fl K Ar keepalive
+.Op Fl O Ar outfill
+.Op Fl S Ar unit
+.Ar device user password
+.Sh DESCRIPTION
+The
+.Nm
+utility opens the specified
+.Ar device .
+Once carrier is asserted (if modem control is enabled)
+.Nm
+attempts to login as the specified
+.Ar user
+with the given
+.Ar password .
+Standard login: & Password: scheme assumed unless Annex option
+specified (see
+.Fl A ) .
+If successful, it puts the device into the slip line discipline,
+calls
+.Ar upscript
+and goes to wait mode.
+If carrier drops (modem control enabled) or a
+.Dv SIGHUP
+is sent to
+.Nm ,
+it closes the device, calls
+.Ar downscript
+and attempts to repeat the dialup and login sequence.
+If a
+.Dv SIGTERM
+is send to
+.Nm ,
+it closes the device, calls
+.Ar downscript
+and exits.
+When
+.Nm
+called twice on the same device, previous copy killed by a
+.Dv SIGTERM
+before any operation.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Print out debugging information.
+.It Fl b Ar speed
+Determine the baud rate used for
+.Ar device ,
+default value is 9600.
+.It Fl t Ar script_timeout
+Set login script timeout in seconds, default value is 90.
+.It Fl w Ar retry_pause
+Set connecting retry pause in seconds (increased from one retry to another),
+default value is 60.
+.It Fl W Ar maxtries
+Set maximum number of connection retries for one session,
+default value is 6.
+Value 0 means infinite count.
+.It Fl s Ar stringN
+The optional
+.Ar stringN
+is written to
+.Ar device .
+Return character is added automatically.
+For a dialup modem,
+the string is used to specify a dial sequence.
+No string written by default.
+You can specify several
+.Fl s Ar stringN
+arguments to use with each try, f.e.
+several host phone numbers.
+.It Fl A Ar annexname
+The
+.Nm
+utility assumes it is connecting to a Xylogics Annex box and engages in an
+appropriate dialog using the
+.Ar user
+and
+.Ar password
+arguments.
+The
+.Ar annexname
+argument is a string that is used to match against the Annex prompt
+to determine when a connection has been established.
+.It Fl h
+Use hardware (CTS/RTS) flow control for
+.Ar device .
+Default value is no flow control.
+.It Fl l
+Disable modem control (waiting for carrier and carrier drop sense) for
+.Ar device .
+Modem control is enabled by default.
+.It Fl U Ar upscript
+Specify a script to run when a SLIP interface becomes connected.
+This may
+contain
+.Xr ifconfig 8 ,
+.Xr route 8 ,
+and other appropriate commands.
+The arguments that
+are passed to the script are "slX up".
+Default value is
+.Pa /sbin/ifconfig .
+Dial sequence number (see
+.Fl s )
+passed via
+.Ev LINE
+environment variable.
+.It Fl D Ar downscript
+Specify a script to run when a SLIP connection goes away.
+The arguments that
+are passed to the script are "slX down".
+Default value is
+.Pa /sbin/ifconfig .
+Dial sequence number (see
+.Fl s )
+passed via
+.Ev LINE
+environment variable.
+.It Fl K Ar keepalive
+Set SLIP "keep alive" timeout in seconds.
+If FRAME_END not received during this
+time period, a reconnect will occur.
+Active "out fill" timeout expected from other
+side.
+Default value is no timeout.
+.It Fl O Ar outfill
+Set SLIP "out fill" timeout in seconds.
+It causes at least one FRAME_END
+to be sent during this timeout.
+Needed for "keep alive" timeout on other side.
+Default value is no timeout.
+.It Fl S Ar unit
+Set SLIP unit number directly.
+Use with caution, no check for two
+interfaces with same number made.
+Default is dynamic assignment.
+.It Fl L
+Use uucp-style device locking.
+You need it unless you start
+.Nm
+from external program which already does uucp locking.
+Default case is no uucp locking to satisfy such programs.
+.El
+.Sh FILES
+.Bl -tag -width /var/run/startslip.<device>.pid -compact
+.It Pa /var/run/startslip.<device>.pid
+pid stored here
+.It Pa /usr/share/examples/startslip/*
+.El
+.Sh SEE ALSO
+.Xr uustat 1 Pq Pa ports/net/freebsd-uucp ,
+.Xr slattach 8 ,
+.Xr sliplogin 8
+.Sh HISTORY
+The
+.Nm
+appeared in
+.Bx 4.4 .
diff --git a/sbin/startslip/startslip.c b/sbin/startslip/startslip.c
new file mode 100644
index 0000000..2d4d3bf
--- /dev/null
+++ b/sbin/startslip/startslip.c
@@ -0,0 +1,595 @@
+/*-
+ * Copyright (c) 1990, 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1990, 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)startslip.c 8.1 (Berkeley) 6/5/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <net/slip.h>
+
+#define DEFAULT_BAUD B9600
+int speed = DEFAULT_BAUD;
+#define FC_NONE 0 /* flow control: none */
+#define FC_HW 1 /* flow control: hardware (RTS/CTS) */
+int flowcontrol = FC_NONE;
+int modem_control = 1; /* !CLOCAL+HUPCL iff we watch carrier. */
+int sl_unit = -1;
+int uucp_lock = 0; /* uucp locking */
+char *annex;
+char *username;
+int hup;
+int terminate;
+int locked = 0; /* uucp lock active */
+int logged_in = 0;
+int wait_time = 60; /* then back off */
+int script_timeout = 90; /* connect script default timeout */
+time_t conn_time, start_time;
+int MAXTRIES = 6; /* w/60 sec and doubling, takes an hour */
+#define PIDFILE "%sstartslip.%s.pid"
+
+#define MAXDIALS 20
+char *dials[MAXDIALS];
+int diali, dialc;
+
+int fd = -1;
+FILE *pfd;
+char *dvname, *devicename;
+char pidfile[80];
+
+#ifdef DEBUG
+int debug = 1;
+#undef LOG_ERR
+#undef LOG_INFO
+#define syslog fprintf
+#define LOG_ERR stderr
+#define LOG_INFO stderr
+#else
+int debug = 0;
+#endif
+#define printd if (debug) printf
+
+int carrier(void);
+void down(int);
+int getline(char *, int, int, time_t);
+void sighup(int);
+void sigterm(int);
+void sigurg(int);
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ char *cp, **ap;
+ int ch, disc;
+ FILE *wfd = NULL;
+ char *dialerstring = 0, buf[BUFSIZ];
+ int unitnum, keepal = 0, outfill = 0;
+ char unitname[32];
+ char *password;
+ char *upscript = NULL, *downscript = NULL;
+ int first = 1, tries = 0;
+ time_t fintimeout;
+ long lpid;
+ pid_t pid;
+ struct termios t;
+
+ while ((ch = getopt(argc, argv, "dhlb:s:t:w:A:U:D:W:K:O:S:L")) != -1)
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'b':
+ speed = atoi(optarg);
+ break;
+ case 's':
+ if (diali >= MAXDIALS)
+ errx(1, "max dial strings number (%d) exceeded", MAXDIALS);
+ dials[diali++] = strdup(optarg);
+ break;
+ case 't':
+ script_timeout = atoi(optarg);
+ break;
+ case 'w':
+ wait_time = atoi(optarg);
+ break;
+ case 'W':
+ MAXTRIES = atoi(optarg);
+ break;
+ case 'A':
+ annex = strdup(optarg);
+ break;
+ case 'U':
+ upscript = strdup(optarg);
+ break;
+ case 'D':
+ downscript = strdup(optarg);
+ break;
+ case 'L':
+ uucp_lock = 1;
+ break;
+ case 'l':
+ modem_control = 0;
+ break;
+ case 'h':
+ flowcontrol = FC_HW;
+ break;
+ case 'K':
+ keepal = atoi(optarg);
+ break;
+ case 'O':
+ outfill = atoi(optarg);
+ break;
+ case 'S':
+ sl_unit = atoi(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 3)
+ usage();
+
+ /*
+ * Copy these so they exist after we clobber them.
+ */
+ devicename = strdup(argv[0]);
+ username = strdup(argv[1]);
+ password = strdup(argv[2]);
+
+ /*
+ * Security hack. Do not want private information such as the
+ * password and possible phone number to be left around.
+ * So we clobber the arguments.
+ */
+ for (ap = argv - optind + 1; ap < argv + 3; ap++)
+ for (cp = *ap; *cp != 0; cp++)
+ *cp = '\0';
+
+ openlog("startslip", LOG_PID|LOG_PERROR, LOG_DAEMON);
+
+ if (debug)
+ setbuf(stdout, NULL);
+
+ signal(SIGTERM, sigterm);
+ if ((dvname = strrchr(devicename, '/')) == NULL)
+ dvname = devicename;
+ else
+ dvname++;
+ if (snprintf(pidfile, sizeof(pidfile), PIDFILE, _PATH_VARRUN, dvname)
+ >= (int)sizeof(pidfile))
+ usage();
+
+ if ((pfd = fopen(pidfile, "r")) != NULL) {
+ if (fscanf(pfd, "%ld\n", &lpid) == 1) {
+ pid = lpid;
+ if (pid == lpid && pid > 0)
+ kill(pid, SIGTERM);
+ }
+ fclose(pfd);
+ pfd = NULL; /* not remove pidfile yet */
+ sleep(5); /* allow down script to be completed */
+ } else
+restart:
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGURG, SIG_IGN);
+ hup = 0;
+ if (wfd) {
+ printd("fclose, ");
+ fclose(wfd);
+ conn_time = time(NULL) - start_time;
+ if (uucp_lock)
+ uu_unlock(dvname);
+ locked = 0;
+ wfd = NULL;
+ fd = -1;
+ sleep(5);
+ } else if (fd >= 0) {
+ printd("close, ");
+ close(fd);
+ conn_time = time(NULL) - start_time;
+ if (uucp_lock)
+ uu_unlock(dvname);
+ locked = 0;
+ fd = -1;
+ sleep(5);
+ }
+ if (logged_in) {
+ syslog(LOG_INFO, "%s: connection time elapsed: %ld secs",
+ username, (long)conn_time);
+ sprintf(buf, "LINE=%d %s %s down",
+ diali ? (dialc - 1) % diali : 0,
+ downscript ? downscript : _PATH_IFCONFIG , unitname);
+ (void) system(buf);
+ logged_in = 0;
+ }
+ if (terminate)
+ down(0);
+ tries++;
+ if (MAXTRIES > 0 && tries > MAXTRIES) {
+ syslog(LOG_ERR, "%s: exiting login after %d tries", username, tries);
+ /* ???
+ if (first)
+ */
+ down(3);
+ }
+ if (tries > 1) {
+ syslog(LOG_INFO, "%s: sleeping %d seconds (%d tries)",
+ username, wait_time * (tries - 1), tries);
+ sleep(wait_time * (tries - 1));
+ if (terminate)
+ goto restart;
+ }
+
+ if (daemon(1, debug) < 0) {
+ syslog(LOG_ERR, "%s: daemon: %m", username);
+ down(2);
+ }
+
+ pid = getpid();
+ printd("restart: pid %ld: ", (long)pid);
+ if ((pfd = fopen(pidfile, "w")) != NULL) {
+ fprintf(pfd, "%ld\n", (long)pid);
+ fclose(pfd);
+ }
+ printd("open");
+ if (uucp_lock) {
+ int res;
+ if ((res = uu_lock(dvname)) != UU_LOCK_OK) {
+ if (res != UU_LOCK_INUSE)
+ syslog(LOG_ERR, "uu_lock: %s", uu_lockerr(res));
+ syslog(LOG_ERR, "%s: can't lock %s", username, devicename);
+ goto restart;
+ }
+ locked = 1;
+ }
+ if ((fd = open(devicename, O_RDWR | O_NONBLOCK)) < 0) {
+ syslog(LOG_ERR, "%s: open %s: %m", username, devicename);
+ if (first)
+ down(1);
+ else {
+ if (uucp_lock)
+ uu_unlock(dvname);
+ locked = 0;
+ goto restart;
+ }
+ }
+ printd(" %d", fd);
+ signal(SIGHUP, sighup);
+ if (ioctl(fd, TIOCSCTTY, 0) < 0) {
+ syslog(LOG_ERR, "%s: ioctl (TIOCSCTTY): %m", username);
+ down(2);
+ }
+ if (tcsetpgrp(fd, getpid()) < 0) {
+ syslog(LOG_ERR, "%s: tcsetpgrp failed: %m", username);
+ down(2);
+ }
+ printd(", ioctl\n");
+ if (tcgetattr(fd, &t) < 0) {
+ syslog(LOG_ERR, "%s: tcgetattr(%s): %m", username, devicename);
+ down(2);
+ }
+ cfmakeraw(&t);
+ switch (flowcontrol) {
+ case FC_HW:
+ t.c_cflag |= (CRTS_IFLOW|CCTS_OFLOW);
+ break;
+ case FC_NONE:
+ t.c_cflag &= ~(CRTS_IFLOW|CCTS_OFLOW);
+ break;
+ }
+ if (modem_control)
+ t.c_cflag |= HUPCL;
+ else
+ t.c_cflag &= ~(HUPCL);
+ t.c_cflag |= CLOCAL; /* until modem commands passes */
+ cfsetispeed(&t, speed);
+ cfsetospeed(&t, speed);
+ if (tcsetattr(fd, TCSAFLUSH, &t) < 0) {
+ syslog(LOG_ERR, "%s: tcsetattr(%s): %m", username, devicename);
+ down(2);
+ }
+ sleep(2); /* wait for flakey line to settle */
+ if (hup || terminate)
+ goto restart;
+
+ wfd = fdopen(fd, "w+");
+ if (wfd == NULL) {
+ syslog(LOG_ERR, "%s: can't fdopen %s: %m", username, devicename);
+ down(2);
+ }
+ setbuf(wfd, NULL);
+
+ if (diali > 0)
+ dialerstring = dials[dialc++ % diali];
+ if (dialerstring) {
+ syslog(LOG_INFO, "%s: dialer string: %s\\r", username, dialerstring);
+ fprintf(wfd, "%s\r", dialerstring);
+ }
+ printd("\n");
+
+ fintimeout = time(NULL) + script_timeout;
+ if (modem_control) {
+ printd("waiting for carrier\n");
+ while (time(NULL) < fintimeout && !carrier()) {
+ sleep(1);
+ if (hup || terminate)
+ goto restart;
+ }
+ if (!carrier())
+ goto restart;
+ t.c_cflag &= ~(CLOCAL);
+ if (tcsetattr(fd, TCSANOW, &t) < 0) {
+ syslog(LOG_ERR, "%s: tcsetattr(%s): %m", username, devicename);
+ down(2);
+ }
+ /* Only now we able to receive HUP on carrier drop! */
+ }
+
+ /*
+ * Log in
+ */
+ printd("look for login: ");
+ for (;;) {
+ if (getline(buf, BUFSIZ, fd, fintimeout) == 0 || hup || terminate)
+ goto restart;
+ if (annex) {
+ if (bcmp(buf, annex, strlen(annex)) == 0) {
+ fprintf(wfd, "slip\r");
+ printd("Sent \"slip\"\n");
+ continue;
+ }
+ if (bcmp(&buf[1], "sername:", 8) == 0) {
+ fprintf(wfd, "%s\r", username);
+ printd("Sent login: %s\n", username);
+ continue;
+ }
+ if (bcmp(&buf[1], "assword:", 8) == 0) {
+ fprintf(wfd, "%s\r", password);
+ printd("Sent password: %s\n", password);
+ break;
+ }
+ } else {
+ if (strstr(&buf[1], "ogin:") != NULL) {
+ fprintf(wfd, "%s\r", username);
+ printd("Sent login: %s\n", username);
+ continue;
+ }
+ if (strstr(&buf[1], "assword:") != NULL) {
+ fprintf(wfd, "%s\r", password);
+ printd("Sent password: %s\n", password);
+ break;
+ }
+ }
+ }
+
+ sleep(5); /* Wait until login completed */
+ if (hup || terminate)
+ goto restart;
+ start_time = time(NULL);
+ /*
+ * Attach
+ */
+ printd("setd");
+ disc = SLIPDISC;
+ if (ioctl(fd, TIOCSETD, &disc) < 0) {
+ syslog(LOG_ERR, "%s: ioctl (%s, TIOCSETD): %m",
+ username, devicename);
+ down(2);
+ }
+ if (sl_unit >= 0 && ioctl(fd, SLIOCSUNIT, &sl_unit) < 0) {
+ syslog(LOG_ERR, "%s: ioctl(SLIOCSUNIT): %m", username);
+ down(2);
+ }
+ if (ioctl(fd, SLIOCGUNIT, &unitnum) < 0) {
+ syslog(LOG_ERR, "%s: ioctl(SLIOCGUNIT): %m", username);
+ down(2);
+ }
+ sprintf(unitname, "sl%d", unitnum);
+
+ if (keepal > 0) {
+ signal(SIGURG, sigurg);
+ if (ioctl(fd, SLIOCSKEEPAL, &keepal) < 0) {
+ syslog(LOG_ERR, "%s: ioctl(SLIOCSKEEPAL): %m", username);
+ down(2);
+ }
+ }
+ if (outfill > 0 && ioctl(fd, SLIOCSOUTFILL, &outfill) < 0) {
+ syslog(LOG_ERR, "%s: ioctl(SLIOCSOUTFILL): %m", username);
+ down(2);
+ }
+
+ sprintf(buf, "LINE=%d %s %s up",
+ diali ? (dialc - 1) % diali : 0,
+ upscript ? upscript : _PATH_IFCONFIG , unitname);
+ (void) system(buf);
+
+ printd(", ready\n");
+ if (!first)
+ syslog(LOG_INFO, "%s: reconnected on %s (%d tries)", username, unitname, tries);
+ else
+ syslog(LOG_INFO, "%s: connected on %s", username, unitname);
+ first = 0;
+ tries = 0;
+ logged_in = 1;
+ while (hup == 0 && terminate == 0) {
+ sigpause(0L);
+ printd("sigpause return\n");
+ }
+ goto restart;
+ return(0); /* not reached */
+}
+
+void
+sighup(int sig __unused)
+{
+
+ printd("hup\n");
+ if (hup == 0 && logged_in)
+ syslog(LOG_INFO, "%s: got hangup signal", username);
+ hup = 1;
+}
+
+void
+sigurg(int sig __unused)
+{
+
+ printd("urg\n");
+ if (hup == 0 && logged_in)
+ syslog(LOG_INFO, "%s: got dead line signal", username);
+ hup = 1;
+}
+
+void
+sigterm(int sig __unused)
+{
+
+ printd("terminate\n");
+ if (terminate == 0 && logged_in)
+ syslog(LOG_INFO, "%s: got terminate signal", username);
+ terminate = 1;
+}
+
+int
+getline(char *buf, int size, int fd, time_t fintimeout)
+{
+ int i;
+ int ret;
+ fd_set readfds;
+ struct timeval tv;
+ time_t timeout;
+
+ size--;
+ for (i = 0; i < size; i++) {
+ if (hup || terminate)
+ return (0);
+ if ((timeout = fintimeout - time(NULL)) <= 0)
+ goto tout;
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ if ((ret = select(fd + 1, &readfds, NULL, NULL, &tv)) < 0) {
+ if (errno != EINTR)
+ syslog(LOG_ERR, "%s: getline: select: %m", username);
+ } else {
+ if (! ret) {
+ tout:
+ printd("getline: timed out\n");
+ return (0);
+ }
+ if ((ret = read(fd, &buf[i], 1)) == 1) {
+ buf[i] &= 0177;
+ if (buf[i] == '\r' || buf[i] == '\0') {
+ i--;
+ continue;
+ }
+ if (buf[i] != '\n' && buf[i] != ':')
+ continue;
+ buf[i + 1] = '\0';
+ printd("Got %d: %s", i + 1, buf);
+ return (i+1);
+ }
+ if (ret <= 0) {
+ if (ret < 0) {
+ syslog(LOG_ERR, "%s: getline: read: %m", username);
+ } else
+ syslog(LOG_ERR, "%s: read returned 0", username);
+ buf[i] = '\0';
+ printd("returning %d after %d: %s\n", ret, i, buf);
+ return (0);
+ }
+ }
+ }
+ return (0);
+}
+
+int
+carrier(void)
+{
+ int comstate;
+
+ if (ioctl(fd, TIOCMGET, &comstate) < 0) {
+ syslog(LOG_ERR, "%s: ioctl (%s, TIOCMGET): %m",
+ username, devicename);
+ down(2);
+ }
+ return !!(comstate & TIOCM_CD);
+}
+
+void
+down(int code)
+{
+ if (fd > -1)
+ close(fd);
+ if (pfd)
+ unlink(pidfile);
+ if (uucp_lock && locked)
+ uu_unlock(dvname);
+ exit(code);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
+"usage: startslip [-d] [-b speed] [-s string1 [-s string2 [...]]] [-h] [-l]",
+" [-L] [-A annexname] [-U upscript] [-D downscript]",
+" [-t script_timeout] [-W maxtries] [-w retry_pause]",
+" [-K keepalive] [-O outfill] [-S unit] device user password");
+ exit(1);
+}
diff --git a/sbin/sunlabel/Makefile b/sbin/sunlabel/Makefile
new file mode 100644
index 0000000..143b6c7
--- /dev/null
+++ b/sbin/sunlabel/Makefile
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../sys/geom
+
+PROG= sunlabel
+SRCS= sunlabel.c geom_sunlabel_enc.c
+MAN= sunlabel.8
+
+.if ${MACHINE_ARCH} == "sparc64"
+LINKS= ${BINDIR}/sunlabel ${BINDIR}/disklabel
+MLINKS= sunlabel.8 disklabel.8
+.endif
+
+WARNS?= 5
+
+DPADD= ${LIBGEOM}
+LDADD= -lgeom
+
+.include <bsd.prog.mk>
+
+test: ${PROG}
+ sh ${.CURDIR}/runtest.sh
+
+testx: ${PROG}
+ sh -x ${.CURDIR}/runtest.sh
diff --git a/sbin/sunlabel/runtest.sh b/sbin/sunlabel/runtest.sh
new file mode 100644
index 0000000..73833e0
--- /dev/null
+++ b/sbin/sunlabel/runtest.sh
@@ -0,0 +1,157 @@
+#!/bin/sh
+# $FreeBSD$
+
+TMP=/tmp/$$.
+set -e
+MD=`mdconfig -a -t malloc -s 2m`
+trap "exec 7</dev/null; rm -f ${TMP}* ; mdconfig -d -u ${MD}" EXIT INT TERM
+
+./sunlabel -r -w $MD auto
+
+dd if=/dev/$MD of=${TMP}i0 count=16 > /dev/null 2>&1
+./sunlabel $MD > ${TMP}l0
+
+sed '
+/ c:/{
+p
+s/c:/a:/
+s/3969/1024/
+}
+' ${TMP}l0 > ${TMP}l1
+
+./sunlabel -R $MD ${TMP}l1
+if [ -c /dev/${MD}a ] ; then
+ echo "PASS: Created a: partition" 1>&2
+else
+ echo "FAIL: Did not create a: partition" 1>&2
+ exit 2
+fi
+
+# Spoil and rediscover
+
+true > /dev/${MD}
+if [ -c /dev/${MD}a ] ; then
+ echo "PASS: Recreated a: partition after spoilage" 1>&2
+else
+ echo "FAIL: Did not recreate a: partition after spoilage" 1>&2
+ exit 2
+fi
+
+dd if=/dev/$MD of=${TMP}i1 count=16 > /dev/null 2>&1
+sed '
+/ c:/{
+p
+s/c:/a:/
+s/3969/2048/
+}
+' ${TMP}l0 > ${TMP}l2
+
+./sunlabel -R $MD ${TMP}l2
+dd if=/dev/$MD of=${TMP}i2 count=16 > /dev/null 2>&1
+
+exec 7< /dev/${MD}a
+
+for t in a c
+do
+ if dd if=${TMP}i2 of=/dev/${MD}$t 2>/dev/null ; then
+ echo "PASS: Could rewrite same label to ...$t while ...a open" 1>&2
+ else
+ echo "FAIL: Could not rewrite same label to ...$t while ...a open" 1>&2
+ exit 2
+ fi
+
+ if dd if=${TMP}i1 of=/dev/${MD}$t 2>/dev/null ; then
+ echo "FAIL: Could label with smaller ...a to ...$t while ...a open" 1>&2
+ exit 2
+ else
+ echo "PASS: Could not label with smaller ...a to ...$t while ...a open" 1>&2
+ fi
+
+ if dd if=${TMP}i0 of=/dev/${MD}$t 2>/dev/null ; then
+ echo "FAIL: Could write label missing ...a to ...$t while ...a open" 1>&2
+ exit 2
+ else
+ echo "PASS: Could not write label missing ...a to ...$t while ...a open" 1>&2
+ fi
+done
+
+exec 7< /dev/null
+
+if dd if=${TMP}i0 of=/dev/${MD}c 2>/dev/null ; then
+ echo "PASS: Could write missing ...a label to ...c" 1>&2
+else
+ echo "FAIL: Could not write missing ...a label to ...c" 1>&2
+ exit 2
+fi
+
+if dd if=${TMP}i2 of=/dev/${MD}c 2>/dev/null ; then
+ echo "PASS: Could write large ...a label to ...c" 1>&2
+else
+ echo "FAIL: Could not write large ...a label to ...c" 1>&2
+ exit 2
+fi
+
+if dd if=${TMP}i1 of=/dev/${MD}c 2>/dev/null ; then
+ echo "PASS: Could write small ...a label to ...c" 1>&2
+else
+ echo "FAIL: Could not write small ...a label to ...c" 1>&2
+ exit 2
+fi
+
+if dd if=${TMP}i2 of=/dev/${MD}a 2>/dev/null ; then
+ echo "PASS: Could increase size of ...a by writing to ...a" 1>&2
+else
+ echo "FAIL: Could not increase size of ...a by writing to ...a" 1>&2
+ exit 2
+fi
+
+if dd if=${TMP}i1 of=/dev/${MD}a 2>/dev/null ; then
+ echo "FAIL: Could decrease size of ...a by writing to ...a" 1>&2
+ exit 2
+else
+ echo "PASS: Could not decrease size of ...a by writing to ...a" 1>&2
+fi
+
+if dd if=${TMP}i0 of=/dev/${MD}a 2>/dev/null ; then
+ echo "FAIL: Could delete ...a by writing to ...a" 1>&2
+ exit 2
+else
+ echo "PASS: Could not delete ...a by writing to ...a" 1>&2
+fi
+
+if ./sunlabel -B -b ${TMP}i0 ${MD} ; then
+ if [ ! -c /dev/${MD}a ] ; then
+ echo "FAILED: Writing bootcode killed ...a" 1>&2
+ exit 2
+ else
+ echo "PASS: Could write bootcode while closed" 1>&2
+ fi
+else
+ echo "FAILED: Could not write bootcode while closed" 1>&2
+ exit 2
+fi
+
+exec 7> /dev/${MD}c
+if ktrace ./sunlabel -B -b ${TMP}i0 ${MD} ; then
+ if [ ! -c /dev/${MD}a ] ; then
+ echo "FAILED: Writing bootcode killed ...a" 1>&2
+ exit 2
+ else
+ echo "PASS: Could write bootcode while open" 1>&2
+ fi
+else
+ echo "FAILED: Could not write bootcode while open" 1>&2
+ exit 2
+fi
+exec 7> /dev/null
+
+if dd if=${TMP}i0 of=/dev/${MD}c 2>/dev/null ; then
+ echo "PASS: Could delete ...a by writing to ...c" 1>&2
+else
+ echo "FAIL: Could not delete ...a by writing to ...c" 1>&2
+ exit 2
+fi
+
+# XXX: need to add a 'b' partition and check for overlaps.
+
+exit 0
diff --git a/sbin/sunlabel/sunlabel.8 b/sbin/sunlabel/sunlabel.8
new file mode 100644
index 0000000..6009604
--- /dev/null
+++ b/sbin/sunlabel/sunlabel.8
@@ -0,0 +1,433 @@
+.\" Copyright (c) 2004
+.\" David E. O'Brien. All rights reserved.
+.\" Copyright (c) 2004, 2005
+.\" Joerg Wunsch. 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 30, 2005
+.Dt SUNLABEL 8
+.Os
+.Sh NAME
+.Nm sunlabel
+.Nd read and write disk pack label suitable for Sun's OpenBoot PROM
+.Sh SYNOPSIS
+.Nm
+.Op Fl r
+.Op Fl c No \&| Fl h
+.Ar disk
+.Nm
+.Fl B
+.Op Fl b Ar boot1
+.Op Fl n
+.Ar disk
+.Nm
+.Fl R
+.Op Fl B Op Fl b Ar boot1
+.Op Fl r
+.Op Fl n
+.Op Fl c
+.Ar disk protofile
+.Nm
+.Fl e
+.Op Fl B Op Fl b Ar boot1
+.Op Fl r
+.Op Fl n
+.Op Fl c
+.Ar disk
+.Nm
+.Fl w
+.Op Fl B Op Fl b Ar boot1
+.Op Fl r
+.Op Fl n
+.Op Fl c
+.Ar disk type
+.Sh DESCRIPTION
+The
+.Nm
+utility
+installs, examines or modifies the
+.Em Sun OpenBoot PROM
+label on a disk.
+In addition,
+.Nm
+can install bootstrap code.
+.Ss Introduction
+The label occupies the first sector (i.e., 512 bytes) of each disk.
+It starts with a textual description which by convention also mentions
+the disk geometry in textual form (number of cylinders, alternate
+cylinders, heads, and sectors per track), optionally followed by a
+table of SVR4-compatible VTOC tags and flags per partition, followed
+by the partition table itself.
+Finally, a checksum is recorded to ensure the label has not been
+tampered with.
+.Pp
+The
+.Em Sun OpenBoot PROM
+label allows for 8 disk partitions.
+The partition table lists the starting cylinder of the partition,
+plus the size of the partition in 512-byte sectors.
+Thus, partitions in the
+.Em Sun OpenBoot PROM
+must always start at a cylinder boundary (for whatever geometry
+emulation has been chosen).
+.Pp
+The optional SVR4-compatible VTOC tag and flags table is not used
+by the
+.Fx
+kernel.
+It is maintained solely for compatibilty with the
+.Tn Solaris
+operating system that might share disks with
+.Fx
+on the same hardware platform.
+.Pp
+The
+.Em Sun OpenBoot PROM
+label is natively understood by the underlying hardware, which can
+bootstrap from a single partition entry, as opposed to the very first
+block(s) of the entire disk as on many other hardware platforms.
+.Pp
+Note that the hardware platform mandates that two cylinders are set
+aside as
+.Em alternate cylinders
+which are not available to user programs (and not even through the
+.Dq Li backup
+partition).
+.Ss Options
+Options are listed in alphabetical order here.
+Note that only those option combinations listed under
+.Sx SYNOPSIS
+are allowable.
+.Pp
+.Bl -tag -width ".Fl b Ar bootpath"
+.It Fl b Ar bootpath
+Specify that
+.Ar bootpath
+is to be used as the boot image, rather than the default of
+.Pa /boot/boot1 .
+.It Fl B
+Install bootstrap code onto the disk.
+Note that since the underlying hardware platform bootstraps from
+partitions, not disks, this operation is only useful if there is
+a partition starting at offset 0.
+.It Fl c
+Use cylinders for partition size display rather than
+(512-byte) sectors.
+This also changes the default interpretation of the partition
+size entries when editing the label, or reading from a prototype
+file.
+Thus, prototype files are only compatible when both, obtaining
+the file and re-installing it is done using the same
+.Fl c
+option setting.
+.It Fl e
+Enter edit mode.
+See
+.Sx Edit mode
+below for a more detailed explanation.
+.It Fl h
+When displaying the label, make the partition size and offset
+values
+.Dq human readable .
+The displayed numbers will get a suffix of
+.Ql B
+for bytes,
+.Ql K
+for 1024 bytes each,
+.Ql M
+for 1048576 bytes each, or
+.Ql G
+for 1073741824 bytes each appended.
+Note that due to possible rounding errors, prototype files
+obtained using the
+.Fl h
+option are not suited for re-installing using the
+.Fl R
+option.
+.It Fl n
+No changes.
+All operations, checks etc., are performed normally, but nothing
+is written to disk.
+.It Fl r
+Obsolete option that used to indicate that the operation should
+be done directly on disk, as opposed through the respective kernel
+services.
+Ignored.
+.It Fl R
+Restore label from the prototype in
+.Ar protofile .
+A prototype file is simply the textual representation of the
+label as printed using the first form of the
+.Nm
+utility shown in the
+.Sx SYNOPSIS .
+Note that the
+.Fl c
+option used to obtain the prototype must match the option used
+when restoring the label (both present, or both absent).
+.It Fl w
+Write mode.
+Suitable to write an initial label to disk.
+The
+.Ar type
+argument used to be an entry into a table of predefined labels,
+but this functionality is not supported by
+.Nm .
+Instead, the only allowable
+.Ar type
+argument is the string
+.Dq Li auto ,
+indicating that an automatically created label should be written
+to disk.
+This automatism will try to create an initial label that fits as
+best as possible into the available disk capacity.
+.El
+.Pp
+If neither of the
+.Fl e , R ,
+or
+.Fl w
+options are present, the existing label for
+.Ar disk
+will be printed to standard output.
+.Pp
+The
+.Ar disk
+argument
+must be given as a plain disk name, without any leading
+.Pa /dev/ .
+.Ss Edit mode
+In edit mode, the existing label from
+.Ar disk
+will be read, and put into a template file.
+The command referenced by the
+.Ev EDITOR
+environmental variable will be started to allow the user
+to edit the label.
+The label is then checked and examined for any errors.
+If no errors have been found, the new label is written to disk.
+If there were any errors, a message is printed to standard
+error output, and the user is given the opportunity to edit
+the template file again.
+If accepted, editing starts over.
+If declined, no changes will
+be written to disk.
+.Pp
+The label presented for editing is the same as the standard
+printout, with some added hints about the possible options to
+specify the sector size and starting cylinder.
+The following areas in the template can be edited:
+.Bl -tag -width indent
+.It Sy Textual label, geometry emulation
+The line
+.D1 Li text: Ar XXXX Li cyl Ar CC Li alt 2 hd Ar HH Li sec Ar SS
+represents the label text.
+It must be retained exactly in the form shown.
+The editable text
+.Ar XXXX
+is a simple (non-whitespace) text describing the disk.
+By convention, this text mentions the approximate size of the
+disk, as in
+.Dq Li SUN9.0G
+for a 9 GB disk shipped by Sun.
+.Pp
+The values
+.Ar CC ,
+.Ar HH ,
+and
+.Ar SS
+describe the number of cylinders, heads (tracks per
+cylinder), and sectors per track respectively.
+They might be modified to change the geometry emulation.
+Each number must be between 1 and 65535.
+The product
+.D1 Em (CC + 2) * HH * SS
+must be less than or equal to the total number of sectors of the
+disk (which is given as a hint in a comment field).
+.It Sy Volume name
+The volume name (if present) is introduced by the string
+.Dq "volume name:" .
+It can be up to 8 characters long, and might be useful to distinguish
+different disks in a system.
+Note that volume names require the VTOC elements to be present, so
+any of the VTOC constraints described below need to be obeyed as well
+if a volume name is to be set.
+Setting an empty volume name will delete it from the label.
+.It Sy Partition entries
+Partition entries start with a letter from
+.Ql a
+through
+.Ql h ,
+immediately followed by a colon, followed by the size of this
+partition, and the starting cylinder of the partition.
+The unit of the size field defaults to sectors, or to cylinders
+if the
+.Fl c
+option is in effect.
+Alternatively, a different unit may be specified by appending
+.Ql s
+for (512-byte) sectors,
+.Ql c
+for cylinders,
+.Ql k
+for kilobytes,
+.Ql m
+for megabytes, or
+.Ql g
+for gigabytes.
+The last partition entry may specify the size as
+.Ql *
+to indicate that this entry should consume the rest of disk not
+consumed by any other partition so far.
+.Pp
+The start of partition is always taken as a cylinder number (starting
+at 0) since this is what the underlying hardware uses.
+Alternatively, specifying it as
+.Ql *
+will make the computation automatically chose the nearest possible
+cylinder boundary.
+.Pp
+Partition
+.Ql c
+must always be present, must start at 0, and must cover the entire
+disk (without considering the alternate cylinders though).
+.Pp
+Optionally, each partition entry may be followed by an SVR4-compatible
+VTOC tag name, and a flag description.
+The following VTOC tag names are known:
+.Bl -column -offset indent ".Li unassigned" ".Sy value" ".Sy comment"
+.It Sy name Ta Sy value Ta Sy comment
+.It Li unassigned Ta No 0x00
+.It Li boot Ta No 0x01
+.It Li root Ta No 0x02
+.It Li swap Ta No 0x03
+.It Li usr Ta No 0x04
+.It Li backup Ta No 0x05 Ta c partition, entire disk
+.It Li stand Ta No 0x06
+.It Li var Ta No 0x07
+.It Li home Ta No 0x08
+.It Li altsctr Ta No 0x09 Ta alternate sector partition
+.It Li cache Ta No 0x0a Ta Solaris cachefs partition
+.It Li VxVM_pub Ta No 0x0e Ta VxVM public region
+.It Li VxVM_priv Ta No 0x0f Ta VxVM private region
+.El
+.Pp
+The following VTOC flags are known:
+.Bl -column -offset indent ".Sy name" ".Sy value" ".Sy comment"
+.It Sy name Ta Sy value Ta Sy comment
+.It Li wm Ta No 0x00 Ta read/write, mountable
+.It Li wu Ta No 0x01 Ta read/write, unmountable
+.It Li rm Ta No 0x10 Ta read/only, mountable
+.It Li ru Ta No 0x11 Ta read/only, unmountable
+.El
+.Pp
+Optionally, both the tag and/or the flag name may be specified
+numerically, using standard
+.Ql C
+numerial notation (prefix
+.Ql 0x
+for hexadecimal numbers,
+.Ql 0
+for octal numbers).
+If the flag field is omitted, it defaults to
+.Ql wm .
+If the tag field is also omitted, it defaults to
+.Dq Li unassigned .
+If none of the partitions lists any VTOC tag/flags, no
+SVR4-compatible VTOC elements will be written to disk.
+If VTOC-style elements are present, partition
+.Ql c
+must be marked as
+.Dq Li backup
+(and should be marked
+.Ql wu ) .
+.El
+.Pp
+When checking the label, partition
+.Ql c
+is checked for presence, and for the mentioned restrictions.
+All other partitions are checked for possible overlaps, as
+well as for not extending past the end of unit.
+If VTOC-style elements are present, overlaps of unmountable
+partitions against other partitions will be warned still but
+do not cause a rejection of the label.
+That way,
+.Em encapsulated disks
+of volume management software are acceptable as long as the
+volume management partitions are clearly marked as unmountable.
+.Pp
+Any other fields in the label template are informational only,
+and will not be parsed when reading the label.
+.Pp
+Note that when changing the geometry emulation by editing the
+textual description line, all partition entries will be
+considered based on the new geometry emulation.
+.Sh ENVIRONMENT
+.Bl -tag -width ".Ev EDITOR" -compact
+.It Ev EDITOR
+Name of the command to edit the template file in edit-mode.
+Defaults to
+.Xr vi 1 .
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /boot/boot1" -compact
+.It Pa /boot/boot1
+Default boot image.
+.El
+.Sh SEE ALSO
+.Xr vi 1 ,
+.Xr geom 4 ,
+.Xr bsdlabel 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 5.1 .
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Jake Burkholder ,
+modeling it after the
+.Xr bsdlabel 8
+command available on other architectures.
+.Pp
+.An -nosplit
+This man page was initially written by
+.An David O'Brien ,
+and later substantially updated by
+.An J\(:org Wunsch .
+.Sh BUGS
+Installing bootstrap code onto an entire disk is merely pointless.
+.Nm
+should rather support installing bootstrap code into a partition
+instead.
+.Pp
+The
+.Dq auto
+layout algorithm could be smarter.
+By now, it tends to emulate fairly large cylinders which due to
+the two reserved alternate cylinders causes a fair amount of
+wasted disk space.
diff --git a/sbin/sunlabel/sunlabel.c b/sbin/sunlabel/sunlabel.c
new file mode 100644
index 0000000..7bf051e
--- /dev/null
+++ b/sbin/sunlabel/sunlabel.c
@@ -0,0 +1,1007 @@
+/*-
+ * Copyright (c) 2003 Jake Burkholder.
+ * Copyright (c) 2004,2005 Joerg Wunsch.
+ * 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.
+ */
+/*
+ * Copyright (c) 1994, 1995 Gordon W. Ross
+ * Copyright (c) 1994 Theo de Raadt
+ * All rights reserved.
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Symmetric Computer Systems.
+ *
+ * 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 the University of
+ * California, Berkeley and its contributors.
+ * This product includes software developed by Theo de Raadt.
+ * 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 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: $NetBSD: disksubr.c,v 1.13 2000/12/17 22:39:18 pk $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/disk.h>
+#include <sys/ioctl.h>
+#include <sys/sun_disklabel.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libgeom.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define _PATH_TMPFILE "/tmp/EdDk.XXXXXXXXXX"
+#define _PATH_BOOT "/boot/boot1"
+
+static int bflag;
+static int Bflag;
+static int cflag;
+static int eflag;
+static int hflag;
+static int nflag;
+static int Rflag;
+static int wflag;
+
+static off_t mediasize;
+static uint32_t sectorsize;
+
+struct tags {
+ const char *name;
+ unsigned int id;
+};
+
+static int check_label(struct sun_disklabel *sl);
+static void read_label(struct sun_disklabel *sl, const char *disk);
+static void write_label(struct sun_disklabel *sl, const char *disk,
+ const char *bootpath);
+static void edit_label(struct sun_disklabel *sl, const char *disk,
+ const char *bootpath);
+static int parse_label(struct sun_disklabel *sl, const char *file);
+static void print_label(struct sun_disklabel *sl, const char *disk, FILE *out);
+
+static int parse_size(struct sun_disklabel *sl, int part, char *size);
+static int parse_offset(struct sun_disklabel *sl, int part, char *offset);
+
+static const char *flagname(unsigned int tag);
+static const char *tagname(unsigned int tag);
+static unsigned int parse_flag(struct sun_disklabel *sl, int part,
+ const char *flag);
+static unsigned int parse_tag(struct sun_disklabel *sl, int part,
+ const char *tag);
+static const char *make_h_number(uintmax_t u);
+
+static void usage(void);
+
+extern char *__progname;
+
+static struct tags knowntags[] = {
+ { "unassigned", VTOC_UNASSIGNED },
+ { "boot", VTOC_BOOT },
+ { "root", VTOC_ROOT },
+ { "swap", VTOC_SWAP },
+ { "usr", VTOC_USR },
+ { "backup", VTOC_BACKUP },
+ { "stand", VTOC_STAND },
+ { "var", VTOC_VAR },
+ { "home", VTOC_HOME },
+ { "altsctr", VTOC_ALTSCTR },
+ { "cache", VTOC_CACHE },
+ { "VxVM_pub", VTOC_VXVM_PUB },
+ { "VxVM_priv", VTOC_VXVM_PRIV },
+};
+
+static struct tags knownflags[] = {
+ { "wm", 0 },
+ { "wu", VTOC_UNMNT },
+ { "rm", VTOC_RONLY },
+ { "ru", VTOC_UNMNT | VTOC_RONLY },
+};
+
+/*
+ * Disk label editor for sun disklabels.
+ */
+int
+main(int ac, char **av)
+{
+ struct sun_disklabel sl;
+ const char *bootpath;
+ const char *proto;
+ const char *disk;
+ int ch;
+
+ bootpath = _PATH_BOOT;
+ while ((ch = getopt(ac, av, "b:BcehnrRw")) != -1)
+ switch (ch) {
+ case 'b':
+ bflag = 1;
+ bootpath = optarg;
+ break;
+ case 'B':
+ Bflag = 1;
+ break;
+ case 'c':
+ cflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ break;
+ case 'h':
+ hflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'r':
+ fprintf(stderr, "Obsolete -r flag ignored\n");
+ break;
+ case 'R':
+ Rflag = 1;
+ break;
+ case 'w':
+ wflag = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ if (bflag && !Bflag)
+ usage();
+ if (nflag && !(Bflag || eflag || Rflag || wflag))
+ usage();
+ if (eflag && (Rflag || wflag))
+ usage();
+ if (eflag)
+ hflag = 0;
+ ac -= optind;
+ av += optind;
+ if (ac == 0)
+ usage();
+ bzero(&sl, sizeof(sl));
+ disk = av[0];
+ if (wflag) {
+ if (ac != 2 || strcmp(av[1], "auto") != 0)
+ usage();
+ read_label(&sl, disk);
+ bzero(sl.sl_part, sizeof(sl.sl_part));
+ sl.sl_part[SUN_RAWPART].sdkp_cyloffset = 0;
+ sl.sl_part[SUN_RAWPART].sdkp_nsectors = sl.sl_ncylinders *
+ sl.sl_ntracks * sl.sl_nsectors;
+ write_label(&sl, disk, bootpath);
+ } else if (eflag) {
+ if (ac != 1)
+ usage();
+ read_label(&sl, disk);
+ if (sl.sl_magic != SUN_DKMAGIC)
+ errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk);
+ edit_label(&sl, disk, bootpath);
+ } else if (Rflag) {
+ if (ac != 2)
+ usage();
+ proto = av[1];
+ read_label(&sl, disk);
+ if (parse_label(&sl, proto) != 0)
+ errx(1, "%s: invalid label", proto);
+ write_label(&sl, disk, bootpath);
+ } else if (Bflag) {
+ read_label(&sl, disk);
+ if (sl.sl_magic != SUN_DKMAGIC)
+ errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk);
+ write_label(&sl, disk, bootpath);
+ } else {
+ read_label(&sl, disk);
+ if (sl.sl_magic != SUN_DKMAGIC)
+ errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk);
+ print_label(&sl, disk, stdout);
+ }
+ return (0);
+}
+
+static int
+check_label(struct sun_disklabel *sl)
+{
+ uint64_t nsectors;
+ uint64_t ostart;
+ uint64_t start;
+ uint64_t oend;
+ uint64_t end;
+ int havevtoc;
+ int warnonly;
+ int i;
+ int j;
+
+ havevtoc = sl->sl_vtoc_sane == SUN_VTOC_SANE;
+
+ nsectors = sl->sl_ncylinders * sl->sl_ntracks * sl->sl_nsectors;
+ if (sl->sl_part[SUN_RAWPART].sdkp_cyloffset != 0 ||
+ sl->sl_part[SUN_RAWPART].sdkp_nsectors != nsectors) {
+ warnx("partition c is incorrect, must start at 0 and cover "
+ "whole disk");
+ return (1);
+ }
+ if (havevtoc && sl->sl_vtoc_map[2].svtoc_tag != VTOC_BACKUP) {
+ warnx("partition c must have tag \"backup\"");
+ return (1);
+ }
+ for (i = 0; i < SUN_NPART; i++) {
+ if (i == 2 || sl->sl_part[i].sdkp_nsectors == 0)
+ continue;
+ start = (uint64_t)sl->sl_part[i].sdkp_cyloffset *
+ sl->sl_ntracks * sl->sl_nsectors;
+ end = start + sl->sl_part[i].sdkp_nsectors;
+ if (end > nsectors) {
+ warnx("partition %c extends past end of disk",
+ 'a' + i);
+ return (1);
+ }
+ if (havevtoc) {
+ if (sl->sl_vtoc_map[i].svtoc_tag == VTOC_BACKUP) {
+ warnx("only partition c is allowed to have "
+ "tag \"backup\"");
+ return (1);
+ }
+ }
+ for (j = 0; j < SUN_NPART; j++) {
+ /*
+ * Overlaps for unmountable partitions are
+ * non-fatal but will be warned anyway.
+ */
+ warnonly = havevtoc &&
+ ((sl->sl_vtoc_map[i].svtoc_flag & VTOC_UNMNT) != 0 ||
+ (sl->sl_vtoc_map[j].svtoc_flag & VTOC_UNMNT) != 0);
+
+ if (j == 2 || j == i ||
+ sl->sl_part[j].sdkp_nsectors == 0)
+ continue;
+ ostart = (uint64_t)sl->sl_part[j].sdkp_cyloffset *
+ sl->sl_ntracks * sl->sl_nsectors;
+ oend = ostart + sl->sl_part[j].sdkp_nsectors;
+ if ((start <= ostart && end >= oend) ||
+ (start > ostart && start < oend) ||
+ (end > ostart && end < oend)) {
+ warnx("partition %c overlaps partition %c",
+ 'a' + i, 'a' + j);
+ if (!warnonly)
+ return (1);
+ }
+ }
+ }
+ return (0);
+}
+
+static void
+read_label(struct sun_disklabel *sl, const char *disk)
+{
+ char path[MAXPATHLEN];
+ uint32_t fwsectors;
+ uint32_t fwheads;
+ char buf[SUN_SIZE];
+ int fd, error;
+
+ snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk);
+ if ((fd = open(path, O_RDONLY)) < 0)
+ err(1, "open %s", path);
+ if (read(fd, buf, sizeof(buf)) != sizeof(buf))
+ err(1, "read");
+ error = sunlabel_dec(buf, sl);
+ if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) != 0)
+ if (error)
+ err(1, "%s: ioctl(DIOCGMEDIASIZE) failed", disk);
+ if (ioctl(fd, DIOCGSECTORSIZE, &sectorsize) != 0) {
+ if (error)
+ err(1, "%s: DIOCGSECTORSIZE failed", disk);
+ else
+ sectorsize = 512;
+ }
+ if (error) {
+ bzero(sl, sizeof(*sl));
+ if (ioctl(fd, DIOCGFWSECTORS, &fwsectors) != 0)
+ fwsectors = 63;
+ if (ioctl(fd, DIOCGFWHEADS, &fwheads) != 0) {
+ if (mediasize <= 63 * 1024 * sectorsize)
+ fwheads = 1;
+ else if (mediasize <= 63 * 16 * 1024 * sectorsize)
+ fwheads = 16;
+ else
+ fwheads = 255;
+ }
+ sl->sl_rpm = 3600;
+ sl->sl_pcylinders = mediasize / (fwsectors * fwheads *
+ sectorsize);
+ sl->sl_sparespercyl = 0;
+ sl->sl_interleave = 1;
+ sl->sl_ncylinders = sl->sl_pcylinders - 2;
+ sl->sl_acylinders = 2;
+ sl->sl_nsectors = fwsectors;
+ sl->sl_ntracks = fwheads;
+ sl->sl_part[SUN_RAWPART].sdkp_cyloffset = 0;
+ sl->sl_part[SUN_RAWPART].sdkp_nsectors = sl->sl_ncylinders *
+ sl->sl_ntracks * sl->sl_nsectors;
+ if (mediasize > (off_t)4999L * 1024L * 1024L) {
+ sprintf(sl->sl_text,
+ "FreeBSD%jdG cyl %u alt %u hd %u sec %u",
+ (intmax_t)(mediasize + 512 * 1024 * 1024) /
+ (1024 * 1024 * 1024),
+ sl->sl_ncylinders, sl->sl_acylinders,
+ sl->sl_ntracks, sl->sl_nsectors);
+ } else {
+ sprintf(sl->sl_text,
+ "FreeBSD%jdM cyl %u alt %u hd %u sec %u",
+ (intmax_t)(mediasize + 512 * 1024) / (1024 * 1024),
+ sl->sl_ncylinders, sl->sl_acylinders,
+ sl->sl_ntracks, sl->sl_nsectors);
+ }
+ }
+ close(fd);
+}
+
+static void
+write_label(struct sun_disklabel *sl, const char *disk, const char *bootpath)
+{
+ char path[MAXPATHLEN];
+ char boot[SUN_BOOTSIZE];
+ char buf[SUN_SIZE];
+ const char *errstr;
+ off_t off;
+ int bfd;
+ int fd;
+ int i;
+ struct gctl_req *grq;
+
+ sl->sl_magic = SUN_DKMAGIC;
+
+ if (check_label(sl) != 0)
+ errx(1, "invalid label");
+
+ bzero(buf, sizeof(buf));
+ sunlabel_enc(buf, sl);
+
+ if (nflag) {
+ print_label(sl, disk, stdout);
+ return;
+ }
+ if (Bflag) {
+ if ((bfd = open(bootpath, O_RDONLY)) < 0)
+ err(1, "open %s", bootpath);
+ i = read(bfd, boot, sizeof(boot));
+ if (i < 0)
+ err(1, "read");
+ else if (i != sizeof (boot))
+ errx(1, "read wrong size boot code (%d)", i);
+ close(bfd);
+ }
+ snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk);
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ grq = gctl_get_handle();
+ gctl_ro_param(grq, "verb", -1, "write label");
+ gctl_ro_param(grq, "class", -1, "SUN");
+ gctl_ro_param(grq, "geom", -1, disk);
+ gctl_ro_param(grq, "label", sizeof buf, buf);
+ errstr = gctl_issue(grq);
+ if (errstr != NULL)
+ errx(1, "%s", errstr);
+ gctl_free(grq);
+ if (Bflag) {
+ grq = gctl_get_handle();
+ gctl_ro_param(grq, "verb", -1, "write bootcode");
+ gctl_ro_param(grq, "class", -1, "SUN");
+ gctl_ro_param(grq, "geom", -1, disk);
+ gctl_ro_param(grq, "bootcode", sizeof boot, boot);
+ errstr = gctl_issue(grq);
+ if (errstr != NULL)
+ errx(1, "%s", errstr);
+ gctl_free(grq);
+ }
+ } else {
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ err(1, "lseek");
+ if (write(fd, buf, sizeof(buf)) != sizeof(buf))
+ err (1, "write");
+ if (Bflag) {
+ for (i = 0; i < SUN_NPART; i++) {
+ if (sl->sl_part[i].sdkp_nsectors == 0)
+ continue;
+ off = sl->sl_part[i].sdkp_cyloffset *
+ sl->sl_ntracks * sl->sl_nsectors * 512;
+ /*
+ * Ignore first SUN_SIZE bytes of boot code to
+ * avoid overwriting the label.
+ */
+ if (lseek(fd, off + SUN_SIZE, SEEK_SET) < 0)
+ err(1, "lseek");
+ if (write(fd, boot + SUN_SIZE,
+ sizeof(boot) - SUN_SIZE) !=
+ sizeof(boot) - SUN_SIZE)
+ err(1, "write");
+ }
+ }
+ close(fd);
+ }
+ exit(0);
+}
+
+static void
+edit_label(struct sun_disklabel *sl, const char *disk, const char *bootpath)
+{
+ char tmpfil[] = _PATH_TMPFILE;
+ const char *editor;
+ int status;
+ FILE *fp;
+ pid_t pid;
+ pid_t r;
+ int fd;
+ int c;
+
+ if ((fd = mkstemp(tmpfil)) < 0)
+ err(1, "mkstemp");
+ if ((fp = fdopen(fd, "w")) == NULL)
+ err(1, "fdopen");
+ print_label(sl, disk, fp);
+ fflush(fp);
+ for (;;) {
+ if ((pid = fork()) < 0)
+ err(1, "fork");
+ if (pid == 0) {
+ if ((editor = getenv("EDITOR")) == NULL)
+ editor = _PATH_VI;
+ execlp(editor, editor, tmpfil, (char *)NULL);
+ err(1, "execlp %s", editor);
+ }
+ status = 0;
+ while ((r = wait(&status)) > 0 && r != pid)
+ ;
+ if (WIFEXITED(status)) {
+ if (parse_label(sl, tmpfil) == 0) {
+ fclose(fp);
+ unlink(tmpfil);
+ write_label(sl, disk, bootpath);
+ return;
+ }
+ printf("re-edit the label? [y]: ");
+ fflush(stdout);
+ c = getchar();
+ if (c != EOF && c != '\n')
+ while (getchar() != '\n')
+ ;
+ if (c == 'n') {
+ fclose(fp);
+ unlink(tmpfil);
+ return;
+ }
+ }
+ }
+ fclose(fp);
+ unlink(tmpfil);
+ return;
+}
+
+static int
+parse_label(struct sun_disklabel *sl, const char *file)
+{
+ char offset[32];
+ char size[32];
+ char flag[32];
+ char tag[32];
+ char buf[128];
+ char text[128];
+ char volname[SUN_VOLNAME_LEN + 1];
+ struct sun_disklabel sl1;
+ char *bp;
+ const char *what;
+ uint8_t part;
+ FILE *fp;
+ int line;
+ int rv;
+ int wantvtoc;
+ unsigned alt, cyl, hd, nr, sec;
+
+ line = wantvtoc = 0;
+ if ((fp = fopen(file, "r")) == NULL)
+ err(1, "fopen");
+ sl1 = *sl;
+ bzero(&sl1.sl_part, sizeof(sl1.sl_part));
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ /*
+ * In order to recognize a partition entry, we search
+ * for lines starting with a single letter followed by
+ * a colon as their first non-white characters. We
+ * silently ignore any other lines, so any comment etc.
+ * lines in the label template will be ignored.
+ *
+ * XXX We should probably also recognize the geometry
+ * fields on top, and allow changing the geometry
+ * emulated by this disk.
+ */
+ for (bp = buf; isspace(*bp); bp++)
+ ;
+ if (strncmp(bp, "text:", strlen("text:")) == 0) {
+ bp += strlen("text:");
+ rv = sscanf(bp,
+ " %s cyl %u alt %u hd %u sec %u",
+ text, &cyl, &alt, &hd, &sec);
+ if (rv != 5) {
+ warnx("%s, line %d: text label does not "
+ "contain required fields",
+ file, line + 1);
+ fclose(fp);
+ return (1);
+ }
+ if (alt != 2) {
+ warnx("%s, line %d: # alt must be equal 2",
+ file, line + 1);
+ fclose(fp);
+ return (1);
+ }
+ if (cyl == 0 || cyl > USHRT_MAX) {
+ what = "cyl";
+ nr = cyl;
+ unreasonable:
+ warnx("%s, line %d: # %s %d unreasonable",
+ file, line + 1, what, nr);
+ fclose(fp);
+ return (1);
+ }
+ if (hd == 0 || hd > USHRT_MAX) {
+ what = "hd";
+ nr = hd;
+ goto unreasonable;
+ }
+ if (sec == 0 || sec > USHRT_MAX) {
+ what = "sec";
+ nr = sec;
+ goto unreasonable;
+ }
+ if (mediasize == 0)
+ warnx("unit size unknown, no sector count "
+ "check could be done");
+ else if ((uintmax_t)(cyl + alt) * sec * hd >
+ (uintmax_t)mediasize / sectorsize) {
+ warnx("%s, line %d: sector count %ju exceeds "
+ "unit size %ju",
+ file, line + 1,
+ (uintmax_t)(cyl + alt) * sec * hd,
+ (uintmax_t)mediasize / sectorsize);
+ fclose(fp);
+ return (1);
+ }
+ sl1.sl_pcylinders = cyl + alt;
+ sl1.sl_ncylinders = cyl;
+ sl1.sl_acylinders = alt;
+ sl1.sl_nsectors = sec;
+ sl1.sl_ntracks = hd;
+ memset(sl1.sl_text, 0, sizeof(sl1.sl_text));
+ snprintf(sl1.sl_text, sizeof(sl1.sl_text),
+ "%s cyl %u alt %u hd %u sec %u",
+ text, cyl, alt, hd, sec);
+ continue;
+ }
+ if (strncmp(bp, "volume name:", strlen("volume name:")) == 0) {
+ wantvtoc = 1; /* Volume name requires VTOC. */
+ bp += strlen("volume name:");
+#if SUN_VOLNAME_LEN != 8
+# error "scanf field width does not match SUN_VOLNAME_LEN"
+#endif
+ /*
+ * We set the field length to one more than
+ * SUN_VOLNAME_LEN to allow detecting an
+ * overflow.
+ */
+ memset(volname, 0, sizeof volname);
+ rv = sscanf(bp, " %9[^\n]", volname);
+ if (rv != 1) {
+ /* Clear the volume name. */
+ memset(sl1.sl_vtoc_volname, 0,
+ SUN_VOLNAME_LEN);
+ } else {
+ memcpy(sl1.sl_vtoc_volname, volname,
+ SUN_VOLNAME_LEN);
+ if (volname[SUN_VOLNAME_LEN] != '\0')
+ warnx(
+"%s, line %d: volume name longer than %d characters, truncating",
+ file, line + 1, SUN_VOLNAME_LEN);
+ }
+ continue;
+ }
+ if (strlen(bp) < 2 || bp[1] != ':') {
+ line++;
+ continue;
+ }
+ rv = sscanf(bp, "%c: %30s %30s %30s %30s",
+ &part, size, offset, tag, flag);
+ if (rv < 3) {
+ syntaxerr:
+ warnx("%s: syntax error on line %d",
+ file, line + 1);
+ fclose(fp);
+ return (1);
+ }
+ if (parse_size(&sl1, part - 'a', size) ||
+ parse_offset(&sl1, part - 'a', offset))
+ goto syntaxerr;
+ if (rv > 3) {
+ wantvtoc = 1;
+ if (rv == 5 && parse_flag(&sl1, part - 'a', flag))
+ goto syntaxerr;
+ if (parse_tag(&sl1, part - 'a', tag))
+ goto syntaxerr;
+ }
+ line++;
+ }
+ fclose(fp);
+ if (wantvtoc) {
+ sl1.sl_vtoc_sane = SUN_VTOC_SANE;
+ sl1.sl_vtoc_vers = SUN_VTOC_VERSION;
+ sl1.sl_vtoc_nparts = SUN_NPART;
+ } else {
+ sl1.sl_vtoc_sane = 0;
+ sl1.sl_vtoc_vers = 0;
+ sl1.sl_vtoc_nparts = 0;
+ bzero(&sl1.sl_vtoc_map, sizeof(sl1.sl_vtoc_map));
+ }
+ *sl = sl1;
+ return (check_label(sl));
+}
+
+static int
+parse_size(struct sun_disklabel *sl, int part, char *size)
+{
+ uintmax_t nsectors;
+ uintmax_t total;
+ uintmax_t n;
+ char *p;
+ int i;
+
+ nsectors = 0;
+ n = strtoumax(size, &p, 10);
+ if (*p != '\0') {
+ if (strcmp(size, "*") == 0) {
+ total = sl->sl_ncylinders * sl->sl_ntracks *
+ sl->sl_nsectors;
+ for (i = 0; i < part; i++) {
+ if (i == 2)
+ continue;
+ nsectors += sl->sl_part[i].sdkp_nsectors;
+ }
+ n = total - nsectors;
+ } else if (p[1] == '\0' && (p[0] == 'C' || p[0] == 'c')) {
+ n = n * sl->sl_ntracks * sl->sl_nsectors;
+ } else if (p[1] == '\0' && (p[0] == 'K' || p[0] == 'k')) {
+ n = roundup((n * 1024) / 512,
+ sl->sl_ntracks * sl->sl_nsectors);
+ } else if (p[1] == '\0' && (p[0] == 'M' || p[0] == 'm')) {
+ n = roundup((n * 1024 * 1024) / 512,
+ sl->sl_ntracks * sl->sl_nsectors);
+ } else if (p[1] == '\0' && (p[0] == 'S' || p[0] == 's')) {
+ /* size in sectors, no action neded */
+ } else if (p[1] == '\0' && (p[0] == 'G' || p[0] == 'g')) {
+ n = roundup((n * 1024 * 1024 * 1024) / 512,
+ sl->sl_ntracks * sl->sl_nsectors);
+ } else
+ return (-1);
+ } else if (cflag) {
+ n = n * sl->sl_ntracks * sl->sl_nsectors;
+ }
+ sl->sl_part[part].sdkp_nsectors = n;
+ return (0);
+}
+
+static int
+parse_offset(struct sun_disklabel *sl, int part, char *offset)
+{
+ uintmax_t nsectors;
+ uintmax_t n;
+ char *p;
+ int i;
+
+ nsectors = 0;
+ n = strtoumax(offset, &p, 10);
+ if (*p != '\0') {
+ if (strcmp(offset, "*") == 0) {
+ for (i = 0; i < part; i++) {
+ if (i == 2)
+ continue;
+ nsectors += sl->sl_part[i].sdkp_nsectors;
+ }
+ n = nsectors / (sl->sl_nsectors * sl->sl_ntracks);
+ } else
+ return (-1);
+ }
+ sl->sl_part[part].sdkp_cyloffset = n;
+ return (0);
+}
+
+static void
+print_label(struct sun_disklabel *sl, const char *disk, FILE *out)
+{
+ int i, j;
+ int havevtoc;
+ uintmax_t secpercyl;
+ /* Long enough to hex-encode each character. */
+ char volname[4 * SUN_VOLNAME_LEN + 1];
+
+ havevtoc = sl->sl_vtoc_sane == SUN_VTOC_SANE;
+ secpercyl = sl->sl_nsectors * sl->sl_ntracks;
+
+ fprintf(out,
+"# /dev/%s:\n"
+"text: %s\n"
+"bytes/sector: %d\n"
+"sectors/cylinder: %ju\n",
+ disk,
+ sl->sl_text,
+ sectorsize,
+ secpercyl);
+ if (eflag)
+ fprintf(out,
+ "# max sectors/unit (including alt cylinders): %ju\n",
+ (uintmax_t)mediasize / sectorsize);
+ fprintf(out,
+"sectors/unit: %ju\n",
+ secpercyl * sl->sl_ncylinders);
+ if (havevtoc && sl->sl_vtoc_volname[0] != '\0') {
+ for (i = j = 0; i < SUN_VOLNAME_LEN; i++) {
+ if (sl->sl_vtoc_volname[i] == '\0')
+ break;
+ if (isprint(sl->sl_vtoc_volname[i]))
+ volname[j++] = sl->sl_vtoc_volname[i];
+ else
+ j += sprintf(volname + j, "\\x%02X",
+ sl->sl_vtoc_volname[i]);
+ }
+ volname[j] = '\0';
+ fprintf(out, "volume name: %s\n", volname);
+ }
+ fprintf(out,
+"\n"
+"%d partitions:\n"
+"#\n",
+ SUN_NPART);
+ if (!hflag) {
+ fprintf(out, "# Size is in %s.", cflag? "cylinders": "sectors");
+ if (eflag)
+ fprintf(out,
+" Use %%d%c, %%dK, %%dM or %%dG to specify in %s,\n"
+"# kilobytes, megabytes or gigabytes respectively, or '*' to specify rest of\n"
+"# disk.\n",
+ cflag? 's': 'c',
+ cflag? "sectors": "cylinders");
+ else
+ putc('\n', out);
+ fprintf(out, "# Offset is in cylinders.");
+ if (eflag)
+ fprintf(out,
+" Use '*' to calculate offsets automatically.\n"
+"#\n");
+ else
+ putc('\n', out);
+ }
+ if (havevtoc)
+ fprintf(out,
+"# size offset tag flag\n"
+"# ---------- ---------- ---------- ----\n"
+ );
+ else
+ fprintf(out,
+"# size offset\n"
+"# ---------- ----------\n"
+ );
+
+ for (i = 0; i < SUN_NPART; i++) {
+ if (sl->sl_part[i].sdkp_nsectors == 0)
+ continue;
+ if (hflag) {
+ fprintf(out, " %c: %10s",
+ 'a' + i,
+ make_h_number((uintmax_t)
+ sl->sl_part[i].sdkp_nsectors * 512));
+ fprintf(out, " %10s",
+ make_h_number((uintmax_t)
+ sl->sl_part[i].sdkp_cyloffset * 512
+ * secpercyl));
+ } else {
+ fprintf(out, " %c: %10ju %10u",
+ 'a' + i,
+ sl->sl_part[i].sdkp_nsectors / (cflag? secpercyl: 1),
+ sl->sl_part[i].sdkp_cyloffset);
+ }
+ if (havevtoc)
+ fprintf(out, " %11s %5s",
+ tagname(sl->sl_vtoc_map[i].svtoc_tag),
+ flagname(sl->sl_vtoc_map[i].svtoc_flag));
+ putc('\n', out);
+ }
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage:"
+"\t%s [-r] [-c | -h] disk\n"
+"\t\t(to read label)\n"
+"\t%s -B [-b boot1] [-n] disk\n"
+"\t\t(to install boot program only)\n"
+"\t%s -R [-B [-b boot1]] [-r] [-n] [-c] disk protofile\n"
+"\t\t(to restore label)\n"
+"\t%s -e [-B [-b boot1]] [-r] [-n] [-c] disk\n"
+"\t\t(to edit label)\n"
+"\t%s -w [-B [-b boot1]] [-r] [-n] disk type\n"
+"\t\t(to write default label)\n",
+ __progname,
+ __progname,
+ __progname,
+ __progname,
+ __progname);
+ exit(1);
+}
+
+/*
+ * Return VTOC tag and flag names for tag or flag ID, resp.
+ */
+static const char *
+tagname(unsigned int tag)
+{
+ static char buf[32];
+ size_t i;
+ struct tags *tp;
+
+ for (i = 0, tp = knowntags;
+ i < sizeof(knowntags) / sizeof(struct tags);
+ i++, tp++)
+ if (tp->id == tag)
+ return (tp->name);
+
+ sprintf(buf, "%u", tag);
+
+ return (buf);
+}
+
+static const char *
+flagname(unsigned int flag)
+{
+ static char buf[32];
+ size_t i;
+ struct tags *tp;
+
+ for (i = 0, tp = knownflags;
+ i < sizeof(knownflags) / sizeof(struct tags);
+ i++, tp++)
+ if (tp->id == flag)
+ return (tp->name);
+
+ sprintf(buf, "%u", flag);
+
+ return (buf);
+}
+
+static unsigned int
+parse_tag(struct sun_disklabel *sl, int part, const char *tag)
+{
+ struct tags *tp;
+ char *endp;
+ size_t i;
+ unsigned long l;
+
+ for (i = 0, tp = knowntags;
+ i < sizeof(knowntags) / sizeof(struct tags);
+ i++, tp++)
+ if (strcmp(tp->name, tag) == 0) {
+ sl->sl_vtoc_map[part].svtoc_tag = (uint16_t)tp->id;
+ return (0);
+ }
+
+ l = strtoul(tag, &endp, 0);
+ if (*tag != '\0' && *endp == '\0') {
+ sl->sl_vtoc_map[part].svtoc_tag = (uint16_t)l;
+ return (0);
+ }
+
+ return (-1);
+}
+
+static unsigned int
+parse_flag(struct sun_disklabel *sl, int part, const char *flag)
+{
+ struct tags *tp;
+ char *endp;
+ size_t i;
+ unsigned long l;
+
+ for (i = 0, tp = knownflags;
+ i < sizeof(knownflags) / sizeof(struct tags);
+ i++, tp++)
+ if (strcmp(tp->name, flag) == 0) {
+ sl->sl_vtoc_map[part].svtoc_flag = (uint16_t)tp->id;
+ return (0);
+ }
+
+ l = strtoul(flag, &endp, 0);
+ if (*flag != '\0' && *endp == '\0') {
+ sl->sl_vtoc_map[part].svtoc_flag = (uint16_t)l;
+ return (0);
+ }
+
+ return (-1);
+}
+
+/*
+ * Convert argument into `human readable' byte number form.
+ */
+static const char *
+make_h_number(uintmax_t u)
+{
+ static char buf[32];
+ double d;
+
+ if (u == 0) {
+ strcpy(buf, "0B");
+ } else if (u > 2000000000UL) {
+ d = (double)u / 1e9;
+ sprintf(buf, "%.1fG", d);
+ } else if (u > 2000000UL) {
+ d = (double)u / 1e6;
+ sprintf(buf, "%.1fM", d);
+ } else {
+ d = (double)u / 1e3;
+ sprintf(buf, "%.1fK", d);
+ }
+
+ return (buf);
+}
diff --git a/sbin/swapon/Makefile b/sbin/swapon/Makefile
new file mode 100644
index 0000000..85b0f0a
--- /dev/null
+++ b/sbin/swapon/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= swapon
+MAN= swapon.8
+LINKS= ${BINDIR}/swapon ${BINDIR}/swapoff
+LINKS+= ${BINDIR}/swapon ${BINDIR}/swapctl
+MLINKS= swapon.8 swapoff.8
+MLINKS+=swapon.8 swapctl.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/swapon/swapon.8 b/sbin/swapon/swapon.8
new file mode 100644
index 0000000..f4ecd14
--- /dev/null
+++ b/sbin/swapon/swapon.8
@@ -0,0 +1,177 @@
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)swapon.8 8.1 (Berkeley) 6/5/93
+.\" $FreeBSD$
+.\"
+.Dd December 28, 2002
+.Dt SWAPON 8
+.Os
+.Sh NAME
+.Nm swapon , swapoff , swapctl
+.Nd "specify devices for paging and swapping"
+.Sh SYNOPSIS
+.Nm swapon Fl a | Ar
+.Nm swapoff Fl a | Ar
+.Nm swapctl
+.Op Fl AhklsU
+.Oo
+.Fl a Ar
+|
+.Fl d Ar
+.Oc
+.Sh DESCRIPTION
+The
+.Nm swapon , swapoff
+and
+.Nm swapctl
+utilities are used to control swap devices in the system.
+At boot time all swap entries in
+.Pa /etc/fstab
+are added automatically when the system goes multi-user.
+Swap devices use a fixed interleave; the maximum number of devices
+is specified by the kernel configuration option
+.Dv NSWAPDEV ,
+which is typically set to 4.
+There is no priority mechanism.
+.Pp
+The
+.Nm swapon
+utility adds the specified swap devices to the system.
+If the
+.Fl a
+option is used, all swap devices in
+.Pa /etc/fstab
+will be added, unless their
+.Dq noauto
+option is also set.
+.Pp
+The
+.Nm swapoff
+utility removes the specified swap devices from the system.
+If the
+.Fl a
+option is used, all swap devices in
+.Pa /etc/fstab
+will be removed, unless their
+.Dq noauto
+option is also set.
+Note that
+.Nm swapoff
+will fail and refuse to remove a swap device if there is insufficient
+VM (memory + remaining swap devices) to run the system.
+The
+.Nm swapoff
+utility
+must move swapped pages out of the device being removed which could
+lead to high system loads for a period of time, depending on how
+much data has been swapped out to that device.
+.Pp
+The
+.Nm swapctl
+utility exists primarily for those familiar with other
+.Bx Ns s
+and may be
+used to add, remove, or list swap devices.
+Note that the
+.Fl a
+option is used differently in
+.Nm swapctl
+and indicates that a specific list of devices should be added.
+The
+.Fl d
+option indicates that a specific list should be removed.
+The
+.Fl A
+and
+.Fl U
+options to
+.Nm swapctl
+operate on all swap entries in
+.Pa /etc/fstab
+which do not have their
+.Dq noauto
+option set.
+.Pp
+Swap information can be generated using the
+.Xr swapinfo 8
+utility,
+.Nm pstat
+.Fl s ,
+or
+.Nm swapctl
+.Fl l .
+The
+.Nm swapctl
+utility has the following options for listing swap:
+.Bl -tag -width indent
+.It Fl h
+Output values in megabytes.
+.It Fl k
+Output values in kilobytes.
+.It Fl l
+List the devices making up system swap.
+.It Fl s
+Print a summary line for system swap.
+.Pp
+The
+.Ev BLOCKSIZE
+environment variable is used if not specifically
+overridden.
+512 byte blocks are used by default.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /dev/{ad,da}?s?b" -compact
+.It Pa /dev/{ad,da}?s?b
+standard paging devices
+.It Pa /dev/md?
+memory disk devices
+.It Pa /etc/fstab
+.Tn ASCII
+file system description table
+.El
+.Sh DIAGNOSTICS
+These utilities may fail for the reasons described in
+.Xr swapon 2 .
+.Sh SEE ALSO
+.Xr swapon 2 ,
+.Xr fstab 5 ,
+.Xr init 8 ,
+.Xr mdconfig 8 ,
+.Xr pstat 8 ,
+.Xr rc 8
+.Sh HISTORY
+The
+.Nm swapon
+utility appeared in
+.Bx 4.0 .
+The
+.Nm swapoff
+and
+.Nm swapctl
+utilities appeared in
+.Fx 5.1 .
diff --git a/sbin/swapon/swapon.c b/sbin/swapon/swapon.c
new file mode 100644
index 0000000..13297f9
--- /dev/null
+++ b/sbin/swapon/swapon.c
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)swapon.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <vm/vm_param.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fstab.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+static void usage(void);
+static int swap_on_off(char *name, int ignoreebusy);
+static void swaplist(int, int, int);
+
+enum { SWAPON, SWAPOFF, SWAPCTL } orig_prog, which_prog = SWAPCTL;
+
+int
+main(int argc, char **argv)
+{
+ struct fstab *fsp;
+ char *ptr;
+ int stat;
+ int ch, doall;
+ int sflag = 0, lflag = 0, hflag = 0;
+
+ if ((ptr = strrchr(argv[0], '/')) == NULL)
+ ptr = argv[0];
+ if (strstr(ptr, "swapon"))
+ which_prog = SWAPON;
+ else if (strstr(ptr, "swapoff"))
+ which_prog = SWAPOFF;
+ orig_prog = which_prog;
+
+ doall = 0;
+ while ((ch = getopt(argc, argv, "AadlhksU")) != -1) {
+ switch(ch) {
+ case 'A':
+ if (which_prog == SWAPCTL) {
+ doall = 1;
+ which_prog = SWAPON;
+ } else {
+ usage();
+ }
+ break;
+ case 'a':
+ if (which_prog == SWAPON || which_prog == SWAPOFF)
+ doall = 1;
+ else
+ which_prog = SWAPON;
+ break;
+ case 'd':
+ if (which_prog == SWAPCTL)
+ which_prog = SWAPOFF;
+ else
+ usage();
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'l':
+ lflag = 1;
+ break;
+ case 'h':
+ hflag = 'M';
+ break;
+ case 'k':
+ hflag = 'K';
+ break;
+ case 'U':
+ if (which_prog == SWAPCTL) {
+ doall = 1;
+ which_prog = SWAPOFF;
+ } else {
+ usage();
+ }
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argv += optind;
+
+ stat = 0;
+ if (which_prog == SWAPON || which_prog == SWAPOFF) {
+ if (doall) {
+ while ((fsp = getfsent()) != NULL) {
+ if (strcmp(fsp->fs_type, FSTAB_SW))
+ continue;
+ if (strstr(fsp->fs_mntops, "noauto"))
+ continue;
+ if (swap_on_off(fsp->fs_spec, 1)) {
+ stat = 1;
+ } else {
+ printf("%s: %sing %s as swap device\n",
+ getprogname(), which_prog == SWAPOFF ? "remov" : "add",
+ fsp->fs_spec);
+ }
+ }
+ }
+ else if (!*argv)
+ usage();
+ for (; *argv; ++argv) {
+ if (swap_on_off(*argv, 0)) {
+ stat = 1;
+ } else if (orig_prog == SWAPCTL) {
+ printf("%s: %sing %s as swap device\n",
+ getprogname(), which_prog == SWAPOFF ? "remov" : "add",
+ *argv);
+ }
+ }
+ } else {
+ if (lflag || sflag)
+ swaplist(lflag, sflag, hflag);
+ else
+ usage();
+ }
+ exit(stat);
+}
+
+static int
+swap_on_off(char *name, int doingall)
+{
+ if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
+ switch (errno) {
+ case EBUSY:
+ if (!doingall)
+ warnx("%s: device already in use", name);
+ break;
+ case EINVAL:
+ if (which_prog == SWAPON)
+ warnx("%s: NSWAPDEV limit reached", name);
+ else if (!doingall)
+ warn("%s", name);
+ break;
+ default:
+ warn("%s", name);
+ break;
+ }
+ return(1);
+ }
+ return(0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s ", getprogname());
+ switch(orig_prog) {
+ case SWAPON:
+ case SWAPOFF:
+ fprintf(stderr, "-a | file ...\n");
+ break;
+ case SWAPCTL:
+ fprintf(stderr, "[-AhklsU] [-a file ... | -d file ...]\n");
+ break;
+ }
+ exit(1);
+}
+
+static void
+swaplist(int lflag, int sflag, int hflag)
+{
+ size_t mibsize, size;
+ struct xswdev xsw;
+ int hlen, mib[16], n, pagesize;
+ long blocksize;
+ long long total = 0;
+ long long used = 0;
+ long long tmp_total;
+ long long tmp_used;
+
+ pagesize = getpagesize();
+ switch(hflag) {
+ case 'K':
+ blocksize = 1024;
+ hlen = 10;
+ break;
+ case 'M':
+ blocksize = 1024 * 1024;
+ hlen = 10;
+ break;
+ default:
+ getbsize(&hlen, &blocksize);
+ break;
+ }
+
+ mibsize = sizeof mib / sizeof mib[0];
+ if (sysctlnametomib("vm.swap_info", mib, &mibsize) == -1)
+ err(1, "sysctlnametomib()");
+
+ if (lflag) {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%ld-blocks", blocksize);
+ printf("%-13s %*s %*s\n",
+ "Device:",
+ hlen, buf,
+ hlen, "Used:");
+ }
+
+ for (n = 0; ; ++n) {
+ mib[mibsize] = n;
+ size = sizeof xsw;
+ if (sysctl(mib, mibsize + 1, &xsw, &size, NULL, 0) == -1)
+ break;
+ if (xsw.xsw_version != XSWDEV_VERSION)
+ errx(1, "xswdev version mismatch");
+
+ tmp_total = (long long)xsw.xsw_nblks * pagesize / blocksize;
+ tmp_used = (long long)xsw.xsw_used * pagesize / blocksize;
+ total += tmp_total;
+ used += tmp_used;
+ if (lflag) {
+ printf("/dev/%-8s %*lld %*lld\n",
+ devname(xsw.xsw_dev, S_IFCHR),
+ hlen, tmp_total,
+ hlen, tmp_used);
+ }
+ }
+ if (errno != ENOENT)
+ err(1, "sysctl()");
+
+ if (sflag) {
+ printf("Total: %*lld %*lld\n",
+ hlen, total,
+ hlen, used);
+ }
+}
+
diff --git a/sbin/sysctl/Makefile b/sbin/sysctl/Makefile
new file mode 100644
index 0000000..381f199
--- /dev/null
+++ b/sbin/sysctl/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= sysctl
+WARNS?= 0
+MAN= sysctl.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/sysctl/sysctl.8 b/sbin/sysctl/sysctl.8
new file mode 100644
index 0000000..b87061c
--- /dev/null
+++ b/sbin/sysctl/sysctl.8
@@ -0,0 +1,316 @@
+.\" 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.
+.\" 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 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: @(#)sysctl.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd September 15, 2005
+.Dt SYSCTL 8
+.Os
+.Sh NAME
+.Nm sysctl
+.Nd get or set kernel state
+.Sh SYNOPSIS
+.Nm
+.Op Fl bdehNnoqx
+.Ar name Ns Op = Ns Ar value
+.Ar ...
+.Nm
+.Op Fl bdehNnoqx
+.Fl a
+.Sh DESCRIPTION
+The
+.Nm
+utility retrieves kernel state and allows processes with appropriate
+privilege to set kernel state.
+The state to be retrieved or set is described using a
+.Dq Management Information Base
+.Pq Dq MIB
+style name, described as a dotted set of
+components.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl A
+Equivalent to
+.Fl o a
+(for compatibility).
+.It Fl a
+List all the currently available non-opaque values.
+This option is ignored if one or more variable names are specified on
+the command line.
+.It Fl b
+Force the value of the variable(s) to be output in raw, binary format.
+No names are printed and no terminating newlines are output.
+This is mostly useful with a single variable.
+.It Fl d
+Print the description of the variable instead of its value.
+.It Fl e
+Separate the name and the value of the variable(s) with
+.Ql = .
+This is useful for producing output which can be fed back to the
+.Nm
+utility.
+This option is ignored if either
+.Fl N
+or
+.Fl n
+is specified, or a variable is being set.
+.It Fl h
+Format output for human, rather than machine, readability.
+.It Fl N
+Show only variable names, not their values.
+This is particularly useful with shells that offer programmable
+completion.
+To enable completion of variable names in
+.Xr zsh 1 Pq Pa ports/shells/zsh ,
+use the following code:
+.Bd -literal -offset indent
+listsysctls () { set -A reply $(sysctl -AN ${1%.*}) }
+compctl -K listsysctls sysctl
+.Ed
+.Pp
+To enable completion of variable names in
+.Xr tcsh 1 ,
+use:
+.Pp
+.Dl "complete sysctl 'n/*/`sysctl -Na`/'"
+.It Fl n
+Show only variable values, not their names.
+This option is useful for setting shell variables.
+For instance, to save the pagesize in variable
+.Va psize ,
+use:
+.Pp
+.Dl "set psize=`sysctl -n hw.pagesize`"
+.It Fl o
+Show opaque variables (which are normally suppressed).
+The format and length are printed, as well as a hex dump of the first
+sixteen bytes of the value.
+.It Fl q
+Suppress some warnings generated by
+.Nm
+to standard error.
+.It Fl X
+Equivalent to
+.Fl x a
+(for compatibility).
+.It Fl x
+As
+.Fl o ,
+but prints a hex dump of the entire value instead of just the first
+few bytes.
+.El
+.Pp
+The information available from
+.Nm
+consists of integers, strings, devices
+.Pq Vt dev_t ,
+and opaque types.
+The
+.Nm
+utility
+only knows about a couple of opaque types, and will resort to hexdumps
+for the rest.
+The opaque information is much more useful if retrieved by special
+purpose programs such as
+.Xr ps 1 ,
+.Xr systat 1 ,
+and
+.Xr netstat 1 .
+.Pp
+Some of the variables which cannot be modified during normal system
+operation can be initialized via
+.Xr loader 8
+tunables.
+This can for example be done by setting them in
+.Xr loader.conf 5 .
+Please refer to
+.Xr loader.conf 5
+for more information on which tunables are available and how to set them.
+.Pp
+The string and integer information is summarized below.
+For a detailed description of these variable see
+.Xr sysctl 3 .
+.Pp
+The changeable column indicates whether a process with appropriate
+privilege can change the value.
+String, integer, and devices values can be set using
+.Nm .
+For device values,
+.Ar value
+can be specified as a character device special file name.
+Special values
+.Cm off
+and
+.Cm none
+denote
+.Dq no device .
+.Bl -column security.bsd.unprivileged_read_msgbuf integerxxx
+.It Sy "Name Type Changeable
+.It "kern.ostype string no
+.It "kern.osrelease string no
+.It "kern.osrevision integer no
+.It "kern.version string no
+.It "kern.maxvnodes integer yes
+.It "kern.maxproc integer no
+.It "kern.maxprocperuid integer yes
+.It "kern.maxfiles integer yes
+.It "kern.maxfilesperproc integer yes
+.It "kern.argmax integer no
+.It "kern.securelevel integer raise only
+.It "kern.hostname string yes
+.It "kern.hostid integer yes
+.It "kern.clockrate struct no
+.It "kern.posix1version integer no
+.It "kern.ngroups integer no
+.It "kern.job_control integer no
+.It "kern.saved_ids integer no
+.It "kern.boottime struct no
+.It "kern.domainname string yes
+.It "kern.filedelay integer yes
+.It "kern.dirdelay integer yes
+.It "kern.metadelay integer yes
+.It "kern.osreldate string no
+.It "kern.bootfile string yes
+.It "kern.corefile string yes
+.It "kern.dumpdev dev_t yes
+.It "kern.logsigexit integer yes
+.It "security.bsd.suser_enabled integer yes
+.It "security.bsd.see_other_uids integer yes
+.It "security.bsd.unprivileged_proc_debug integer yes
+.It "security.bsd.unprivileged_read_msgbuf integer yes
+.It "vm.loadavg struct no
+.It "hw.machine string no
+.It "hw.model string no
+.It "hw.ncpu integer no
+.It "hw.byteorder integer no
+.It "hw.physmem integer no
+.It "hw.usermem integer no
+.It "hw.pagesize integer no
+.It "hw.floatingpoint integer no
+.It "hw.machine_arch string no
+.It "machdep.console_device dev_t no
+.It "machdep.adjkerntz integer yes
+.It "machdep.disable_rtc_set integer yes
+.It "machdep.guessed_bootdev string no
+.It "user.cs_path string no
+.It "user.bc_base_max integer no
+.It "user.bc_dim_max integer no
+.It "user.bc_scale_max integer no
+.It "user.bc_string_max integer no
+.It "user.coll_weights_max integer no
+.It "user.expr_nest_max integer no
+.It "user.line_max integer no
+.It "user.re_dup_max integer no
+.It "user.posix2_version integer no
+.It "user.posix2_c_bind integer no
+.It "user.posix2_c_dev integer no
+.It "user.posix2_char_term integer no
+.It "user.posix2_fort_dev integer no
+.It "user.posix2_fort_run integer no
+.It "user.posix2_localedef integer no
+.It "user.posix2_sw_dev integer no
+.It "user.posix2_upe integer no
+.It "user.stream_max integer no
+.It "user.tzname_max integer no
+.El
+.Sh FILES
+.Bl -tag -width ".In netinet/icmp_var.h" -compact
+.It In sys/sysctl.h
+definitions for top level identifiers, second level kernel and hardware
+identifiers, and user level identifiers
+.It In sys/socket.h
+definitions for second level network identifiers
+.It In sys/gmon.h
+definitions for third level profiling identifiers
+.It In vm/vm_param.h
+definitions for second level virtual memory identifiers
+.It In netinet/in.h
+definitions for third level Internet identifiers and
+fourth level IP identifiers
+.It In netinet/icmp_var.h
+definitions for fourth level ICMP identifiers
+.It In netinet/udp_var.h
+definitions for fourth level UDP identifiers
+.El
+.Sh EXAMPLES
+For example, to retrieve the maximum number of processes allowed
+in the system, one would use the following request:
+.Pp
+.Dl "sysctl kern.maxproc"
+.Pp
+To set the maximum number of processes allowed
+per uid to 1000, one would use the following request:
+.Pp
+.Dl "sysctl kern.maxprocperuid=1000"
+.Pp
+The device used for crash dumps can be specified using:
+.Pp
+.Dl "sysctl kern.dumpdev=/dev/somedev"
+.Pp
+which is equivalent to
+.Pp
+.Dl "dumpon /dev/somedev"
+.Pp
+Information about the system clock rate may be obtained with:
+.Pp
+.Dl "sysctl kern.clockrate"
+.Pp
+Information about the load average history may be obtained with:
+.Pp
+.Dl "sysctl vm.loadavg"
+.Pp
+More variables than these exist, and the best and likely only place
+to search for their deeper meaning is undoubtedly the source where
+they are defined.
+.Sh COMPATIBILITY
+The
+.Fl w
+option has been deprecated and is silently ignored.
+.Sh SEE ALSO
+.Xr sysctl 3 ,
+.Xr loader.conf 5 ,
+.Xr sysctl.conf 5 ,
+.Xr loader 8
+.Sh HISTORY
+A
+.Nm
+utility first appeared in
+.Bx 4.4 .
+.Pp
+In
+.Fx 2.2 ,
+.Nm
+was significantly remodeled.
+.Sh BUGS
+The
+.Nm
+utility presently exploits an undocumented interface to the kernel
+sysctl facility to traverse the sysctl tree and to retrieve format
+and name information.
+This correct interface is being thought about for the time being.
diff --git a/sbin/sysctl/sysctl.c b/sbin/sysctl/sysctl.c
new file mode 100644
index 0000000..aaffc19
--- /dev/null
+++ b/sbin/sysctl/sysctl.c
@@ -0,0 +1,702 @@
+/*
+ * 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)from: sysctl.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#ifdef __i386__
+#include <sys/reboot.h> /* used for bootdev parsing */
+#endif
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/vmmeter.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static int aflag, bflag, dflag, eflag, hflag, Nflag, nflag, oflag;
+static int qflag, xflag;
+
+static int oidfmt(int *, int, char *, u_int *);
+static void parse(char *);
+static int show_var(int *, int);
+static int sysctl_all (int *oid, int len);
+static int name2oid(char *, int *);
+
+static void set_T_dev_t (char *, void **, size_t *);
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr, "%s\n%s\n",
+ "usage: sysctl [-bdehNnoqx] name[=value] ...",
+ " sysctl [-bdehNnoqx] -a");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+
+ setlocale(LC_NUMERIC, "");
+ setbuf(stdout,0);
+ setbuf(stderr,0);
+
+ while ((ch = getopt(argc, argv, "AabdehNnoqwxX")) != -1) {
+ switch (ch) {
+ case 'A':
+ /* compatibility */
+ aflag = oflag = 1;
+ break;
+ case 'a':
+ aflag = 1;
+ break;
+ case 'b':
+ bflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ break;
+ case 'h':
+ hflag = 1;
+ break;
+ case 'N':
+ Nflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'o':
+ oflag = 1;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'w':
+ /* compatibility */
+ /* ignored */
+ break;
+ case 'X':
+ /* compatibility */
+ aflag = xflag = 1;
+ break;
+ case 'x':
+ xflag = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (Nflag && nflag)
+ usage();
+ if (aflag && argc == 0)
+ exit(sysctl_all(0, 0));
+ if (argc == 0)
+ usage();
+ while (argc-- > 0)
+ parse(*argv++);
+ exit(0);
+}
+
+/*
+ * Parse a name into a MIB entry.
+ * Lookup and print out the MIB entry if it exists.
+ * Set a new value if requested.
+ */
+static void
+parse(char *string)
+{
+ int len, i, j;
+ void *newval = 0;
+ int intval;
+ unsigned int uintval;
+ long longval;
+ unsigned long ulongval;
+ size_t newsize = 0;
+ quad_t quadval;
+ int mib[CTL_MAXNAME];
+ char *cp, *bufp, buf[BUFSIZ], *endptr, fmt[BUFSIZ];
+ u_int kind;
+
+ bufp = buf;
+ if (snprintf(buf, BUFSIZ, "%s", string) >= BUFSIZ)
+ errx(1, "oid too long: '%s'", string);
+ if ((cp = strchr(string, '=')) != NULL) {
+ *strchr(buf, '=') = '\0';
+ *cp++ = '\0';
+ while (isspace(*cp))
+ cp++;
+ newval = cp;
+ newsize = strlen(cp);
+ }
+ len = name2oid(bufp, mib);
+
+ if (len < 0) {
+ if (qflag)
+ exit(1);
+ else
+ errx(1, "unknown oid '%s'", bufp);
+ }
+
+ if (oidfmt(mib, len, fmt, &kind))
+ err(1, "couldn't find format of oid '%s'", bufp);
+
+ if (newval == NULL) {
+ if ((kind & CTLTYPE) == CTLTYPE_NODE) {
+ if (dflag) {
+ i = show_var(mib, len);
+ if (!i && !bflag)
+ putchar('\n');
+ }
+ sysctl_all(mib, len);
+ } else {
+ i = show_var(mib, len);
+ if (!i && !bflag)
+ putchar('\n');
+ }
+ } else {
+ if ((kind & CTLTYPE) == CTLTYPE_NODE)
+ errx(1, "oid '%s' isn't a leaf node", bufp);
+
+ if (!(kind & CTLFLAG_WR)) {
+ if (kind & CTLFLAG_TUN) {
+ warnx("oid '%s' is a read only tunable", bufp);
+ errx(1, "Tunable values are set in /boot/loader.conf");
+ } else {
+ errx(1, "oid '%s' is read only", bufp);
+ }
+ }
+
+ if ((kind & CTLTYPE) == CTLTYPE_INT ||
+ (kind & CTLTYPE) == CTLTYPE_UINT ||
+ (kind & CTLTYPE) == CTLTYPE_LONG ||
+ (kind & CTLTYPE) == CTLTYPE_ULONG ||
+ (kind & CTLTYPE) == CTLTYPE_QUAD) {
+ if (strlen(newval) == 0)
+ errx(1, "empty numeric value");
+ }
+
+ switch (kind & CTLTYPE) {
+ case CTLTYPE_INT:
+ intval = (int)strtol(newval, &endptr, 0);
+ if (endptr == newval || *endptr != '\0')
+ errx(1, "invalid integer '%s'",
+ newval);
+ newval = &intval;
+ newsize = sizeof(intval);
+ break;
+ case CTLTYPE_UINT:
+ uintval = (int) strtoul(newval, &endptr, 0);
+ if (endptr == newval || *endptr != '\0')
+ errx(1, "invalid unsigned integer '%s'",
+ newval);
+ newval = &uintval;
+ newsize = sizeof uintval;
+ break;
+ case CTLTYPE_LONG:
+ longval = strtol(newval, &endptr, 0);
+ if (endptr == newval || *endptr != '\0')
+ errx(1, "invalid long integer '%s'",
+ newval);
+ newval = &longval;
+ newsize = sizeof longval;
+ break;
+ case CTLTYPE_ULONG:
+ ulongval = strtoul(newval, &endptr, 0);
+ if (endptr == newval || *endptr != '\0')
+ errx(1, "invalid unsigned long integer"
+ " '%s'", newval);
+ newval = &ulongval;
+ newsize = sizeof ulongval;
+ break;
+ case CTLTYPE_STRING:
+ break;
+ case CTLTYPE_QUAD:
+ sscanf(newval, "%qd", &quadval);
+ newval = &quadval;
+ newsize = sizeof(quadval);
+ break;
+ case CTLTYPE_OPAQUE:
+ if (strcmp(fmt, "T,dev_t") == 0) {
+ set_T_dev_t ((char*)newval, &newval, &newsize);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ errx(1, "oid '%s' is type %d,"
+ " cannot set that", bufp,
+ kind & CTLTYPE);
+ }
+
+ i = show_var(mib, len);
+ if (sysctl(mib, len, 0, 0, newval, newsize) == -1) {
+ if (!i && !bflag)
+ putchar('\n');
+ switch (errno) {
+ case EOPNOTSUPP:
+ errx(1, "%s: value is not available",
+ string);
+ case ENOTDIR:
+ errx(1, "%s: specification is incomplete",
+ string);
+ case ENOMEM:
+ errx(1, "%s: type is unknown to this program",
+ string);
+ default:
+ warn("%s", string);
+ return;
+ }
+ }
+ if (!bflag)
+ printf(" -> ");
+ i = nflag;
+ nflag = 1;
+ j = show_var(mib, len);
+ if (!j && !bflag)
+ putchar('\n');
+ nflag = i;
+ }
+}
+
+/* These functions will dump out various interesting structures. */
+
+static int
+S_clockinfo(int l2, void *p)
+{
+ struct clockinfo *ci = (struct clockinfo*)p;
+ if (l2 != sizeof(*ci)) {
+ warnx("S_clockinfo %d != %d", l2, sizeof(*ci));
+ return (0);
+ }
+ printf(hflag ? "{ hz = %'d, tick = %'d, profhz = %'d, stathz = %'d }" :
+ "{ hz = %d, tick = %d, profhz = %d, stathz = %d }",
+ ci->hz, ci->tick, ci->profhz, ci->stathz);
+ return (0);
+}
+
+static int
+S_loadavg(int l2, void *p)
+{
+ struct loadavg *tv = (struct loadavg*)p;
+
+ if (l2 != sizeof(*tv)) {
+ warnx("S_loadavg %d != %d", l2, sizeof(*tv));
+ return (0);
+ }
+ printf(hflag ? "{ %'.2f %'.2f %'.2f }" : "{ %.2f %.2f %.2f }",
+ (double)tv->ldavg[0]/(double)tv->fscale,
+ (double)tv->ldavg[1]/(double)tv->fscale,
+ (double)tv->ldavg[2]/(double)tv->fscale);
+ return (0);
+}
+
+static int
+S_timeval(int l2, void *p)
+{
+ struct timeval *tv = (struct timeval*)p;
+ time_t tv_sec;
+ char *p1, *p2;
+
+ if (l2 != sizeof(*tv)) {
+ warnx("S_timeval %d != %d", l2, sizeof(*tv));
+ return (0);
+ }
+ printf(hflag ? "{ sec = %'ld, usec = %'ld } " :
+ "{ sec = %ld, usec = %ld } ",
+ tv->tv_sec, tv->tv_usec);
+ tv_sec = tv->tv_sec;
+ p1 = strdup(ctime(&tv_sec));
+ for (p2=p1; *p2 ; p2++)
+ if (*p2 == '\n')
+ *p2 = '\0';
+ fputs(p1, stdout);
+ return (0);
+}
+
+static int
+S_vmtotal(int l2, void *p)
+{
+ struct vmtotal *v = (struct vmtotal *)p;
+ int pageKilo = getpagesize() / 1024;
+
+ if (l2 != sizeof(*v)) {
+ warnx("S_vmtotal %d != %d", l2, sizeof(*v));
+ return (0);
+ }
+
+ printf(
+ "\nSystem wide totals computed every five seconds:"
+ " (values in kilobytes)\n");
+ printf("===============================================\n");
+ printf(
+ "Processes:\t\t(RUNQ: %hu Disk Wait: %hu Page Wait: "
+ "%hu Sleep: %hu)\n",
+ v->t_rq, v->t_dw, v->t_pw, v->t_sl);
+ printf(
+ "Virtual Memory:\t\t(Total: %luK, Active %lldK)\n",
+ (unsigned long)v->t_vm / 1024,
+ (long long)v->t_avm * pageKilo);
+ printf("Real Memory:\t\t(Total: %lldK Active %lldK)\n",
+ (long long)v->t_rm * pageKilo, (long long)v->t_arm * pageKilo);
+ printf("Shared Virtual Memory:\t(Total: %lldK Active: %lldK)\n",
+ (long long)v->t_vmshr * pageKilo,
+ (long long)v->t_avmshr * pageKilo);
+ printf("Shared Real Memory:\t(Total: %lldK Active: %lldK)\n",
+ (long long)v->t_rmshr * pageKilo,
+ (long long)v->t_armshr * pageKilo);
+ printf("Free Memory Pages:\t%lldK\n", (long long)v->t_free * pageKilo);
+
+ return (0);
+}
+
+static int
+T_dev_t(int l2, void *p)
+{
+ dev_t *d = (dev_t *)p;
+ if (l2 != sizeof(*d)) {
+ warnx("T_dev_T %d != %d", l2, sizeof(*d));
+ return (0);
+ }
+ if ((int)(*d) != -1) {
+ if (minor(*d) > 255 || minor(*d) < 0)
+ printf("{ major = %d, minor = 0x%x }",
+ major(*d), minor(*d));
+ else
+ printf("{ major = %d, minor = %d }",
+ major(*d), minor(*d));
+ }
+ return (0);
+}
+
+static void
+set_T_dev_t (char *path, void **val, size_t *size)
+{
+ static struct stat statb;
+
+ if (strcmp(path, "none") && strcmp(path, "off")) {
+ int rc = stat (path, &statb);
+ if (rc) {
+ err(1, "cannot stat %s", path);
+ }
+
+ if (!S_ISCHR(statb.st_mode)) {
+ errx(1, "must specify a device special file.");
+ }
+ } else {
+ statb.st_rdev = NODEV;
+ }
+ *val = (char*) &statb.st_rdev;
+ *size = sizeof statb.st_rdev;
+}
+
+/*
+ * These functions uses a presently undocumented interface to the kernel
+ * to walk the tree and get the type so it can print the value.
+ * This interface is under work and consideration, and should probably
+ * be killed with a big axe by the first person who can find the time.
+ * (be aware though, that the proper interface isn't as obvious as it
+ * may seem, there are various conflicting requirements.
+ */
+
+static int
+name2oid(char *name, int *oidp)
+{
+ int oid[2];
+ int i;
+ size_t j;
+
+ oid[0] = 0;
+ oid[1] = 3;
+
+ j = CTL_MAXNAME * sizeof(int);
+ i = sysctl(oid, 2, oidp, &j, name, strlen(name));
+ if (i < 0)
+ return i;
+ j /= sizeof(int);
+ return (j);
+}
+
+static int
+oidfmt(int *oid, int len, char *fmt, u_int *kind)
+{
+ int qoid[CTL_MAXNAME+2];
+ u_char buf[BUFSIZ];
+ int i;
+ size_t j;
+
+ qoid[0] = 0;
+ qoid[1] = 4;
+ memcpy(qoid + 2, oid, len * sizeof(int));
+
+ j = sizeof(buf);
+ i = sysctl(qoid, len + 2, buf, &j, 0, 0);
+ if (i)
+ err(1, "sysctl fmt %d %d %d", i, j, errno);
+
+ if (kind)
+ *kind = *(u_int *)buf;
+
+ if (fmt)
+ strcpy(fmt, (char *)(buf + sizeof(u_int)));
+ return 0;
+}
+
+/*
+ * This formats and outputs the value of one variable
+ *
+ * Returns zero if anything was actually output.
+ * Returns one if didn't know what to do with this.
+ * Return minus one if we had errors.
+ */
+
+static int
+show_var(int *oid, int nlen)
+{
+ u_char buf[BUFSIZ], *val, *p;
+ char name[BUFSIZ], *fmt, *sep;
+ int qoid[CTL_MAXNAME+2];
+ int i;
+ size_t j, len;
+ u_int kind;
+ int (*func)(int, void *);
+
+ bzero(buf, BUFSIZ);
+ bzero(name, BUFSIZ);
+ qoid[0] = 0;
+ memcpy(qoid + 2, oid, nlen * sizeof(int));
+
+ qoid[1] = 1;
+ j = sizeof(name);
+ i = sysctl(qoid, nlen + 2, name, &j, 0, 0);
+ if (i || !j)
+ err(1, "sysctl name %d %d %d", i, j, errno);
+
+ if (Nflag) {
+ printf("%s", name);
+ return (0);
+ }
+
+ if (eflag)
+ sep = "=";
+ else
+ sep = ": ";
+
+ if (dflag) { /* just print description */
+ qoid[1] = 5;
+ j = sizeof(buf);
+ i = sysctl(qoid, nlen + 2, buf, &j, 0, 0);
+ if (!nflag)
+ printf("%s%s", name, sep);
+ printf("%s", buf);
+ return (0);
+ }
+ /* find an estimate of how much we need for this var */
+ j = 0;
+ i = sysctl(oid, nlen, 0, &j, 0, 0);
+ j += j; /* we want to be sure :-) */
+
+ val = alloca(j + 1);
+ len = j;
+ i = sysctl(oid, nlen, val, &len, 0, 0);
+ if (i || !len)
+ return (1);
+
+ if (bflag) {
+ fwrite(val, 1, len, stdout);
+ return (0);
+ }
+ val[len] = '\0';
+ fmt = buf;
+ oidfmt(oid, nlen, fmt, &kind);
+ p = val;
+ switch (*fmt) {
+ case 'A':
+ if (!nflag)
+ printf("%s%s", name, sep);
+ printf("%.*s", len, p);
+ return (0);
+
+ case 'I':
+ if (!nflag)
+ printf("%s%s", name, sep);
+ fmt++;
+ val = "";
+ while (len >= sizeof(int)) {
+ fputs(val, stdout);
+ if(*fmt == 'U')
+ printf(hflag ? "%'u" : "%u", *(unsigned int *)p);
+ else if (*fmt == 'K') {
+ if (*(int *)p < 0)
+ printf("%d", *(int *)p);
+ else
+ printf("%d.%dC", (*(int *)p - 2732) / 10, (*(int *)p - 2732) % 10);
+ } else
+ printf(hflag ? "%'d" : "%d", *(int *)p);
+ val = " ";
+ len -= sizeof(int);
+ p += sizeof(int);
+ }
+ return (0);
+
+ case 'L':
+ if (!nflag)
+ printf("%s%s", name, sep);
+ fmt++;
+ val = "";
+ while (len >= sizeof(long)) {
+ fputs(val, stdout);
+ if(*fmt == 'U')
+ printf(hflag ? "%'lu" : "%lu", *(unsigned long *)p);
+ else if (*fmt == 'K') {
+ if (*(long *)p < 0)
+ printf("%ld", *(long *)p);
+ else
+ printf("%ld.%ldC", (*(long *)p - 2732) / 10, (*(long *)p - 2732) % 10);
+ } else
+ printf(hflag ? "%'ld" : "%ld", *(long *)p);
+ val = " ";
+ len -= sizeof(long);
+ p += sizeof(long);
+ }
+ return (0);
+
+ case 'P':
+ if (!nflag)
+ printf("%s%s", name, sep);
+ printf("%p", *(void **)p);
+ return (0);
+
+ case 'T':
+ case 'S':
+ i = 0;
+ if (strcmp(fmt, "S,clockinfo") == 0)
+ func = S_clockinfo;
+ else if (strcmp(fmt, "S,timeval") == 0)
+ func = S_timeval;
+ else if (strcmp(fmt, "S,loadavg") == 0)
+ func = S_loadavg;
+ else if (strcmp(fmt, "S,vmtotal") == 0)
+ func = S_vmtotal;
+ else if (strcmp(fmt, "T,dev_t") == 0)
+ func = T_dev_t;
+ else
+ func = NULL;
+ if (func) {
+ if (!nflag)
+ printf("%s%s", name, sep);
+ return ((*func)(len, p));
+ }
+ /* FALLTHROUGH */
+ default:
+ if (!oflag && !xflag)
+ return (1);
+ if (!nflag)
+ printf("%s%s", name, sep);
+ printf("Format:%s Length:%d Dump:0x", fmt, len);
+ while (len-- && (xflag || p < val + 16))
+ printf("%02x", *p++);
+ if (!xflag && len > 16)
+ printf("...");
+ return (0);
+ }
+ return (1);
+}
+
+static int
+sysctl_all (int *oid, int len)
+{
+ int name1[22], name2[22];
+ int i, j;
+ size_t l1, l2;
+
+ name1[0] = 0;
+ name1[1] = 2;
+ l1 = 2;
+ if (len) {
+ memcpy(name1+2, oid, len * sizeof(int));
+ l1 += len;
+ } else {
+ name1[2] = 1;
+ l1++;
+ }
+ for (;;) {
+ l2 = sizeof(name2);
+ j = sysctl(name1, l1, name2, &l2, 0, 0);
+ if (j < 0) {
+ if (errno == ENOENT)
+ return 0;
+ else
+ err(1, "sysctl(getnext) %d %d", j, l2);
+ }
+
+ l2 /= sizeof(int);
+
+ if (l2 < len)
+ return 0;
+
+ for (i = 0; i < len; i++)
+ if (name2[i] != oid[i])
+ return 0;
+
+ i = show_var(name2, l2);
+ if (!i && !bflag)
+ putchar('\n');
+
+ memcpy(name1+2, name2, l2 * sizeof(int));
+ l1 = 2 + l2;
+ }
+}
diff --git a/sbin/tunefs/Makefile b/sbin/tunefs/Makefile
new file mode 100644
index 0000000..d501d10
--- /dev/null
+++ b/sbin/tunefs/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= tunefs
+DPADD= ${LIBUFS}
+LDADD= -lufs
+MAN= tunefs.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/tunefs/tunefs.8 b/sbin/tunefs/tunefs.8
new file mode 100644
index 0000000..ac8641c
--- /dev/null
+++ b/sbin/tunefs/tunefs.8
@@ -0,0 +1,168 @@
+.\" 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.
+.\" 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 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.
+.\"
+.\" @(#)tunefs.8 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.Dd May 18, 2002
+.Dt TUNEFS 8
+.Os
+.Sh NAME
+.Nm tunefs
+.Nd tune up an existing file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl A
+.Op Fl a Cm enable | disable
+.Op Fl e Ar maxbpg
+.Op Fl f Ar avgfilesize
+.Op Fl L Ar volname
+.Op Fl l Cm enable | disable
+.Op Fl m Ar minfree
+.Op Fl n Cm enable | disable
+.Op Fl o Cm space | time
+.Op Fl p
+.Op Fl s Ar avgfpdir
+.Ar special | filesystem
+.Sh DESCRIPTION
+The
+.Nm
+utility is designed to change the dynamic parameters of a file system
+which affect the layout policies.
+The
+.Nm
+utility cannot be run on an active file system.
+To change an active file system,
+it must be downgraded to read-only or unmounted.
+.Pp
+The parameters which are to be changed are indicated by the flags
+given below:
+.Bl -tag -width indent
+.It Fl A
+The file system has several backups of the super-block.
+Specifying
+this option will cause all backups to be modified as well as the
+primary super-block.
+This is potentially dangerous - use with caution.
+.It Fl a Cm enable | disable
+Turn on/off the administrative ACL enable flag.
+.It Fl e Ar maxbpg
+Indicate the maximum number of blocks any single file can
+allocate out of a cylinder group before it is forced to begin
+allocating blocks from another cylinder group.
+Typically this value is set to about one quarter of the total blocks
+in a cylinder group.
+The intent is to prevent any single file from using up all the
+blocks in a single cylinder group,
+thus degrading access times for all files subsequently allocated
+in that cylinder group.
+The effect of this limit is to cause big files to do long seeks
+more frequently than if they were allowed to allocate all the blocks
+in a cylinder group before seeking elsewhere.
+For file systems with exclusively large files,
+this parameter should be set higher.
+.It Fl f Ar avgfilesize
+Specify the expected average file size.
+.It Fl L Ar volname
+Add/modify an optional file system volume label.
+.It Fl l Cm enable | disable
+Turn on/off MAC multilabel flag.
+.It Fl m Ar minfree
+Specify the percentage of space held back
+from normal users; the minimum free space threshold.
+The default value used is 8%.
+Note that lowering the threshold can adversely affect performance:
+.Bl -bullet
+.It
+Settings of 5% and less force space optimization to
+always be used which will greatly increase the overhead for file
+writes.
+.It
+The file system's ability to avoid fragmentation will be reduced
+when the total free space, including the reserve, drops below 15%.
+As free space approaches zero, throughput can degrade by up to a
+factor of three over the performance obtained at a 10% threshold.
+.El
+.Pp
+If the value is raised above the current usage level,
+users will be unable to allocate files until enough files have
+been deleted to get under the higher threshold.
+.It Fl n Cm enable | disable
+Turn on/off soft updates.
+.It Fl o Cm space | time
+The file system can either try to minimize the time spent
+allocating blocks, or it can attempt to minimize the space
+fragmentation on the disk.
+Optimization for space has much
+higher overhead for file writes.
+The kernel normally changes the preference automatically as
+the percent fragmentation changes on the file system.
+.It Fl p
+Show a summary of what the current tunable settings
+are on the selected file system.
+More detailed information can be
+obtained from the
+.Xr dumpfs 8
+utility.
+.It Fl s Ar avgfpdir
+Specify the expected number of files per directory.
+.El
+.Pp
+At least one of the above flags is required.
+.Sh FILES
+.Bl -tag -width ".Pa /etc/fstab"
+.It Pa /etc/fstab
+read this to determine the device file for a
+specified mount point.
+.El
+.Sh SEE ALSO
+.Xr fs 5 ,
+.Xr dumpfs 8 ,
+.Xr newfs 8
+.Rs
+.%A M. McKusick
+.%A W. Joy
+.%A S. Leffler
+.%A R. Fabry
+.%T "A Fast File System for UNIX"
+.%J "ACM Transactions on Computer Systems 2"
+.%N 3
+.%P pp 181-197
+.%D August 1984
+.%O "(reprinted in the BSD System Manager's Manual, SMM:5)"
+.Re
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
+.Sh BUGS
+This utility should work on active file systems.
+.\" Take this out and a Unix Daemon will dog your steps from now until
+.\" the time_t's wrap around.
+.Pp
+You can tune a file system, but you cannot tune a fish.
diff --git a/sbin/tunefs/tunefs.c b/sbin/tunefs/tunefs.c
new file mode 100644
index 0000000..e8ab8d3
--- /dev/null
+++ b/sbin/tunefs/tunefs.c
@@ -0,0 +1,425 @@
+/*
+ * 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.
+ * 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 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tunefs.c 8.2 (Berkeley) 4/19/94";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * tunefs: change layout parameters to an existing file system.
+ */
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/disklabel.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <libufs.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* the optimization warning string template */
+#define OPTWARN "should optimize for %s with minfree %s %d%%"
+
+struct uufsd disk;
+#define sblock disk.d_fs
+
+void usage(void);
+void printfs(void);
+
+int
+main(int argc, char *argv[])
+{
+ char *avalue, *Lvalue, *lvalue, *nvalue;
+ const char *special, *on;
+ const char *name;
+ int active;
+ int Aflag, aflag, eflag, evalue, fflag, fvalue, Lflag, lflag;
+ int mflag, mvalue, nflag, oflag, ovalue, pflag, sflag, svalue;
+ int ch, found_arg, i;
+ const char *chg[2];
+ struct ufs_args args;
+ struct statfs stfs;
+
+ if (argc < 3)
+ usage();
+ Aflag = aflag = eflag = fflag = Lflag = lflag = mflag = 0;
+ nflag = oflag = pflag = sflag = 0;
+ avalue = Lvalue = lvalue = nvalue = NULL;
+ evalue = fvalue = mvalue = ovalue = svalue = 0;
+ active = 0;
+ found_arg = 0; /* At least one arg is required. */
+ while ((ch = getopt(argc, argv, "Aa:e:f:L:l:m:n:o:ps:")) != -1)
+ switch (ch) {
+
+ case 'A':
+ found_arg = 1;
+ Aflag++;
+ break;
+
+ case 'a':
+ found_arg = 1;
+ name = "ACLs";
+ avalue = optarg;
+ if (strcmp(avalue, "enable") &&
+ strcmp(avalue, "disable")) {
+ errx(10, "bad %s (options are %s)",
+ name, "`enable' or `disable'");
+ }
+ aflag = 1;
+ break;
+
+ case 'e':
+ found_arg = 1;
+ name = "maximum blocks per file in a cylinder group";
+ evalue = atoi(optarg);
+ if (evalue < 1)
+ errx(10, "%s must be >= 1 (was %s)",
+ name, optarg);
+ eflag = 1;
+ break;
+
+ case 'f':
+ found_arg = 1;
+ name = "average file size";
+ fvalue = atoi(optarg);
+ if (fvalue < 1)
+ errx(10, "%s must be >= 1 (was %s)",
+ name, optarg);
+ fflag = 1;
+ break;
+
+ case 'L':
+ found_arg = 1;
+ name = "volume label";
+ Lvalue = optarg;
+ i = -1;
+ while (isalnum(Lvalue[++i]));
+ if (Lvalue[i] != '\0') {
+ errx(10,
+ "bad %s. Valid characters are alphanumerics.",
+ name);
+ }
+ if (strlen(Lvalue) >= MAXVOLLEN) {
+ errx(10, "bad %s. Length is longer than %d.",
+ name, MAXVOLLEN - 1);
+ }
+ Lflag = 1;
+ break;
+
+ case 'l':
+ found_arg = 1;
+ name = "multilabel MAC file system";
+ lvalue = optarg;
+ if (strcmp(lvalue, "enable") &&
+ strcmp(lvalue, "disable")) {
+ errx(10, "bad %s (options are %s)",
+ name, "`enable' or `disable'");
+ }
+ lflag = 1;
+ break;
+
+ case 'm':
+ found_arg = 1;
+ name = "minimum percentage of free space";
+ mvalue = atoi(optarg);
+ if (mvalue < 0 || mvalue > 99)
+ errx(10, "bad %s (%s)", name, optarg);
+ mflag = 1;
+ break;
+
+ case 'n':
+ found_arg = 1;
+ name = "soft updates";
+ nvalue = optarg;
+ if (strcmp(nvalue, "enable") != 0 &&
+ strcmp(nvalue, "disable") != 0) {
+ errx(10, "bad %s (options are %s)",
+ name, "`enable' or `disable'");
+ }
+ nflag = 1;
+ break;
+
+ case 'o':
+ found_arg = 1;
+ name = "optimization preference";
+ if (strcmp(optarg, "space") == 0)
+ ovalue = FS_OPTSPACE;
+ else if (strcmp(optarg, "time") == 0)
+ ovalue = FS_OPTTIME;
+ else
+ errx(10,
+ "bad %s (options are `space' or `time')",
+ name);
+ oflag = 1;
+ break;
+
+ case 'p':
+ found_arg = 1;
+ pflag = 1;
+ break;
+
+ case 's':
+ found_arg = 1;
+ name = "expected number of files per directory";
+ svalue = atoi(optarg);
+ if (svalue < 1)
+ errx(10, "%s must be >= 1 (was %s)",
+ name, optarg);
+ sflag = 1;
+ break;
+
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (found_arg == 0 || argc != 1)
+ usage();
+
+ on = special = argv[0];
+ if (ufs_disk_fillout(&disk, special) == -1)
+ goto err;
+ if (disk.d_name != special) {
+ special = disk.d_name;
+ if (statfs(special, &stfs) == 0 &&
+ strcmp(special, stfs.f_mntonname) == 0)
+ active = 1;
+ }
+
+ if (pflag) {
+ printfs();
+ exit(0);
+ }
+ if (Lflag) {
+ name = "volume label";
+ strlcpy(sblock.fs_volname, Lvalue, MAXVOLLEN);
+ }
+ if (aflag) {
+ name = "ACLs";
+ if (strcmp(avalue, "enable") == 0) {
+ if (sblock.fs_flags & FS_ACLS) {
+ warnx("%s remains unchanged as enabled", name);
+ } else {
+ sblock.fs_flags |= FS_ACLS;
+ warnx("%s set", name);
+ }
+ } else if (strcmp(avalue, "disable") == 0) {
+ if ((~sblock.fs_flags & FS_ACLS) ==
+ FS_ACLS) {
+ warnx("%s remains unchanged as disabled",
+ name);
+ } else {
+ sblock.fs_flags &= ~FS_ACLS;
+ warnx("%s cleared", name);
+ }
+ }
+ }
+ if (eflag) {
+ name = "maximum blocks per file in a cylinder group";
+ if (sblock.fs_maxbpg == evalue)
+ warnx("%s remains unchanged as %d", name, evalue);
+ else {
+ warnx("%s changes from %d to %d",
+ name, sblock.fs_maxbpg, evalue);
+ sblock.fs_maxbpg = evalue;
+ }
+ }
+ if (fflag) {
+ name = "average file size";
+ if (sblock.fs_avgfilesize == fvalue) {
+ warnx("%s remains unchanged as %d", name, fvalue);
+ }
+ else {
+ warnx("%s changes from %d to %d",
+ name, sblock.fs_avgfilesize, fvalue);
+ sblock.fs_avgfilesize = fvalue;
+ }
+ }
+ if (lflag) {
+ name = "multilabel";
+ if (strcmp(lvalue, "enable") == 0) {
+ if (sblock.fs_flags & FS_MULTILABEL) {
+ warnx("%s remains unchanged as enabled", name);
+ } else {
+ sblock.fs_flags |= FS_MULTILABEL;
+ warnx("%s set", name);
+ }
+ } else if (strcmp(lvalue, "disable") == 0) {
+ if ((~sblock.fs_flags & FS_MULTILABEL) ==
+ FS_MULTILABEL) {
+ warnx("%s remains unchanged as disabled",
+ name);
+ } else {
+ sblock.fs_flags &= ~FS_MULTILABEL;
+ warnx("%s cleared", name);
+ }
+ }
+ }
+ if (mflag) {
+ name = "minimum percentage of free space";
+ if (sblock.fs_minfree == mvalue)
+ warnx("%s remains unchanged as %d%%", name, mvalue);
+ else {
+ warnx("%s changes from %d%% to %d%%",
+ name, sblock.fs_minfree, mvalue);
+ sblock.fs_minfree = mvalue;
+ if (mvalue >= MINFREE && sblock.fs_optim == FS_OPTSPACE)
+ warnx(OPTWARN, "time", ">=", MINFREE);
+ if (mvalue < MINFREE && sblock.fs_optim == FS_OPTTIME)
+ warnx(OPTWARN, "space", "<", MINFREE);
+ }
+ }
+ if (nflag) {
+ name = "soft updates";
+ if (strcmp(nvalue, "enable") == 0) {
+ if (sblock.fs_flags & FS_DOSOFTDEP)
+ warnx("%s remains unchanged as enabled", name);
+ else if (sblock.fs_clean == 0) {
+ warnx("%s cannot be enabled until fsck is run",
+ name);
+ } else {
+ sblock.fs_flags |= FS_DOSOFTDEP;
+ warnx("%s set", name);
+ }
+ } else if (strcmp(nvalue, "disable") == 0) {
+ if ((~sblock.fs_flags & FS_DOSOFTDEP) == FS_DOSOFTDEP)
+ warnx("%s remains unchanged as disabled", name);
+ else {
+ sblock.fs_flags &= ~FS_DOSOFTDEP;
+ warnx("%s cleared", name);
+ }
+ }
+ }
+ if (oflag) {
+ name = "optimization preference";
+ chg[FS_OPTSPACE] = "space";
+ chg[FS_OPTTIME] = "time";
+ if (sblock.fs_optim == ovalue)
+ warnx("%s remains unchanged as %s", name, chg[ovalue]);
+ else {
+ warnx("%s changes from %s to %s",
+ name, chg[sblock.fs_optim], chg[ovalue]);
+ sblock.fs_optim = ovalue;
+ if (sblock.fs_minfree >= MINFREE &&
+ ovalue == FS_OPTSPACE)
+ warnx(OPTWARN, "time", ">=", MINFREE);
+ if (sblock.fs_minfree < MINFREE && ovalue == FS_OPTTIME)
+ warnx(OPTWARN, "space", "<", MINFREE);
+ }
+ }
+ if (sflag) {
+ name = "expected number of files per directory";
+ if (sblock.fs_avgfpdir == svalue) {
+ warnx("%s remains unchanged as %d", name, svalue);
+ }
+ else {
+ warnx("%s changes from %d to %d",
+ name, sblock.fs_avgfpdir, svalue);
+ sblock.fs_avgfpdir = svalue;
+ }
+ }
+
+ if (sbwrite(&disk, Aflag) == -1)
+ goto err;
+ ufs_disk_close(&disk);
+ if (active) {
+ bzero(&args, sizeof(args));
+ if (mount("ufs", on,
+ stfs.f_flags | MNT_UPDATE | MNT_RELOAD, &args) < 0)
+ err(9, "%s: reload", special);
+ warnx("file system reloaded");
+ }
+ exit(0);
+err:
+ if (disk.d_error != NULL)
+ errx(11, "%s: %s", special, disk.d_error);
+ else
+ err(12, "%s", special);
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+"usage: tunefs [-A] [-a enable | disable] [-e maxbpg] [-f avgfilesize]",
+" [-L volname] [-l enable | disable] [-m minfree]",
+" [-n enable | disable] [-o space | time] [-p]",
+" [-s avgfpdir] special | filesystem");
+ exit(2);
+}
+
+void
+printfs(void)
+{
+ warnx("ACLs: (-a) %s",
+ (sblock.fs_flags & FS_ACLS)? "enabled" : "disabled");
+ warnx("MAC multilabel: (-l) %s",
+ (sblock.fs_flags & FS_MULTILABEL)? "enabled" : "disabled");
+ warnx("soft updates: (-n) %s",
+ (sblock.fs_flags & FS_DOSOFTDEP)? "enabled" : "disabled");
+ warnx("maximum blocks per file in a cylinder group: (-e) %d",
+ sblock.fs_maxbpg);
+ warnx("average file size: (-f) %d",
+ sblock.fs_avgfilesize);
+ warnx("average number of files in a directory: (-s) %d",
+ sblock.fs_avgfpdir);
+ warnx("minimum percentage of free space: (-m) %d%%",
+ sblock.fs_minfree);
+ warnx("optimization preference: (-o) %s",
+ sblock.fs_optim == FS_OPTSPACE ? "space" : "time");
+ if (sblock.fs_minfree >= MINFREE &&
+ sblock.fs_optim == FS_OPTSPACE)
+ warnx(OPTWARN, "time", ">=", MINFREE);
+ if (sblock.fs_minfree < MINFREE &&
+ sblock.fs_optim == FS_OPTTIME)
+ warnx(OPTWARN, "space", "<", MINFREE);
+ warnx("volume label: (-L) %s",
+ sblock.fs_volname);
+}
diff --git a/sbin/umount/Makefile b/sbin/umount/Makefile
new file mode 100644
index 0000000..70ccc5a
--- /dev/null
+++ b/sbin/umount/Makefile
@@ -0,0 +1,16 @@
+# @(#)Makefile 8.4 (Berkeley) 6/22/95
+#
+# $FreeBSD$
+
+PROG= umount
+SRCS= umount.c vfslist.c mounttab.c
+WARNS?= 0
+MAN= umount.8
+
+MOUNT= ${.CURDIR}/../mount
+UMNTALL= ${.CURDIR}/../../usr.sbin/rpc.umntall
+CFLAGS+= -I${MOUNT} -I${UMNTALL}
+
+.PATH: ${MOUNT} ${UMNTALL}
+
+.include <bsd.prog.mk>
diff --git a/sbin/umount/umount.8 b/sbin/umount/umount.8
new file mode 100644
index 0000000..d19ea9a
--- /dev/null
+++ b/sbin/umount/umount.8
@@ -0,0 +1,144 @@
+.\" Copyright (c) 1980, 1989, 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.
+.\" 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 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.
+.\"
+.\" @(#)umount.8 8.2 (Berkeley) 5/8/95
+.\" $FreeBSD$
+.\"
+.Dd July 18, 2003
+.Dt UMOUNT 8
+.Os
+.Sh NAME
+.Nm umount
+.Nd unmount file systems
+.Sh SYNOPSIS
+.Nm
+.Op Fl fv
+.Ar special | node | fsid
+.Nm
+.Fl a | A
+.Op Fl F Ar fstab
+.Op Fl fv
+.Op Fl h Ar host
+.Op Fl t Ar type
+.Sh DESCRIPTION
+The
+.Nm
+utility calls the
+.Xr unmount 2
+system call to remove a file system from the file system tree.
+The file system can be specified by its
+.Ar special
+device or remote node (rhost:path), the path to the mount point
+.Ar node
+or by the file system ID
+.Ar fsid
+as reported by
+.Dq mount -v .
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a
+All the file systems described in
+.Xr fstab 5
+are unmounted.
+.It Fl A
+All the currently mounted file systems except
+the root are unmounted.
+.It Fl F Ar fstab
+Specify the
+.Pa fstab
+file to use.
+.It Fl f
+The file system is forcibly unmounted.
+Active special devices continue to work,
+but all other files return errors if further accesses are attempted.
+The root file system cannot be forcibly unmounted.
+.It Fl h Ar host
+Only file systems mounted from the specified host will be
+unmounted.
+This option implies the
+.Fl A
+option and, unless otherwise specified with the
+.Fl t
+option, will only unmount
+.Tn NFS
+file systems.
+.It Fl t Ar type
+Is used to indicate the actions should only be taken on
+file systems of the specified type.
+More than one type may be specified in a comma separated list.
+The list of file system types can be prefixed with
+.Dq no
+to specify the file system types for which action should
+.Em not
+be taken.
+For example, the
+.Nm
+command:
+.Bd -literal -offset indent
+umount -a -t nfs,nullfs
+.Ed
+.Pp
+unmounts all file systems of the type
+.Tn NFS
+and
+.Tn NULLFS
+that are listed in the
+.Xr fstab 5
+file.
+.It Fl v
+Verbose, additional information is printed out as each file system
+is unmounted.
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width ".Ev PATH_FSTAB"
+.It Ev PATH_FSTAB
+If the environment variable
+.Ev PATH_FSTAB
+is set, all operations are performed against the specified file.
+.Ev PATH_FSTAB
+will not be honored if the process environment or memory address space is
+considered
+.Dq tainted .
+(See
+.Xr issetugid 2
+for more information.)
+.El
+.Sh FILES
+.Bl -tag -width /etc/fstab -compact
+.It Pa /etc/fstab
+file system table
+.El
+.Sh SEE ALSO
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.At v6 .
diff --git a/sbin/umount/umount.c b/sbin/umount/umount.c
new file mode 100644
index 0000000..c00a09d
--- /dev/null
+++ b/sbin/umount/umount.c
@@ -0,0 +1,597 @@
+/*-
+ * Copyright (c) 1980, 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.
+ * 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 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 lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)umount.c 8.8 (Berkeley) 5/8/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <netdb.h>
+#include <rpc/rpc.h>
+#include <nfs/rpcv2.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fstab.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mounttab.h"
+
+typedef enum { FIND, REMOVE, CHECKUNIQUE } dowhat;
+
+struct addrinfo *nfshost_ai = NULL;
+int fflag, vflag;
+char *nfshost;
+
+struct statfs *checkmntlist(char *);
+int checkvfsname (const char *, char **);
+struct statfs *getmntentry(const char *fromname, const char *onname,
+ fsid_t *fsid, dowhat what);
+char **makevfslist (const char *);
+size_t mntinfo (struct statfs **);
+int namematch (struct addrinfo *);
+int parsehexfsid(const char *hex, fsid_t *fsid);
+int sacmp (struct sockaddr *, struct sockaddr *);
+int umountall (char **);
+int checkname (char *, char **);
+int umountfs(struct statfs *sfs);
+void usage (void);
+int xdr_dir (XDR *, char *);
+
+int
+main(int argc, char *argv[])
+{
+ int all, errs, ch, mntsize, error;
+ char **typelist = NULL;
+ struct statfs *mntbuf, *sfs;
+ struct addrinfo hints;
+
+ /* Start disks transferring immediately. */
+ sync();
+
+ all = errs = 0;
+ while ((ch = getopt(argc, argv, "AaF:fh:t:v")) != -1)
+ switch (ch) {
+ case 'A':
+ all = 2;
+ break;
+ case 'a':
+ all = 1;
+ break;
+ case 'F':
+ setfstab(optarg);
+ break;
+ case 'f':
+ fflag = MNT_FORCE;
+ break;
+ case 'h': /* -h implies -A. */
+ all = 2;
+ nfshost = optarg;
+ break;
+ case 't':
+ if (typelist != NULL)
+ err(1, "only one -t option may be specified");
+ typelist = makevfslist(optarg);
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ argc -= optind;
+ argv += optind;
+
+ if ((argc == 0 && !all) || (argc != 0 && all))
+ usage();
+
+ /* -h implies "-t nfs" if no -t flag. */
+ if ((nfshost != NULL) && (typelist == NULL))
+ typelist = makevfslist("nfs");
+
+ if (nfshost != NULL) {
+ memset(&hints, 0, sizeof hints);
+ error = getaddrinfo(nfshost, NULL, &hints, &nfshost_ai);
+ if (error)
+ errx(1, "%s: %s", nfshost, gai_strerror(error));
+ }
+
+ switch (all) {
+ case 2:
+ if ((mntsize = mntinfo(&mntbuf)) <= 0)
+ break;
+ /*
+ * We unmount the nfs-mounts in the reverse order
+ * that they were mounted.
+ */
+ for (errs = 0, mntsize--; mntsize > 0; mntsize--) {
+ sfs = &mntbuf[mntsize];
+ if (checkvfsname(sfs->f_fstypename, typelist))
+ continue;
+ if (umountfs(sfs) != 0)
+ errs = 1;
+ }
+ free(mntbuf);
+ break;
+ case 1:
+ if (setfsent() == 0)
+ err(1, "%s", getfstab());
+ errs = umountall(typelist);
+ break;
+ case 0:
+ for (errs = 0; *argv != NULL; ++argv)
+ if (checkname(*argv, typelist) != 0)
+ errs = 1;
+ break;
+ }
+ exit(errs);
+}
+
+int
+umountall(char **typelist)
+{
+ struct xvfsconf vfc;
+ struct fstab *fs;
+ int rval;
+ char *cp;
+ static int firstcall = 1;
+
+ if ((fs = getfsent()) != NULL)
+ firstcall = 0;
+ else if (firstcall)
+ errx(1, "fstab reading failure");
+ else
+ return (0);
+ do {
+ /* Ignore the root. */
+ if (strcmp(fs->fs_file, "/") == 0)
+ continue;
+ /*
+ * !!!
+ * Historic practice: ignore unknown FSTAB_* fields.
+ */
+ if (strcmp(fs->fs_type, FSTAB_RW) &&
+ strcmp(fs->fs_type, FSTAB_RO) &&
+ strcmp(fs->fs_type, FSTAB_RQ))
+ continue;
+ /* Ignore unknown file system types. */
+ if (getvfsbyname(fs->fs_vfstype, &vfc) == -1)
+ continue;
+ if (checkvfsname(fs->fs_vfstype, typelist))
+ continue;
+
+ /*
+ * We want to unmount the file systems in the reverse order
+ * that they were mounted. So, we save off the file name
+ * in some allocated memory, and then call recursively.
+ */
+ if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL)
+ err(1, "malloc failed");
+ (void)strcpy(cp, fs->fs_file);
+ rval = umountall(typelist);
+ rval = checkname(cp, typelist) || rval;
+ free(cp);
+ return (rval);
+ } while ((fs = getfsent()) != NULL);
+ return (0);
+}
+
+/*
+ * Do magic checks on mountpoint/device/fsid, and then call unmount(2).
+ */
+int
+checkname(char *name, char **typelist)
+{
+ char buf[MAXPATHLEN];
+ struct statfs sfsbuf;
+ struct stat sb;
+ struct statfs *sfs;
+ char *delimp;
+ dev_t dev;
+ int len;
+
+ /*
+ * 1. Check if the name exists in the mounttable.
+ */
+ sfs = checkmntlist(name);
+ /*
+ * 2. Remove trailing slashes if there are any. After that
+ * we look up the name in the mounttable again.
+ */
+ if (sfs == NULL) {
+ len = strlen(name);
+ while (len > 1 && name[len - 1] == '/')
+ name[--len] = '\0';
+ sfs = checkmntlist(name);
+ }
+ /*
+ * 3. Check if the deprecated NFS syntax with an '@' has been used
+ * and translate it to the ':' syntax. Look up the name in the
+ * mount table again.
+ */
+ if (sfs == NULL && (delimp = strrchr(name, '@')) != NULL) {
+ snprintf(buf, sizeof(buf), "%s:%.*s", delimp + 1, delimp - name,
+ name);
+ len = strlen(buf);
+ while (len > 1 && buf[len - 1] == '/')
+ buf[--len] = '\0';
+ sfs = checkmntlist(buf);
+ }
+ /*
+ * 4. Resort to a statfs(2) call. This is the last check so that
+ * hung NFS filesystems for example can be unmounted without
+ * potentially blocking forever in statfs() as long as the
+ * filesystem is specified unambiguously. This covers all the
+ * hard cases such as symlinks and mismatches between the
+ * mount list and reality.
+ * We also do this if an ambiguous mount point was specified.
+ */
+ if (sfs == NULL || (getmntentry(NULL, name, NULL, FIND) != NULL &&
+ getmntentry(NULL, name, NULL, CHECKUNIQUE) == NULL)) {
+ if (statfs(name, &sfsbuf) != 0) {
+ warn("%s: statfs", name);
+ } else if (stat(name, &sb) != 0) {
+ warn("%s: stat", name);
+ } else if (S_ISDIR(sb.st_mode)) {
+ /* Check that `name' is the root directory. */
+ dev = sb.st_dev;
+ snprintf(buf, sizeof(buf), "%s/..", name);
+ if (stat(buf, &sb) != 0) {
+ warn("%s: stat", buf);
+ } else if (sb.st_dev == dev) {
+ warnx("%s: not a file system root directory",
+ name);
+ return (1);
+ } else
+ sfs = &sfsbuf;
+ }
+ }
+ if (sfs == NULL) {
+ warnx("%s: unknown file system", name);
+ return (1);
+ }
+ if (checkvfsname(sfs->f_fstypename, typelist))
+ return (1);
+ return (umountfs(sfs));
+}
+
+/*
+ * NFS stuff and unmount(2) call
+ */
+int
+umountfs(struct statfs *sfs)
+{
+ char fsidbuf[64];
+ enum clnt_stat clnt_stat;
+ struct timeval try;
+ struct addrinfo *ai, hints;
+ int do_rpc;
+ CLIENT *clp;
+ char *nfsdirname, *orignfsdirname;
+ char *hostp, *delimp;
+
+ ai = NULL;
+ do_rpc = 0;
+ hostp = NULL;
+ nfsdirname = delimp = orignfsdirname = NULL;
+ memset(&hints, 0, sizeof hints);
+
+ if (strcmp(sfs->f_fstypename, "nfs") == 0) {
+ if ((nfsdirname = strdup(sfs->f_mntfromname)) == NULL)
+ err(1, "strdup");
+ orignfsdirname = nfsdirname;
+ if ((delimp = strrchr(nfsdirname, ':')) != NULL) {
+ *delimp = '\0';
+ hostp = nfsdirname;
+ getaddrinfo(hostp, NULL, &hints, &ai);
+ if (ai == NULL) {
+ warnx("can't get net id for host");
+ }
+ nfsdirname = delimp + 1;
+ }
+
+ /*
+ * Check if we have to start the rpc-call later.
+ * If there are still identical nfs-names mounted,
+ * we skip the rpc-call. Obviously this has to
+ * happen before unmount(2), but it should happen
+ * after the previous namecheck.
+ * A non-NULL return means that this is the last
+ * mount from mntfromname that is still mounted.
+ */
+ if (getmntentry(sfs->f_mntfromname, NULL, NULL,
+ CHECKUNIQUE) != NULL)
+ do_rpc = 1;
+ }
+
+ if (!namematch(ai))
+ return (1);
+ /* First try to unmount using the file system ID. */
+ snprintf(fsidbuf, sizeof(fsidbuf), "FSID:%d:%d", sfs->f_fsid.val[0],
+ sfs->f_fsid.val[1]);
+ if (unmount(fsidbuf, fflag | MNT_BYFSID) != 0) {
+ /* XXX, non-root users get a zero fsid, so don't warn. */
+ if (errno != ENOENT || sfs->f_fsid.val[0] != 0 ||
+ sfs->f_fsid.val[1] != 0)
+ warn("unmount of %s failed", sfs->f_mntonname);
+ if (errno != ENOENT)
+ return (1);
+ /* Compatibility for old kernels. */
+ if (sfs->f_fsid.val[0] != 0 || sfs->f_fsid.val[1] != 0)
+ warnx("retrying using path instead of file system ID");
+ if (unmount(sfs->f_mntonname, fflag) != 0) {
+ warn("unmount of %s failed", sfs->f_mntonname);
+ return (1);
+ }
+ }
+ /* Mark this this file system as unmounted. */
+ getmntentry(NULL, NULL, &sfs->f_fsid, REMOVE);
+ if (vflag)
+ (void)printf("%s: unmount from %s\n", sfs->f_mntfromname,
+ sfs->f_mntonname);
+ /*
+ * Report to mountd-server which nfsname
+ * has been unmounted.
+ */
+ if (ai != NULL && !(fflag & MNT_FORCE) && do_rpc) {
+ clp = clnt_create(hostp, RPCPROG_MNT, RPCMNT_VER1, "udp");
+ if (clp == NULL) {
+ warnx("%s: %s", hostp,
+ clnt_spcreateerror("RPCPROG_MNT"));
+ return (1);
+ }
+ clp->cl_auth = authsys_create_default();
+ try.tv_sec = 20;
+ try.tv_usec = 0;
+ clnt_stat = clnt_call(clp, RPCMNT_UMOUNT, (xdrproc_t)xdr_dir,
+ nfsdirname, (xdrproc_t)xdr_void, (caddr_t)0, try);
+ if (clnt_stat != RPC_SUCCESS) {
+ warnx("%s: %s", hostp,
+ clnt_sperror(clp, "RPCMNT_UMOUNT"));
+ return (1);
+ }
+ /*
+ * Remove the unmounted entry from /var/db/mounttab.
+ */
+ if (read_mtab()) {
+ clean_mtab(hostp, nfsdirname, vflag);
+ if(!write_mtab(vflag))
+ warnx("cannot remove mounttab entry %s:%s",
+ hostp, nfsdirname);
+ free_mtab();
+ }
+ free(orignfsdirname);
+ auth_destroy(clp->cl_auth);
+ clnt_destroy(clp);
+ }
+ return (0);
+}
+
+struct statfs *
+getmntentry(const char *fromname, const char *onname, fsid_t *fsid, dowhat what)
+{
+ static struct statfs *mntbuf;
+ static size_t mntsize = 0;
+ static char *mntcheck = NULL;
+ struct statfs *sfs, *foundsfs;
+ int i, count;
+
+ if (mntsize <= 0) {
+ if ((mntsize = mntinfo(&mntbuf)) <= 0)
+ return (NULL);
+ }
+ if (mntcheck == NULL) {
+ if ((mntcheck = calloc(mntsize + 1, sizeof(int))) == NULL)
+ err(1, "calloc");
+ }
+ /*
+ * We want to get the file systems in the reverse order
+ * that they were mounted. Unmounted file systems are marked
+ * in a table called 'mntcheck'.
+ */
+ count = 0;
+ foundsfs = NULL;
+ for (i = mntsize - 1; i >= 0; i--) {
+ if (mntcheck[i])
+ continue;
+ sfs = &mntbuf[i];
+ if (fromname != NULL && strcmp(sfs->f_mntfromname,
+ fromname) != 0)
+ continue;
+ if (onname != NULL && strcmp(sfs->f_mntonname, onname) != 0)
+ continue;
+ if (fsid != NULL && bcmp(&sfs->f_fsid, fsid,
+ sizeof(*fsid)) != 0)
+ continue;
+
+ switch (what) {
+ case CHECKUNIQUE:
+ foundsfs = sfs;
+ count++;
+ continue;
+ case REMOVE:
+ mntcheck[i] = 1;
+ break;
+ default:
+ break;
+ }
+ return (sfs);
+ }
+
+ if (what == CHECKUNIQUE && count == 1)
+ return (foundsfs);
+ return (NULL);
+}
+
+int
+sacmp(struct sockaddr *sa1, struct sockaddr *sa2)
+{
+ void *p1, *p2;
+ int len;
+
+ if (sa1->sa_family != sa2->sa_family)
+ return (1);
+
+ switch (sa1->sa_family) {
+ case AF_INET:
+ p1 = &((struct sockaddr_in *)sa1)->sin_addr;
+ p2 = &((struct sockaddr_in *)sa2)->sin_addr;
+ len = 4;
+ break;
+ case AF_INET6:
+ p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr;
+ p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr;
+ len = 16;
+ if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
+ ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
+ return (1);
+ break;
+ default:
+ return (1);
+ }
+
+ return memcmp(p1, p2, len);
+}
+
+int
+namematch(struct addrinfo *ai)
+{
+ struct addrinfo *aip;
+
+ if (nfshost == NULL || nfshost_ai == NULL)
+ return (1);
+
+ while (ai != NULL) {
+ aip = nfshost_ai;
+ while (aip != NULL) {
+ if (sacmp(ai->ai_addr, aip->ai_addr) == 0)
+ return (1);
+ aip = aip->ai_next;
+ }
+ ai = ai->ai_next;
+ }
+
+ return (0);
+}
+
+struct statfs *
+checkmntlist(char *name)
+{
+ struct statfs *sfs;
+ fsid_t fsid;
+
+ sfs = NULL;
+ if (parsehexfsid(name, &fsid) == 0)
+ sfs = getmntentry(NULL, NULL, &fsid, FIND);
+ if (sfs == NULL)
+ sfs = getmntentry(NULL, name, NULL, FIND);
+ if (sfs == NULL)
+ sfs = getmntentry(name, NULL, NULL, FIND);
+ return (sfs);
+}
+
+size_t
+mntinfo(struct statfs **mntbuf)
+{
+ static struct statfs *origbuf;
+ size_t bufsize;
+ int mntsize;
+
+ mntsize = getfsstat(NULL, 0, MNT_NOWAIT);
+ if (mntsize <= 0)
+ return (0);
+ bufsize = (mntsize + 1) * sizeof(struct statfs);
+ if ((origbuf = malloc(bufsize)) == NULL)
+ err(1, "malloc");
+ mntsize = getfsstat(origbuf, (long)bufsize, MNT_NOWAIT);
+ *mntbuf = origbuf;
+ return (mntsize);
+}
+
+/*
+ * Convert a hexadecimal filesystem ID to an fsid_t.
+ * Returns 0 on success.
+ */
+int
+parsehexfsid(const char *hex, fsid_t *fsid)
+{
+ char hexbuf[3];
+ int i;
+
+ if (strlen(hex) != sizeof(*fsid) * 2)
+ return (-1);
+ hexbuf[2] = '\0';
+ for (i = 0; i < (int)sizeof(*fsid); i++) {
+ hexbuf[0] = hex[i * 2];
+ hexbuf[1] = hex[i * 2 + 1];
+ if (!isxdigit(hexbuf[0]) || !isxdigit(hexbuf[1]))
+ return (-1);
+ ((u_char *)fsid)[i] = strtol(hexbuf, NULL, 16);
+ }
+ return (0);
+}
+
+/*
+ * xdr routines for mount rpc's
+ */
+int
+xdr_dir(XDR *xdrsp, char *dirp)
+{
+
+ return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr, "%s\n%s\n",
+ "usage: umount [-fv] special | node | fsid",
+ " umount -a | -A [-F fstab] [-fv] [-h host] [-t type]");
+ exit(1);
+}
OpenPOWER on IntegriCloud